# Autograph

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-smixs-autograph-autograph`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [smixs](https://agentstack.voostack.com/s/smixs)
- **Installs:** 0
- **Category:** [Productivity](https://agentstack.voostack.com/c/productivity)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [smixs](https://github.com/smixs)
- **Source:** https://github.com/smixs/autograph/tree/main/skills/autograph

## Install

```sh
agentstack add skill-smixs-autograph-autograph
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# autograph — typed vault engine

One schema. One graph. Works on any vault.

## Overview

No hardcoded domains, types, or paths. The agent discovers structure from data, builds a schema, then enforces it. All scripts share `common.py`. Zero external dependencies (stdlib only, API calls via urllib).

## Quick Reference: 6 Workflows

| Workflow | When to use | Entry point |
|----------|-------------|-------------|
| **BOOTSTRAP** | New vault / after import / first setup | `discover.py` → `enforce.py` → `graph.py health` |
| **HEALTH** | Daily maintenance / on request | `graph.py health` → fix → moc → decay |
| **CREATE / UPDATE** | New knowledge card, or new info about an existing one | `search.py` dedup → ADD/UPDATE/SUPERSEDE → link → touch |
| **SEARCH & LINK** | Find info + strengthen connections | Hub → links → target; `graph.py orphans` → connect |
| **ORCHESTRATE** | Automated multi-agent workflows (no API keys) | `orchestrate.py health\|bootstrap` |
| **DAILY → CARDS** | Turn a day's raw notes into linked cards | `daily.py extract` → dedup-first process → link |

---

## Workflow 1: BOOTSTRAP (raw vault → structured graph)

**When to use:** New vault, bulk import, first setup. Run once, then switch to HEALTH.

**Full guide:** `references/bootstrap-workflow.md`

### Summary (10 phases)

1. **Discover:** `uv run scripts/discover.py  --verbose > /tmp/discovery.json`
2. **Generate schema:** Script baseline (`generate_schema.py`) + **agent swarm** (`swarm_prepare.py` → Wave 1 haiku → `swarm_reduce.py` → Wave 2 sonnet). **NEVER skip the swarm.**
3. **Review:** Human approves schema. Never auto-apply.
4. **Bootstrap + Enforce:** `engine.py init` + `enforce.py --apply`
5. **Link cleanup:** `link_cleanup.py --apply` (before enrichment)
6. **Tag enrich:** `enrich.py tags --apply` (via OpenRouter API)
7. **Deduplicate:** `dedup.py --apply` (before link enrichment)
8. **Link enrich:** `enrich.py swarm-links --apply` (**always swarm-links, never links**)
9. **MOC generation:** `moc.py generate`
10. **Verify:** `graph.py health` + `enforce.py` → target 90+/100

### Critical Rules

- **Always run Phase 2B (agent swarm).** Script alone cannot classify unstructured content.
- **Always use `swarm-links`**, not `links` (0.3% vs 81.6% match rate).
- **Always dry-run first** — run without `--apply` before applying.
- **Dedup before link enrich** — prevents links to merged/trashed files.

---

## Workflow 2: HEALTH (daily graph maintenance)

**When to use:** Daily upkeep, after edits, or when health score drops. This is the most common workflow.

### Decision Logic

```
1. Run `graph.py health ` → check score
2. If health  0  → `graph.py fix  --apply`
   b. orphans > 5       → connect orphans to hub files (see Workflow 4)
   c. desc_coverage ` → regenerate indexes
