Installs just this skill. Get the whole plugin for auto-invocation.
โก How it fires
How this skill gets triggered: by you, by Claude, or both.
Fires itselfClaude auto-loads it when your prompt matches the work.
You can call itInvoke it directly when you want it.
Slash command/compress
๐๏ธ Context preview
The summary Claude sees to decide when to auto-load this skill.
Vault alignment engine. Detects and fixes 5 types of structural misalignments: broken backlinks, concept fragmentation, entity miscategorization, duplicated entities, and misnamed entities. Delegates all writes to /bedrock:preserve. Supports interactive mode (user confirmation)
๐ Stats
Stars83
Forks8
LanguageHTML
LicenseMIT
๐ฆ Ships with bedrock
</> SKILL.md
compress.SKILL.md
---name: compress
description: >
Vault alignment engine. Detects and fixes 5 types of structural misalignments:
broken backlinks, concept fragmentation, entity miscategorization, duplicated entities,
and misnamed entities. Delegates all writes to /bedrock:preserve.
Supports interactive mode (user confirmation) and cron mode (autonomous mechanical fixes +
queued semantic proposals). Use when: "bedrock compress", "bedrock-compress",
"align vault", "fix backlinks", "fix misalignments", "/bedrock:compress".
user_invocable: true
allowed-tools: Bash, Read, Glob, Grep, Skill, Agent
---# /bedrock:compress โ Vault Alignment Engine
## Plugin Paths
Entity definitions and templates are in the plugin directory, not the vault root.
Use the "Base directory for this skill" provided at invocation to resolve paths:
- Entity definitions: `<base_dir>/../../entities/`
- Templates: `<base_dir>/../../templates/{type}/_template.md`
- Plugin CLAUDE.md: `<base_dir>/../../CLAUDE.md` (already injected automatically into context)
Where `<base_dir>` is the path provided in "Base directory for this skill".
---## Vault Resolution
Resolve which vault to compress. This skill can be invoked from any directory.
- **NEVER** delete entities (compress aligns, it does not delete)
- People/Teams/Concepts/Topics: **append-only** โ never delete content
- Actors: **free merge** โ may edit body freely
---
## Phase 0 โ Sync the Vault
Execute:
```bash
git -C <VAULT_PATH> pull --rebase origin main
```
If it fails:
- No remote: warn "No remote configured. Working locally." and proceed.
- Conflict: `git -C <VAULT_PATH> rebase --abort` and warn the user. Do NOT proceed without resolving.
---
## Phase 1 โ Scan and Detect
Scan the entire vault and run all 5 detection algorithms. Store results for Phase 2.
### 1.0 Load entity definitions and config
Read the entity definitions from the plugin directory to understand classification criteria:
- `<base_dir>/../../entities/concept.md` โ needed for capability 2 (concept match)
- `<base_dir>/../../entities/*.md` โ needed for capability 3 (entity misalignment)
- `<base_dir>/../../entities/code.md` โ needed for capability 1 graph-side detection (ยง1.2.2)
Store the "When to create", "When NOT to create", and "How to distinguish" sections
from each entity definition for use in detection.
Read vault config for graph-side detection:
```bash
cat <VAULT_PATH>/.bedrock/config.json 2>/dev/null
```
Extract `code.cluster_threshold` (default `0.85`, used by ยง1.2.2 for Scenario A-extended similarity matching and Scenario B clustering) and `code.max_per_actor` (default `200`, used in Phase 5 to surface cap overrun warnings โ never enforced as a hard limit). If `.bedrock/config.json` is missing or has no `code` block, use the defaults silently.
### 1.1 Read all entities
For each entity directory (`<VAULT_PATH>/actors/`, `<VAULT_PATH>/people/`, `<VAULT_PATH>/teams/`, `<VAULT_PATH>/concepts/`, `<VAULT_PATH>/topics/`, `<VAULT_PATH>/discussions/`, `<VAULT_PATH>/projects/`, `<VAULT_PATH>/fleeting/`):
1. List all `.md` files, **excluding `_template.md` and `_template_node.md`**
- For actors: include both `<VAULT_PATH>/actors/*.md` (flat) and `<VAULT_PATH>/actors/*/*.md` (folder)
2. For each entity, read frontmatter + body
3. Extract:
- `type` from frontmatter
- `name` from frontmatter (or filename as fallback)
- `aliases` from frontmatter (array)
- All wikilinks `[[target]]` from body AND frontmatter arrays
- All proper nouns, service names, team names, person names mentioned in the body (for capabilities 4 and 5)
**Optimization for large vaults:** If the vault has more than 100 entities in a type,
use subagents via Agent tool to parallelize reading by entity type.
Cap 1 detects backlinks that are broken on either of two layers: markdown wikilinks (ยง1.2.1, today's behavior) and graph-side back-pointers (ยง1.2.2, total-binding rule).
**Rule:** every node in `<VAULT_PATH>/graphify-out/graph.json` MUST end up with `vault_entity_path` set. There is NO relevance filter at `/compress` time โ `confidence` level (EXTRACTED / INFERRED / AMBIGUOUS), trivial labels (Test / Mock / Builder / Stub / Fixture), `degree`, `is_god_node`, `edge_count` do NOT exclude any node from binding.
**Best-effort load.** If `<VAULT_PATH>/graphify-out/graph.json` is missing, empty, or invalid JSON, skip ยง1.2.2 silently and proceed with ยง1.2.1 results only. The `/compress` run continues normally.
If OK, also load `<VAULT_PATH>/graphify-out/.graphify_analysis.json` (best-effort โ when missing or stale, fall back to `semantically_similar_to`-only grouping; same fallback used by `/preserve` Phase 1.3 step 5).
**Build the entity-id index.** For each `code` entity in `vault_data` (i.e., entities under `<VAULT_PATH>/actors/*/nodes/`), normalize ids to a set:
```python
ids = set()
fm = entity.frontmatter
if isinstance(fm.get("graphify_node_ids"), list):
ids.update(fm["graphify_node_ids"])
if isinstance(fm.get("graphify_node_id"), str): # legacy singular โ backward compat
ids.add(fm["graphify_node_id"])
```
Build a map `entity_id_index: id โ entity_path` covering every `code` entity. Build `bound_node_ids = set(entity_id_index.keys())`.
**Walk every node in graph.json.** For each node N with `id` and OPTIONAL `vault_entity_path`:
- If `vault_entity_path` is already set on N โ already bound, skip.
- Otherwise classify in priority order โ stop at first matching shape:
1. **Scenario A โ `back_pointer_missing`.** If `N.id โ bound_node_ids`: register `{kind: "graph_back_pointer", shape: "back_pointer_missing", node_id: N.id, entity_path: entity_id_index[N.id]}`.
2. **Scenario A-extended โ `extend_existing`.** Find any node M such that:
- `M.id โ bound_node_ids` (M is bound to some entity E),
- There is an edge `(N, M)` or `(M, N)` of `relation: "semantically_similar_to"` with `confidence_score โฅ code.cluster_threshold`,
- OR (when `.graphify_analysis.json` is present) `community_id(N) == community_id(M)` AND (`is_god_node(M)` OR (`edge_count(N) โฅ 2` AND `edge_count(M) โฅ 2`)).
If found, pick the M with the highest `confidence_score` (or highest degree if community-based) and register `{kind: "graph_back_pointer", shape: "extend_existing", node_id: N.id, target_entity_path: entity_id_index[M.id], target_node_id: M.id, similarity: <score>}`.
3. **Scenario B โ `orphan_graph_node`.** Otherwise, mark N as a Scenario B candidate. Do NOT register yet โ Scenario B candidates are clustered together first (next step).
**Cluster Scenario B candidates** using the Part 2 grouping algorithm (mirrors `/preserve` Phase 1.3 step 5):
- Two Scenario B nodes are in the same cluster if there is a `semantically_similar_to` edge between them with `confidence_score โฅ code.cluster_threshold`, OR they share `community_id` AND (at least one is `is_god_node` OR both have `edge_count โฅ 2`).
- If `.graphify_analysis.json` is absent or stale, only the `semantically_similar_to` rule applies.
- Singletons form their own cluster of size 1.
- Each cluster carries: `cluster_id` (synthetic), `member_node_ids[]` (the union of node ids โ this becomes the `graphify_node_ids` array of the resulting `code` entity), `representative` (highest-degree node in the cluster โ used for `label`, `source_file`, `node_type`).
**Resolve `actor_context` for each Scenario B cluster** via (b) + (c) from the spec:
- (b) Mechanical inference: extract the cluster representative's `source_file` and take the first path segment (e.g., `billing-api/src/Foo.cs` โ candidate slug `billing-api`). Match (case-insensitive, kebab-case) against existing actor slugs in `<VAULT_PATH>/actors/`.
- Single match โ set `actor_context = <slug>`.
- No match โ mark cluster as `actor_context: null` โ it will be classified as `concept` global / `topic` / `fleeting` per the corpus-agnostic branch from `/preserve` Phase 1.3 step 6.
- Ambiguous match (2+ candidates) โ mark cluster as `actor_context: <list-of-candidates>` for (c) resolution in Phase 3.
- (c) Phase 3 fallback (resolved later in this run): in `--mode interactive` the user picks; in `--mode cron` the cluster is queued in the `compress-proposals` fleeting note.
For each Scenario B cluster, register `{kind: "graph_back_pointer", shape: "orphan_graph_node", member_node_ids: [...], representative: <node>, actor_context: <slug | null | candidates[]>, node_type: <inferred>}`.
**Inferring `node_type` for Scenario B clusters** (mirrors `/preserve` Phase 1.3 step 6):
| Actor | Existing code entities | Proposed (Scenario B) | Total | Cap |
|---|---|---|---|---|
| `billing-api` | 198 | 12 | 210 | 200 |
**Fix:** /compress will proceed with all proposals โ cap is a warning, not a block. Maintainer can raise `code.max_per_actor` in `.bedrock/config.json` afterwards if needed.
```
If no broken backlinks found across both layers: "No broken backlinks found."
If ยง1.2.2 was skipped (no graph.json or invalid): "Graph-side detection skipped โ `graph.json` is missing or invalid in `<VAULT_PATH>/graphify-out/`."
- Proceed directly to Phase 4 with these findings.
- When invoking `/bedrock:preserve`, include in the prompt: "Autonomous mode โ do not ask for confirmation, process directly."
**Queued proposals (semantic):**
- Capabilities 2, 3, 5.
- Capability 1 graph back-pointers โ `orphan_graph_node` (ยง1.2.2 Scenario B), including clusters whose `actor_context` could not be resolved mechanically (no match) or that have ambiguous candidates.
- If there are findings of any of the above, compile them into a single fleeting note
and delegate creation to `/bedrock:preserve`:
```yaml
entities:
- type: fleeting
name: "<today's date YYYY-MM-DD>-compress-proposals"
The following alignment issues were detected by `/bedrock:compress` running in cron mode.
Review each proposal and run `/bedrock:compress` in interactive mode to execute.
### Concept Fragmentation (Capability 2)
<formatted findings from Phase 2.3>
### Entity Misalignment (Capability 3)
<formatted findings from Phase 2.4>
### Misnamed Entities (Capability 5)
<formatted findings from Phase 2.6>
### Orphan Graph Nodes (Capability 1 โ Scenario B)
<formatted findings from Phase 2.2 โ Scenario B clusters; include the actor_context status (resolved / null / ambiguous candidates) and the cap overrun warnings if any>
relations: {}
source: "compress"
metadata:
status: "raw"
source: "session"
captured_at: "<today's date YYYY-MM-DD>"
```
- Include in the `/bedrock:preserve` invocation:
"Autonomous mode โ do not ask for confirmation, process directly."
---
## Phase 4 โ Delegate to /bedrock:preserve
### 4.1 Compile structured entity list
Build the entity list in the format accepted by `/bedrock:preserve`, grouping all confirmed fixes:
#### Capability 1 fixes (broken backlinks)
Cap 1 has up to four fix templates depending on the finding's `kind` and `shape`.
The empty body change ensures `/preserve` includes the entity in its touched-set; `/preserve` Phase 6.5 will then re-propagate `vault_entity_path` to every node listed in the entity's `graphify_node_ids` (including the missing one). No new graph mutation is performed by `/compress`.
For each finding `{node_id, target_entity_path, target_node_id, similarity}`:
1. Read the target entity's current `graphify_node_ids` from `vault_data` (already loaded in Phase 1.1; accept the legacy singular `graphify_node_id` and normalize to a single-item list).
2. Compute the union: `new_ids = existing_ids โช {node_id}` (deduped).
3. Pass the full array to `/preserve`:
```yaml
- type: code
name: "<target_entity_path's filename without .md>"
action: update
content: ""
relations: {}
source: "compress"
metadata:
graphify_node_ids: <new_ids> # full union, computed by /compress
```
`/preserve` writes the full array to the entity's frontmatter (Phase 4.2 update logic) and Phase 6.5 propagates `vault_entity_path` to every id in the array โ including the newly-added one. No new field is introduced to the `/preserve` protocol.
content: "<brief description from representative.label and source_file; note 'Grouped from N graphify nodes' when cluster size > 1, listing member ids>"
relations: {}
source: "compress"
metadata:
graphify_node_ids: <cluster.member_node_ids>
actor: "[[<actor_context>]]"
node_type: "<inferred node_type>"
source_file: "<representative.source_file>"
confidence: "<strongest edge confidence across cluster members>"
```
If `actor_context` is `null` (no match โ corpus-agnostic branch):
```yaml
# Classify the cluster as concept / topic / fleeting per /preserve Phase 1.3 step 6 (actor_context absent branch).
# Use the same heuristics: pattern/principle/abstraction โ concept; complete + bridge content โ topic; else fleeting.
- type: <concept | topic | fleeting>
name: "<kebab-case slug>"
action: create
content: "<derived content>"
relations: {}
source: "compress"
metadata:
graphify_node_ids: <cluster.member_node_ids>
confidence: "<strongest edge confidence>"
```
If `actor_context` is ambiguous (multiple candidates):
- In `--mode interactive`, the user selected one candidate during Phase 3 โ proceed with the resolved single-match template above.
- In `--mode cron`, the cluster is queued in the `compress-proposals` fleeting note and is NOT delegated to `/preserve` for entity creation in this run.
#### Capability 2 fixes (concept creation)
For each confirmed concept candidate:
```yaml
- type: concept
name: "<concept-slug>"
action: create
content: "<brief definition derived from the recurring mentions>"
Plus, for each referencing entity that should link to the new concept:
```yaml
- type: <entity's type>
name: "<entity's name>"
action: update
content: ""
relations:
concepts: ["<concept-slug>"]
source: "compress"
```
#### Capability 3 fixes (entity misalignment)
For each misaligned entity:
**If current type is `fleeting` (promotion):**
```yaml
- type: <proposed new type>
name: "<new entity name in correct format>"
action: create
content: "<content migrated from the fleeting note>"
relations:
<inferred relations>: [...]
source: "compress"
- type: fleeting
name: "<original fleeting note name>"
action: update
content: ""
relations: {}
source: "compress"
metadata:
status: "promoted"
promoted_to: "[[<new entity name>]]"
```
**If current type is NOT fleeting (recategorization):**
```yaml
- type: <proposed new type>
name: "<new entity name in correct format>"
action: create
content: "<content from the misaligned entity>"
relations:
<inferred relations>: [...]
source: "compress"
```
Add a consolidation callout in the original entity (via update):
```yaml
- type: <current type>
name: "<original entity name>"
action: update
content: "> [!info] Content recategorized to [[<new entity name>]]\n> This entity was recategorized by /bedrock:compress. See [[<new entity name>]] for the current version."
For each file where the variant was found (to add the wikilink):
```yaml
- type: <entity's type>
name: "<entity where variant was found>"
action: update
content: ""
relations:
<canonical entity's type plural>: ["<canonical entity name>"]
source: "compress"
```
**For entity merges** (two files for the same real-world entity):
```yaml
- type: <canonical entity's type>
name: "<canonical entity name>"
action: update
content: "<merged content from both entities>"
relations:
<merged relations from both>: [...]
source: "compress"
metadata:
aliases: ["<combined aliases from both entities>"]
```
Add a consolidation callout in the secondary entity:
```yaml
- type: <secondary entity's type>
name: "<secondary entity name>"
action: update
content: "> [!info] Content consolidated in [[<canonical entity name>]]\n> This entity has been consolidated by /bedrock:compress. See [[<canonical entity name>]] for the merged version."
relations:
<canonical entity's type plural>: ["<canonical entity name>"]
source: "compress"
```
### 4.2 Invoke /bedrock:preserve
Use the Skill tool to invoke `/bedrock:preserve --vault <VAULT_NAME>` passing the compiled structured entity list as argument.
The `--vault <VAULT_NAME>` flag ensures preserve writes to the same vault.
Include `source: "compress"` for all entities so `/bedrock:preserve` records provenance.
If running in cron mode (autonomous capabilities):
- Add to the invocation prompt: "Autonomous mode โ do not ask for confirmation, process directly."
### 4.3 Await result
`/bedrock:preserve` returns:
- List of created/updated entities
- Commit hash (if there was a commit)
- Any errors or warnings
Record the result for use in the final report (Phase 5).
---
## Phase 5 โ Final Report
Present to the user:
```markdown
## /bedrock:compress โ Report
### Mode: interactive / cron
### Alignment fixes applied
| # | Capability | Findings | Fixed | Queued |
|---|---|---|---|---|
| 1A | Broken backlinks (markdown) | N | M | โ |
| 1B-A | Graph back-pointer missing | N | M | โ |
| 1B-B | Extend existing entity | N | M | โ |
| 1B-C | Orphan graph node | N | M | P (cron) |
| 2 | Concept match | N | M | P (cron) |
| 3 | Entity misalignment | N | M | P (cron) |
| 4 | Duplicated entities | N | M | โ |
| 5 | Misnamed entities | N | M | P (cron) |
**Total:** N findings, M fixed, P queued
### Cap overrun warnings (when applicable)
| Actor | Existing code entities | Proposed (Scenario B) | Total | Cap | Status |
If ยง1.2.2 was skipped (no `graph.json`), include this line: "Graph-side detection skipped โ `<VAULT_PATH>/graphify-out/graph.json` is missing or invalid."
### Entities processed (via /bedrock:preserve)
| Type | Name | Action |
|---|---|---|
| <type> | <name> | create / update |
| ... | ... | ... |
### Queued proposals (cron mode only)
- Created fleeting note: [[<YYYY-MM-DD>-compress-proposals]]
- Contains N proposals for capabilities 2, 3, 5, and Cap 1 Scenario B (orphan graph nodes)
- Review and run `/bedrock:compress` in interactive mode to execute
### Git
- Commit: <hash from /bedrock:preserve>
- Push: success / failed (reason)
### Suggestions
- Run `/bedrock:healthcheck` for a full vault health report
- [additional suggestions based on findings]
```
If no fixes were applied (user refused all, or no findings):
Present only the summary table with zero counts.
---
## Error Handling
| Situation | Action |
|---|---|
| Empty vault (no entities) | Report "No entities found in the vault." and end |
| No findings across all 5 capabilities | Report "Vault is aligned." and end |
| User refuses all findings (interactive) | Report "No changes made." and end |
| Error reading entity | Skip entity, warn in the report |
| `/bedrock:preserve` fails | Report the error, list what was NOT processed |
| Entity without frontmatter | Skip entity, warn in the report |
| `--mode` argument not recognized | Default to `interactive`, warn the user |
---
## Critical Rules
| Rule | Detail |
|---|---|
| All writes via /bedrock:preserve | NEVER use Write or Edit on entity files. Invoke `/bedrock:preserve` via the Skill tool. |
| Mechanical vs. semantic split | Capabilities 1, 4 = mechanical (autonomous in cron). Capabilities 2, 3, 5 = semantic (queued in cron, confirmed in interactive). |
| User confirmation in interactive | ALWAYS wait for explicit confirmation before delegating to /bedrock:preserve in interactive mode. |
| Append-only for people/teams/topics | When fixing entities of these types, NEVER delete existing content. Add callouts and new links only. |
| Actors allow free merge | Actor bodies can be modified freely. Frontmatter is merge-only (never delete fields). |
| Never remove wikilinks | When fixing backlinks or renaming, ADD new links. Never remove existing ones. |
| Entity definitions are authoritative | Capabilities 2 and 3 MUST read entity definitions from the plugin directory. Do not hardcode classification heuristics. |
| 3+ threshold | Capabilities 2 and 4 require a term/name to appear in 3+ different entities before flagging. |
| Provenance | All entities delegated to /bedrock:preserve use `source: "compress"`. |
| MCP in main context | Do NOT use subagents for MCP calls โ permissions are not inherited. |
| Sensitive data | NEVER include credentials, tokens, passwords, PANs, CVVs. |
| Vault resolution first | Resolve `VAULT_PATH` before any file operation or git command โ never assume CWD is the vault |
| All git commands use `git -C <VAULT_PATH>` | Never assume CWD is the vault |
| All entity paths use `<VAULT_PATH>/` prefix | `<VAULT_PATH>/actors/`, not `actors/` |
| Pass --vault to /preserve | ALWAYS include `--vault <VAULT_NAME>` when delegating to `/bedrock:preserve` |
| Best-effort graph load | ยง1.2.2 graph-side detection skips silently when `<VAULT_PATH>/graphify-out/graph.json` is missing, empty, or invalid JSON. Markdown-side detection (ยง1.2.1) continues unaffected. |
| No `graph.json` mutation from /compress | NEVER write to `<VAULT_PATH>/graphify-out/graph.json` directly. All `vault_entity_path` propagation flows through `/preserve` Phase 6.5 triggered by the `update`/`create` actions in Phase 4. Honors `/preserve` Critical Rule 28. |
| Total-binding scope, no relevance filter at /compress time | ยง1.2.2 iterates EVERY node in `graph.json` lacking `vault_entity_path`. Confidence level (EXTRACTED / INFERRED / AMBIGUOUS), trivial labels (Test / Mock / Builder / Stub / Fixture), degree, god-node status, and edge_count do NOT exclude any node. The cluster threshold (`code.cluster_threshold`) is the only knob that influences absorption-vs-materialization. |
| Cap overrun is allowed with warning | When Scenario B fixes push an actor above `code.max_per_actor`, the proposals proceed and the report surfaces a per-actor warning row. The cap is a `/teach`-time noise control, not a `/compress`-time hard limit. |
| Reuse Part 2 algorithm verbatim | ยง1.2.2 grouping for Scenario B and similarity matching for Scenario A-extended use the same logic and config keys (`code.cluster_threshold`, community co-membership with god-node/edge_count guard) as `/preserve` Phase 1.3 step 5. Do not fork or re-implement. |