4. Run `engine.py decay ` → recalculate relevance + tiers
5. Run `graph.py health ` → confirm improvement
```

### Thresholds & Action Triggers

| Metric | Good | Action needed |
|--------|------|---------------|
| Health score | ≥90 | 0: `graph.py fix --apply` |
| Orphan files | 90d) | 30%: `engine.py creative` to resurface |

### Commands

```bash
uv run scripts/graph.py health            # health check
uv run scripts/graph.py fix  --apply       # fix broken links
uv run scripts/moc.py generate             # regenerate MOCs
uv run scripts/engine.py decay             # decay cycle (Ebbinghaus)
uv run scripts/engine.py decay  --dry-run  # preview decay changes
uv run scripts/engine.py stats             # tier distribution
uv run scripts/engine.py creative 5        # resurface forgotten cards
```

---

## Workflow 3: CREATE / UPDATE (dedup-first, then link)

**When to use:** Recording any card, or new information about something the vault may already track. Always look up first, always link immediately — a near-duplicate is the most common mistake; an orphan card is wasted knowledge.

### Step 0: LOOKUP (mandatory — never skip)

```bash
uv run scripts/search.py "" --vault  --json
# fallback: grep -ril "" 
```

Pick the operation (full rules: `references/update-in-place.md`):

- **ADD** — no existing card → create it (steps 1–5 below).
- **NOOP** — already captured, unchanged → stop.
- **UPDATE** — same subject, new enrichment → open the card, sharpen `description`, append a dated line under `## Log`, re-`touch`.
- **SUPERSEDE** — new fact *contradicts* a current value → rewrite the current value (frontmatter field + top of description = "Compiled Truth"), move the OLD value to append-only `## History` (`- 2026-03→2026-06 · company: TDI Group`), set `updated:`. Whole card obsolete → `status: superseded` + `superseded_by: [[new-card]]`.

Only when the operation is **ADD**, continue:

### Steps (ADD path)

1. **Type:** Pick from schema `node_types`
2. **Path:** Reverse-lookup `domain_inference` to find target folder:
   ```python
   # domain_inference maps path→domain. To find folder for domain "crm":
   for path_prefix, domain in schema['domain_inference'].items():
       if domain == 'crm':
           target_folder = path_prefix  # e.g. "work/crm/"
           break
   ```
3. **Frontmatter:** Write description (search snippet, not title repeat), tags (2-5, lowercase, kebab-case), status from type's enum
4. **LINKING PROTOCOL (mandatory):**
   a. Add `## Related` section with `[[hub]]` file of the domain
      - Hub = `_index.md` or `MEMORY.md` of that domain
   b. Find 2-3 sibling cards of same type+domain → add `[[links]]`
      - `uv run scripts/graph.py backlinks  ` → find siblings
      - Or: read vault-graph.json → filter nodes by type+domain
   c. Run `uv run scripts/engine.py touch `
5. **Verify checklist:**
   - [ ] Hub linked?
   - [ ] 2+ related cards found?
   - [ ] description ≠ title repeat?
   - [ ] tags: 2-5, lowercase, kebab-case?
   - [ ] status ∈ schema enum?

Templates: `references/card-templates.md`

---

## Workflow 4: SEARCH & LINK (find + strengthen connections)

**When to use:** Looking up information in the vault, or strengthening weak areas of the graph.

### Navigation (Hub → Links → Target)

1. **Determine domain** from the topic (work, personal, research, etc. — whatever your schema defines)
2. **Start at hub:** `_index.md` or `MEMORY.md` of that domain
3. **Follow links** — max 2 hops from hub to target
4. **Fallback:** `uv run scripts/graph.py backlinks  ` for reverse links

### Orphan Rescue

```bash
uv run scripts/graph.py orphans         # find orphans
# For each orphan: connect to nearest hub or sibling card
```

### Link Strengthening

```bash
# Files with  --apply
uv run scripts/graph.py health           # verify improvement
```

---

## Workflow 5: ORCHESTRATE (automated multi-agent workflows)

**When to use:** Instead of running scripts manually. No API keys — the Claude Code agent does all judgment directly.

### Phase 0: Script sequencing

```bash
python3 scripts/orchestrate.py health       # automated health workflow
python3 scripts/orchestrate.py bootstrap     # full bootstrap (one command)
```

`health` runs: graph check > fix broken links > link cleanup > MOC > decay > verify.
`bootstrap` runs: enforce > cleanup > tags > dedup > swarm-links > MOC > verify.

### Phases 1-3: Agent judgment (no API keys)

The agent (you) does the judgment directly — read prepared data, decide, write results.

```bash
# Phase 1: prep dedup clusters for YOUR review
python3 scripts/orchestrate.py dedup-prepare 
# -> writes .graph/dedup-review-input.json
# -> YOU read clusters, mark approved=true, then: dedup.py --apply-manifest

# Phase 2: prep domain catalogs for YOUR link suggestions
python3 scripts/orchestrate.py link-prepare 
# -> writes .graph/link-review-input.json
# -> YOU read catalogs, suggest links per domain, write batch results

# Phase 3: prep graph data for YOUR semantic analysis
python3 scripts/orchestrate.py graph-prepare 
# -> writes .graph/graph-analysis-input.json
# -> YOU analyze contradictions, missing links, stale hubs, write findings
```

For Phases 1-3: run the prep command, read the output JSON, do the analysis yourself (you ARE the LLM), write results back. Use Agent tool for parallel domain work in Phase 2.

---

## Workflow 6: DAILY → CARDS (day's notes → linked cards)

**When to use:** Turning a `daily/YYYY-MM-DD.md` note file into durable cards. Judgment-first — the scripts extract candidates; you classify, dedup, and link.

**Full guide:** `references/daily-processor.md`

### Summary (4 phases + idempotency)

1. **CAPTURE:** `daily.py extract   [date]` (candidates → `.graph/`) + `supersede.py ` (conflict scan). Read schema `node_types`, list noteworthy items + the day's topics.
2. **PROCESS:** per item, run the Workflow 3 Step 0 decision (ADD / UPDATE / SUPERSEDE / NOOP — `references/update-in-place.md`); resolve every `.graph/supersede-candidates.json` entry.
3. **LINK:** apply the Workflow 3 linking protocol (hub + 2 siblings + touch) to each card.
4. **SUMMARIZE (schema-gated):** only if the schema defines a summary type, write a daily-summary card with topics + a MOC down to today's cards and the raw file. No hardcoded DAG.

**Idempotency:** append `` to the end of the daily file; on re-run, skip content above the last marker. Never edit existing lines.

---

## Decay Engine (Ebbinghaus)

The decay system models memory with three key mechanisms:

### 1. Access count (spacing effect)

Each `touch` increments `access_count` in frontmatter. More retrievals = slower forgetting:

```
strength = 1 + ln(access_count)
effective_rate = base_rate / strength
relevance = max(floor, 1.0 - effective_rate * days_since_access)
```

Example: a card touched 5 times has `strength = 1 + ln(5) ≈ 2.6`, decaying ~2.6x slower than a card touched once.

### 2. Domain-specific rates

Different content types decay at different rates. Configure in schema `decay.domain_rates`:

| Type | Rate | Half-life (~) | Rationale |
|------|------|---------------|-----------|
| contact | 0.005 | 100 days | People don't become irrelevant quickly |
| crm | 0.008 | 62 days | Deals have medium lifecycle |
| learning | 0.010 | 50 days | Knowledge fades moderately |
| project | 0.012 | 42 days | Projects have defined timelines |
| daily | 0.020 | 25 days | Daily notes lose relevance fast |
| (default) | 0.015 | 33 days | Fallback for unlisted types |

### 3. Graduated recall

Touch promotes one tier at a time, not a direct jump to active:

```
archive → cold → warm → active
```

Each promotion sets `last_accessed` to a midpoint date, so without re-touch the card naturally drifts back.

### Backward compatibility

- Files without `access_count` → default=1 → `1+ln(1)=1.0` → rate unchanged
- Files without `type` → default rate applies
- Existing calls `calc_relevance(days, schema)` → work unchanged (new params optional)

---

## Maintenance Commands

```bash
uv run scripts/moc.py generate                                        # MOC generation
uv run scripts/engine.py decay                                        # decay cycle
uv run scripts/engine.py touch /path/card.md                          # touch (graduated)
uv run scripts/engine.py creative 5                                   # creative recall
uv run scripts/engine.py stats                                        # stats
uv run scripts/graph.py backlinks  path/to/card                       # backlinks
uv run scripts/graph.py orphans                                       # orphans
uv run scripts/graph.py fix  --apply                                  # fix links
uv run scripts/search.py "" --vault  --json                    # ranked memory search (dedup-first)
uv run scripts/supersede.py                                           # conflict scan (dry-run)
uv run scripts/supersede.py  --apply                                  # stamp superseded (2-card, newer-by-date)
uv run scripts/daily.py extract                           # entity extraction
uv run scripts/engine.py init  --dry-run                              # bootstrap bare files
OPENROUTER_API_KEY=sk-... uv run scripts/enrich.py swarm-links  --apply  # link enrichment
OPENROUTER_API_KEY=sk-... uv run scripts/enrich.py tags  --apply         # tag enrichment
uv run scripts/link_cleanup.py  --apply                               # link cleanup
```

## Scripts

| Script | Purpose |
|--------|---------|
| common.py | Shared: parse FM, walk, domain, decay (Ebbinghaus), wikilinks |
| discover.py | Workflow 1: scan vault, output enum candidates |
| generate_schema.py | Workflow 1: turn discovery JSON into draft schema |
| swarm_prepare.py | Workflow 1: bin-pack vault into agent batches |
| swarm_reduce.py | Workflow 1: consolidate + validate schema |
| enforce.py | Workflow 1: validate + autofix against schema |
| link_cleanup.py | Workflow 1/4: remove phantom wikilinks from ## Related |
| enrich.py | Workflow 1/4: tags + swarm-links (catalog-oriented link enrichment) |
| dedup.py | Workflow 1: safe merge + .trash/ |
| graph.py | Workflow 2/4: health score, link repair, backlinks, orphans |
| moc.py | Workflow 2: MOC generation per domain |
| orchestrate.py | Workflow 5: multi-agent orchestration (health, bootstrap, dedup-review, link-enrich, graph-analyze) |
| engine.py | Workflow 2/3: decay (Ebbinghaus), touch (graduated), creative, stats, init |
| search.py | Workflow 3/4: ranked memory search (BM25 FTS5 + link-graph rerank) — dedup-first lookup |
| supersede.py | Workflow 3: deterministic same-entity conflict scan → `.graph/supersede-candidates.json` |
| daily.py | Entity extraction from memory files |
| tests/test_autograph.py | Self-contained tests (temp fixtures) |

## Files

| File | In package? | Purpose |
|------|------------|---------|
| schema.example.json | Yes | Template — copy and customize (includes domain_rates) |
| schema.json | No | Your vault's schema (generated) |
| schema.local.json | No | Local override (gitignored) |
| references/ | Yes | Bootstrap workflow, schema docs, card templates, linking protocol |

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| **Skipping agent swarm in Phase 2** | **CRITICAL: always run Step 2B. Script alone cannot classify unstructured content. No exceptions.** |
| **Using deprecated `links` subcommand** | **`links` was removed (0.3% match rate). Only `swarm-links` is available — 81.6% match rate.** |
| **Creating cards without linking** | **Always follow Workflow 3 — link to hub + 2 siblings immediately. Orphan cards are wasted knowledge.** |
| **Creating a near-duplicate instead of updating** | **Workflow 3 Step 0 — `search.py`/grep first. Same subject → UPDATE or SUPERSEDE the existing card, never a second one.** |
| **Two contradictory current values on one subject** | **SUPERSEDE: rewrite the current value (Compiled Truth), move the old one to append-only `## History`. Never leave both standing.** |
| **Touching archive cards to active directly** | **Use graduated recall — touch promotes one tier at a time (archive→cold→warm→active).** |
| Sending full vault to one agent | Use `swarm_prepare.py` — bin-packs into ~50K token batches. |
| Running Wave 2 without Wave 1 | `swarm_reduce.py prepare` needs JSONL in `.graph/swarm/classifications/`. |
| Using schema.example.json directly | Run discover → generate your own schema.json |
| Description = title repeat | Write specific search snippet |
| Status not in enum | Check schema's node_types |
| Skip dry run | Always run without --apply first |
| Running link enrich before dedup | Creates links to files that get merged/trashed. Dedup first. |
| Missing OPENROUTER_API_KEY | `enrich.py` reads from `OPENROUTER_API_KEY` env var. |
| Only running swarm-links once | Run again with `--force` to enrich ALL files. |

## Default Models

| Command | Default model | Override |
|---------|--------------|----------|
| tags | google/gemini-3-flash-preview | `--model` flag |
| swarm-links | google/gemini-2.0-flash-001 | `--model` flag |

Both are production-tested. Do not change defaults without benchmarking.

## Troubleshooting

**Error: Schema not

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [smixs](https://github.com/smixs)
- **Source:** [smixs/autograph](https://github.com/smixs/autograph)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-smixs-autograph-autograph
- Seller: https://agentstack.voostack.com/s/smixs
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
