Agent-readable docs

loopany Documentation

Reference for the loopany CLI and agent skill library: markdown artifacts, append-only reference graph, workspace schema, and harness-neutral install paths for long-horizon agent memory.

Pages

  1. OverviewWhat loopany exposes (CLI, workspace, skills), runtime assumptions (Bun, $LOOPANY_HOME), and the shortest path from init to a running mission-backed brain.
  2. InstallationClone the repo, install Bun, link the CLI, set PATH, initialize $LOOPANY_HOME, and wire the resolver injection into Claude Code, Codex, or other agent hosts.
  3. QuickstartFirst successful session: init workspace, create a mission and task, add a reference edge, run doctor, and optional reindex/search or factory UI.
  4. Artifacts and workspaceOn-disk layout under $LOOPANY_HOME: artifacts/<dirName>/<slug>.md, config.yaml, references.jsonl, audit.jsonl, optional search.db, and v0.2 slug-as-id storage rules.
  5. Kinds and validationMarkdown kind definitions, dynamic Zod frontmatter schemas, status machines, indexedFields, slug rules, and immutable write semantics (append, status flip, supersede).
  6. Reference graphAppend-only references.jsonl edges, implicit mentions from frontmatter, canonical relation verbs, refs/trace queries, and wiki-link [[id]] conventions.
  7. DomainsScope-local packs under domains/<name>/, enabled_domains in config.yaml, domain-scoped kind overrides, and when to propose a domain vs a note.
  8. Skills libraryAgent-readable SKILL.md packs (resolver, core, capture, reflect, review), routing table, cross-skill chaining, and harness-neutral memory injection.
  9. Self-improvement loopTask ## Outcome evidence, learning beliefs, skill-proposal accept/reject flow, checkAt followups, and the rule that agents never edit skills directly.
  10. Workspace setupRun loopany init, verify bundled kinds copied, complete ONBOARDING.md once, register cadence (cron vs session-boundary), and confirm doctor passes.
  11. Capture workflowEnd-of-task capture quality gate, event→kind routing (task/signal/note), subagent dispatch pattern, and duplicate detection via artifact list --contains.
  12. Reflect workflowGather recent outcomes and dismissed signals, pattern thresholds, write learning and skill-proposal artifacts, and accept/reject proposals with git-backed skill diffs.

Complete Markdown

# loopany Documentation

> Reference for the loopany CLI and agent skill library: markdown artifacts, append-only reference graph, workspace schema, and harness-neutral install paths for long-horizon agent memory.

## Context Links

- [Agent index](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/llms.txt)
- [Human interactive docs](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8)
- [GitHub repository](https://github.com/superdesigndev/loopany)

## Repository Metadata

- Repository: superdesigndev/loopany

- Generated: 2026-06-05T19:05:11.667Z
- Updated: 2026-06-05T19:21:01.699Z
- Runtime: Grok CLI
- Format: Documentation
- Pages: 22

## Page Index

- 01. [Overview](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/01-overview.md) - What loopany exposes (CLI, workspace, skills), runtime assumptions (Bun, $LOOPANY_HOME), and the shortest path from init to a running mission-backed brain.
- 02. [Installation](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/02-installation.md) - Clone the repo, install Bun, link the CLI, set PATH, initialize $LOOPANY_HOME, and wire the resolver injection into Claude Code, Codex, or other agent hosts.
- 03. [Quickstart](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/03-quickstart.md) - First successful session: init workspace, create a mission and task, add a reference edge, run doctor, and optional reindex/search or factory UI.
- 04. [Artifacts and workspace](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/04-artifacts-and-workspace.md) - On-disk layout under $LOOPANY_HOME: artifacts/<dirName>/<slug>.md, config.yaml, references.jsonl, audit.jsonl, optional search.db, and v0.2 slug-as-id storage rules.
- 05. [Kinds and validation](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/05-kinds-and-validation.md) - Markdown kind definitions, dynamic Zod frontmatter schemas, status machines, indexedFields, slug rules, and immutable write semantics (append, status flip, supersede).
- 06. [Reference graph](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/06-reference-graph.md) - Append-only references.jsonl edges, implicit mentions from frontmatter, canonical relation verbs, refs/trace queries, and wiki-link [[id]] conventions.
- 07. [Domains](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/07-domains.md) - Scope-local packs under domains/<name>/, enabled_domains in config.yaml, domain-scoped kind overrides, and when to propose a domain vs a note.
- 08. [Skills library](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/08-skills-library.md) - Agent-readable SKILL.md packs (resolver, core, capture, reflect, review), routing table, cross-skill chaining, and harness-neutral memory injection.
- 09. [Self-improvement loop](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/09-self-improvement-loop.md) - Task ## Outcome evidence, learning beliefs, skill-proposal accept/reject flow, checkAt followups, and the rule that agents never edit skills directly.
- 10. [Workspace setup](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/10-workspace-setup.md) - Run loopany init, verify bundled kinds copied, complete ONBOARDING.md once, register cadence (cron vs session-boundary), and confirm doctor passes.
- 11. [Capture workflow](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/11-capture-workflow.md) - End-of-task capture quality gate, event→kind routing (task/signal/note), subagent dispatch pattern, and duplicate detection via artifact list --contains.
- 12. [Reflect workflow](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/12-reflect-workflow.md) - Gather recent outcomes and dismissed signals, pattern thresholds, write learning and skill-proposal artifacts, and accept/reject proposals with git-backed skill diffs.
- 13. [Periodic review](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/13-periodic-review.md) - Daily followups (--due today), weekly doctor + overdue sweep, monthly mission-drift checks, and closure rules for checkAt-bearing artifacts.
- 14. [Schema migration](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/14-schema-migration.md) - schemaVersion vs binary VERSION, SchemaVersionMismatchError recovery, loopany migrate discovery, and v0.1.0→v0.2.0 script-driven workspace transforms.
- 15. [CLI reference](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/15-cli-reference.md) - Full command surface from loopany --help: init, artifact/*, refs, trace, domain, followups, search, reindex, factory, kind list, doctor, migrate, and JSON stdout conventions.
- 16. [Artifact commands](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/16-artifact-commands.md) - create/get/list/append/status/set flags, per-kind --field validation, --slug and --content-file, journal auto-management, and required ## Outcome on terminal task statuses.
- 17. [Graph, search, and scheduling](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/17-graph-search-and-scheduling.md) - refs add/query, trace BFS, followups --due, hybrid search/reindex (--no-embed), factory UI (--port/--no-open), and audit.jsonl side effects.
- 18. [Configuration reference](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/18-configuration-reference.md) - config.yaml keys (schemaVersion, enabled_domains), environment variables LOOPANY_HOME, LOOPANY_SKIP_VERSION_CHECK, LOOPANY_EDITOR, and SCHEMA_VERSION guard behavior.
- 19. [Artifact lifecycle example](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/19-artifact-lifecycle-example.md) - End-to-end recipe: signal → task with led-to/addresses edges, status transitions, brief output, and journal linkage—mirroring test/scenario.e2e and skill-regression flows.
- 20. [Self-improvement example](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/20-self-improvement-example.md) - Recipe: three done tasks with outcomes → reflect writes learning + pending skill-proposal → user accepts → skill file diff and proposal Outcome recorded.
- 21. [Build and test](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/21-build-and-test.md) - bun install, typecheck, bun test unit/e2e, compiled binary via bun build --compile, and skill-regression.sh requirements (claude CLI, API key).
- 22. [Doctor and troubleshooting](https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/22-doctor-and-troubleshooting.md) - doctor checks (workspace, schema version, kinds, artifacts, references, onboarding), common failures (WorkspaceNotFound, Zod validation, missing search index), and factory/reindex recovery steps.

## Source File Index

- `CLAUDE.md`
- `injections/resolver-memory.md`
- `INSTALL_FOR_AGENTS.md`
- `ONBOARDING.md`
- `package.json`
- `README.md`
- `skills/loopany-capture/SKILL.md`
- `skills/loopany-core/conventions/relations.md`
- `skills/loopany-core/conventions/taxonomy.md`
- `skills/loopany-core/kinds/journal.md`
- `skills/loopany-core/kinds/learning.md`
- `skills/loopany-core/kinds/mission.md`
- `skills/loopany-core/kinds/note.md`
- `skills/loopany-core/kinds/person.md`
- `skills/loopany-core/kinds/signal.md`
- `skills/loopany-core/kinds/skill-proposal.md`
- `skills/loopany-core/kinds/task.md`
- `skills/loopany-core/SKILL.md`
- `skills/loopany-reflect/SKILL.md`
- `skills/loopany-resolver/SKILL.md`
- `skills/loopany-review/references/daily.md`
- `skills/loopany-review/references/monthly.md`
- `skills/loopany-review/references/weekly.md`
- `skills/loopany-review/SKILL.md`
- `skills/migrations/README.md`
- `skills/migrations/v0.1.0-to-v0.2.0/SKILL.md`
- `src/cli.ts`
- `src/commands/argv.ts`
- `src/commands/artifact-append.ts`
- `src/commands/artifact-create.ts`
- `src/commands/artifact-get.ts`
- `src/commands/artifact-list.ts`
- `src/commands/artifact-set.ts`
- `src/commands/artifact-status.ts`
- `src/commands/body-input.ts`
- `src/commands/doctor.ts`
- `src/commands/domain.ts`
- `src/commands/factory.ts`
- `src/commands/followups.ts`
- `src/commands/init.ts`
- `src/commands/migrate.ts`
- `src/commands/refs.ts`
- `src/commands/reindex.ts`
- `src/commands/search.ts`
- `src/commands/trace.ts`
- `src/core/artifact-store.ts`
- `src/core/audit.ts`
- `src/core/config.ts`
- `src/core/engine.ts`
- `src/core/kind-registry.ts`
- `src/core/link-parser.ts`
- `src/core/operations.ts`
- `src/core/references.ts`
- `src/core/search-store.ts`
- `src/core/slug.ts`
- `src/ui/editor.ts`
- `src/ui/server.ts`
- `src/version.ts`
- `test/artifact-store.test.ts`
- `test/cli.e2e.test.ts`
- `test/config.test.ts`
- `test/helpers/cli.ts`
- `test/kind-registry.test.ts`
- `test/migration-framework.test.ts`
- `test/migration-v0.1-to-v0.2.e2e.test.ts`
- `test/references.test.ts`
- `test/scenario.e2e.test.ts`
- `test/skill-regression.sh`
- `tsconfig.json`

---

## 01. Overview

> What loopany exposes (CLI, workspace, skills), runtime assumptions (Bun, $LOOPANY_HOME), and the shortest path from init to a running mission-backed brain.

- Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/01-overview.md
- Generated: 2026-06-05T18:56:39.037Z

### Source Files

- `README.md`
- `CLAUDE.md`
- `src/cli.ts`
- `src/version.ts`
- `package.json`
- `skills/loopany-resolver/SKILL.md`

---
title: "Overview"
description: "What loopany exposes (CLI, workspace, skills), runtime assumptions (Bun, $LOOPANY_HOME), and the shortest path from init to a running mission-backed brain."
---

loopany ships as a Bun-driven TypeScript CLI (`src/cli.ts`, entry `loopany` in `package.json`) that reads and writes a single per-user workspace under `$LOOPANY_HOME` (default `~/loopany`). Judgment lives in markdown skills under `skills/`; deterministic validation, storage, graph indexing, and search run in code. The binary reports `0.2.0` for both `loopany --version` and workspace `schemaVersion` (`src/version.ts`); `bootstrap()` refuses most commands when those versions diverge.

## What loopany exposes

Three cooperating surfaces attach any agent harness (Claude Code, Codex, Hermes, custom) to one persistent brain:

| Surface | Location | Role |
|---------|----------|------|
| **CLI** | `loopany` → `src/cli.ts` | Artifact CRUD, reference graph, domains, followups, hybrid search, migrations, `doctor`, optional `factory` UI |
| **Workspace** | `$LOOPANY_HOME` | Canonical markdown artifacts, `config.yaml`, append-only `references.jsonl` and `audit.jsonl`, optional derived `search.db` |
| **Skills** | `skills/loopany-*/SKILL.md` in the cloned repo | Routing, capture, reflect, review, kind playbooks — read by the agent, not executed by the runtime |

```mermaid
flowchart TB
  subgraph harness["Agent harness"]
    Agent["Agent session"]
  end
  subgraph skills["Skills (markdown, repo)"]
    Resolver["loopany-resolver/SKILL.md"]
    Core["loopany-core + kinds/*.md"]
    Capture["loopany-capture"]
    Reflect["loopany-reflect"]
    Review["loopany-review"]
  end
  subgraph cli["CLI runtime (Bun / TypeScript)"]
    CLI["src/cli.ts"]
    Engine["bootstrap() → engine.ts"]
    Store["ArtifactStore"]
    Refs["ReferenceGraph"]
    Index["ArtifactIndex / search.db"]
  end
  subgraph disk["$LOOPANY_HOME"]
    Artifacts["artifacts/&lt;dirName&gt;/&lt;slug&gt;.md"]
    Config["config.yaml"]
    RefsFile["references.jsonl"]
    Audit["audit.jsonl"]
  end
  Agent -->|"Read SKILL.md on demand"| Resolver
  Resolver --> Core
  Resolver --> Capture
  Resolver --> Reflect
  Resolver --> Review
  Agent -->|"loopany …"| CLI
  CLI --> Engine
  Engine --> Store
  Engine --> Refs
  Engine --> Index
  Store --> Artifacts
  Refs --> RefsFile
  Engine --> Config
  CLI --> Audit
```

<Note>
Skills are portable markdown packs — not tied to a model provider. Host memory injection (for example `injections/resolver-memory.md` into `~/.claude/CLAUDE.md` or `AGENTS.md`) only points at `~/loopany-src/skills/loopany-resolver/SKILL.md`; skill content can change via `git pull` without re-running injection.
</Note>

## Runtime assumptions

<ParamField body="Bun" type="runtime" required>
  Required to run and link the CLI (`bun install`, `bun link`, `bun run src/cli.ts`). A compiled binary is optional via `bun build --compile --outfile bin/loopany src/cli.ts`.
</ParamField>

<ParamField body="LOOPANY_HOME" type="string">
  Workspace root. Defaults to `~/loopany` when unset (`getWorkspaceRoot()` in `src/core/engine.ts`).
</ParamField>

<ParamField body="LOOPANY_SKIP_VERSION_CHECK" type="string">
  When set to `1`, skips `schemaVersion` vs `SCHEMA_VERSION` guard — used by migration scripts and honored by `bootstrap({ skipVersionCheck: true })`.
</ParamField>

<ParamField body="Node 22+" type="alternative">
  `CLAUDE.md` lists Node 22+ as an alternative runtime; the shipped scripts and tests target Bun.
</ParamField>

Workspace detection is minimal: `bootstrap()` requires `$LOOPANY_HOME/kinds` to exist (created by `loopany init`). Missing workspace → `WorkspaceNotFoundError`. Stale `config.yaml#schemaVersion` → `SchemaVersionMismatchError` with a pointer to `loopany migrate` and `skills/migrations/v*-to-v*/SKILL.md`.

## Workspace layout (v0.2)

After `loopany init`, expect:

:::files
$LOOPANY_HOME/
├── config.yaml              # schemaVersion, enabled_domains
├── kinds/                   # copied from skills/loopany-core/kinds/*.md
├── artifacts/
│   ├── missions/            # mission (flat)
│   ├── people/              # person (flat)
│   ├── tasks/               # task (default dirName: tasks)
│   ├── notes/
│   ├── journal/YYYY/        # journal (slugLayout: year)
│   └── …                    # other kinds → artifacts/<dirName>/
├── references.jsonl         # append-only graph edges
├── audit.jsonl              # CLI operation log (best-effort)
└── search.db                # optional; rebuilt by loopany reindex
:::

Artifact IDs are **slugs** (no kind prefix). Pass `--slug` on create for anything you will cite with `[[slug]]`; otherwise the CLI derives from `--title` or mints `YYYYMMDD-HHMMSS-xxx` (`src/cli.ts` help text).

Bundled core kinds (copied at init): `mission`, `task`, `signal`, `person`, `note`, `learning`, `skill-proposal`, `brief`, `journal`. Kind schemas and status machines live in markdown under `kinds/`; the runtime builds Zod validators dynamically (`KindRegistry`).

## CLI command surface (summary)

Most mutating commands call `bootstrap()`, write JSON to stdout, and append an audit row when the workspace exists.

| Group | Commands |
|-------|----------|
| Init | `init` |
| Artifacts | `artifact create \| get \| list \| append \| status \| set` |
| Graph | `refs`, `refs add`, `trace` |
| Domains | `domain list \| enable \| disable` |
| Scheduling | `followups [--due today\|overdue]` |
| Search | `search`, `reindex [--force] [--no-embed]` |
| UI | `factory [--port N] [--no-open]` |
| System | `kind list`, `doctor`, `migrate`, `--version` |

<Info>
`doctor` and `migrate` call `bootstrap({ skipVersionCheck: true })` so they can report or fix version skew instead of crashing first.
</Info>

## Skills library and resolver gate

Every loopany interaction is expected to start at `skills/loopany-resolver/SKILL.md`:

```bash
loopany artifact list --kind mission --status active
```

**No active mission → run `ONBOARDING.md` and stop.** Core and capture skills repeat the same bootstrap rule.

| Skill | Triggers (examples) |
|-------|---------------------|
| `loopany-resolver` | Session start, ambiguous intent, multi-skill workflows |
| `loopany-core` | Any artifact CRUD; relation verb choice |
| `loopany-capture` | Substantive work ended (PR shipped, incident resolved) |
| `loopany-reflect` | ≥3 done tasks with outcomes; accept/reject skill-proposals |
| `loopany-review` | Daily followups, weekly doctor/sweep, monthly mission drift |

Cross-skill chains documented in the resolver include **capture → core**, **capture → reflect** (after ≥3 captures), **reflect → core** (writes `learning` / `skill-proposal`), and **review → reflect** on weekly resolution thresholds.

Agents **never edit skill files directly** — behavior changes flow through `skill-proposal` artifacts, user accept/reject, then git-backed diffs (see self-improvement docs).

## Mission-backed brain

A mission artifact (`kinds/mission.md`) is the standing intention the brain serves: long-running pursuit, `status: active`, body sections like `## Current hypothesis`. Tasks, signals, and briefs relate to it via `mentions`, `domain`, or graph edges — not by replacing the mission kind.

`loopany doctor` treats onboarding as incomplete without:

- a `self` **person** artifact, and  
- at least one **mission** with `status: active`

Until both exist, the resolver intentionally blocks routine work — the workspace has no declared purpose.

## Shortest path: init → running brain

<Steps>
<Step title="Install CLI and clone skills source">

```bash
git clone https://github.com/superdesigndev/loopany.git ~/loopany-src && cd ~/loopany-src
curl -fsSL https://bun.sh/install | bash
export PATH="$HOME/.bun/bin:$PATH"
bun install && bun link
loopany --version
```

</Step>

<Step title="Initialize workspace">

```bash
loopany init
# Optional: LOOPANY_HOME=/path/to/brain loopany init
```

Creates `$LOOPANY_HOME/kinds`, `artifacts/`, and `config.yaml` with `schemaVersion: 0.2.0`. Idempotent — existing files are preserved. If no `artifacts/missions/*.md` exists, init prints a pointer to `ONBOARDING.md`.

</Step>

<Step title="Onboard and create active mission">

Read `ONBOARDING.md` once. The agent runs a short conversation, then creates artifacts — including one `mission` with `status: active` and usually a `person` slug `self`. Do not re-run if an active mission already exists.

Example mission create (fields per `kinds/mission.md`):

```bash
loopany artifact create --kind mission \
  --slug ship-v1 \
  --title "Ship loopany v1" \
  --status active \
  --content-file -
```

</Step>

<Step title="Wire resolver into host memory (coding CLIs)">

Append `injections/resolver-memory.md` so every session ends by re-reading the resolver (Claude Code: `~/.claude/CLAUDE.md`; Codex: `~/AGENTS.md`). Details in `INSTALL_FOR_AGENTS.md` Step 5.

</Step>

<Step title="Verify">

```bash
loopany doctor
loopany artifact list --kind mission --status active
```

Exit `0` from `doctor` means workspace, kinds, artifacts, references, and onboarding checks passed. Warnings (for example tasks without `mentions` of a mission) do not fail the run.

</Step>
</Steps>

After verification, normal operation is: resolver → appropriate skill → `loopany artifact …` / `loopany refs …`. End-of-task capture and periodic review keep the graph and outcomes current; `loopany reindex` rebuilds search when needed.

## Design constraints (operational)

- **Thin harness, fat skills** — code handles I/O, validation, and graph indexing; judgment stays in SKILL.md files.  
- **Markdown + JSONL source of truth** — no database; `search.db` is disposable.  
- **Immutable cited artifacts** — append body sections, transition `status`, or supersede; do not edit agent-produced history in place.  
- **Open registries** — `kind` and `relation` extend via config/markdown, not TypeScript enums.  
- **Agent proposes, human accepts** — new kinds, domains, and skill changes go through explicit artifacts and user decisions.

Unit and E2E tests run with `bun test`; skill regression (`test/skill-regression.sh`) exercises the five skills against a fresh `LOOPANY_HOME` per scenario.

## Related pages

<CardGroup>
<Card title="Installation" href="/installation">
Clone, Bun, `bun link`, PATH, `$LOOPANY_HOME`, and resolver injection into agent hosts.
</Card>
<Card title="Quickstart" href="/quickstart">
First session: mission, task, reference edge, and `doctor`.
</Card>
<Card title="Artifacts and workspace" href="/artifacts-and-workspace">
On-disk layout, slug-as-id rules, and v0.2 storage paths.
</Card>
<Card title="Skills library" href="/skills-library">
Resolver routing, skill packs, and harness-neutral memory injection.
</Card>
<Card title="Workspace setup" href="/workspace-setup">
`loopany init`, bundled kinds, onboarding, cadence, and doctor checks.
</Card>
<Card title="CLI reference" href="/cli-reference">
Full command list, flags, and JSON stdout conventions.
</Card>
</CardGroup>

---

## 02. Installation

> Clone the repo, install Bun, link the CLI, set PATH, initialize $LOOPANY_HOME, and wire the resolver injection into Claude Code, Codex, or other agent hosts.

- Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/02-installation.md
- Generated: 2026-06-05T18:56:45.415Z

### Source Files

- `INSTALL_FOR_AGENTS.md`
- `package.json`
- `src/cli.ts`
- `injections/resolver-memory.md`
- `src/commands/init.ts`
- `ONBOARDING.md`

---
title: "Installation"
description: "Clone the repo, install Bun, link the CLI, set PATH, initialize $LOOPANY_HOME, and wire the resolver injection into Claude Code, Codex, or other agent hosts."
---

loopany ships as a Bun-linked CLI (`package.json` → `src/cli.ts`) that scaffolds a per-user workspace at `$LOOPANY_HOME` (default `~/loopany`) via `loopany init`, while the skill library and resolver injection stay in the cloned repository checkout (conventionally `~/loopany-src`).

## Prerequisites

| Requirement | Notes |
|-------------|--------|
| [Bun](https://bun.sh) | Runtime and package manager; the CLI shebang is `#!/usr/bin/env bun`. |
| Git | Clone `https://github.com/superdesigndev/loopany`. |
| Agent host (optional) | Claude Code, Codex, Hermes, OpenClaw, or any harness that can read markdown skills and append persistent memory. |

<Note>
`package.json` lists version `0.1.0`; `loopany --version` prints the binary version from `src/version.ts` (currently `0.2.0` with workspace schema `0.2.0`).
</Note>

## Repository layout after clone

Two on-disk locations matter: the **source checkout** (CLI + skills + injections) and the **workspace** (artifacts + copied kinds + config).

```text
~/loopany-src/                    # git clone (path is your choice)
├── src/cli.ts                    # bin entry: loopany
├── skills/
│   ├── loopany-resolver/SKILL.md # dispatcher — read at end of tasks
│   ├── loopany-core/kinds/*.md   # bundled kind defs copied by init
│   └── loopany-capture|reflect|review/ ...
└── injections/resolver-memory.md # host memory snippet

~/loopany/                        # $LOOPANY_HOME (default)
├── config.yaml                   # schemaVersion, enabled_domains
├── kinds/*.md                    # copied once from bundled kinds
├── artifacts/                    # markdown artifacts (created on use)
├── references.jsonl              # created on first graph edge write
└── audit.jsonl                   # created on first post-init CLI op
```

## Install the CLI

<Steps>
<Step title="Clone the repository">

```bash
git clone https://github.com/superdesigndev/loopany.git ~/loopany-src
cd ~/loopany-src
```

</Step>

<Step title="Install Bun (if needed)">

```bash
curl -fsSL https://bun.sh/install | bash
export PATH="$HOME/.bun/bin:$PATH"
```

Persist `export PATH="$HOME/.bun/bin:$PATH"` in `~/.zshrc` or `~/.bashrc`, then `source` the file.

</Step>

<Step title="Install dependencies and link globally">

```bash
bun install && bun link
```

`bun link` registers the `loopany` bin from `package.json` (`"loopany": "./src/cli.ts"`) on your PATH via Bun’s global link.

</Step>

<Step title="Verify the CLI">

```bash
loopany --version
```

<Check>
Expected output shape: `loopany 0.2.0` (semver from `src/version.ts`).
</Check>

If `loopany` is not found, Bun’s bin directory is not on `PATH` — add `$HOME/.bun/bin` and open a new shell.

</Step>
</Steps>

### Optional: compiled binary

For a standalone executable without invoking `bun run` each time:

```bash
bun run build
# writes bin/loopany via: bun build --compile --outfile bin/loopany src/cli.ts
```

Add the `bin/` directory to `PATH`, or copy `bin/loopany` somewhere already on `PATH`.

## Initialize the workspace

`loopany init` scaffolds `$LOOPANY_HOME`, copies bundled kind definitions from `skills/loopany-core/kinds/`, and writes `config.yaml` with the current `SCHEMA_VERSION`. The command is **idempotent**: existing files are left untouched; only missing paths and kind files are created.

```bash
loopany init
```

<ResponseExample>

```text
Initialized loopany workspace at /Users/you/loopany
Created N file(s).

NEXT — read ONBOARDING.md and start the onboarding conversation.
Without an active mission artifact, the brain has no reason to exist.
```

</ResponseExample>

The `NEXT` block appears when `artifacts/missions/` has no `.md` files yet (`needsOnboarding` in `runInit`).

### Custom workspace location

<ParamField body="LOOPANY_HOME" type="path">
Overrides the default `~/loopany`. Resolved in `getWorkspaceRoot()` as `process.env.LOOPANY_HOME ?? join(homedir(), 'loopany')`. Applies to every command after export — not only `init`.
</ParamField>

```bash
export LOOPANY_HOME=/path/to/brain
loopany init
```

<Warning>
Most commands call `bootstrap()`, which requires a `kinds/` directory under `$LOOPANY_HOME`. Without `loopany init`, you get `WorkspaceNotFoundError: No loopany workspace at … Run loopany init first.`
</Warning>

### What `init` creates

| Path | Created by init |
|------|-----------------|
| `$LOOPANY_HOME/` | Yes (if missing) |
| `kinds/` | Yes; copies `task`, `signal`, `person`, `mission`, `note`, `learning`, `skill-proposal`, `brief`, `journal` from bundled kinds |
| `artifacts/` | Empty directory |
| `config.yaml` | Yes, with `schemaVersion: <SCHEMA_VERSION>` |
| `references.jsonl` | No — first `refs add` or append creates the file |
| `domains/` | No — created when domains are enabled later |

## Agent-driven install (harness paste)

Hosts can fetch the canonical agent install doc without shell steps from the user:

```text
Retrieve and follow the instructions at:
https://raw.githubusercontent.com/superdesigndev/loopany/main/INSTALL_FOR_AGENTS.md
```

That document covers clone → link → `init` → onboarding → cadence choice → resolver injection → `loopany doctor`, including tone rules (keep CLI vocabulary out of user-facing chat).

## Wire resolver injection into agent hosts

loopany does not load skills at runtime. Agents **read** markdown under `~/loopany-src/skills/` on demand. The injection only adds one persistent rule: before the final reply on user-requested work, read `~/loopany-src/skills/loopany-resolver/SKILL.md`.

Canonical snippet (`injections/resolver-memory.md`):

```markdown
## loopany skill resolver

Dispatcher lives at `~/loopany-src/skills/loopany-resolver/SKILL.md`.

**At the end of every user-requested task**, before the final message
where you'd normally report "done" / "shipped" / "here is the answer",
**Read `~/loopany-src/skills/loopany-resolver/SKILL.md` first**, then reply.
```

Adjust `~/loopany-src` if you cloned elsewhere; the injection path must match your checkout.

### Coding CLIs (Claude Code, Codex)

<Tabs>
<Tab title="Claude Code (user-wide)">

Appends once (idempotent `grep` guard):

```bash
grep -q "loopany skill resolver" ~/.claude/CLAUDE.md 2>/dev/null || \
  cat ~/loopany-src/injections/resolver-memory.md >> ~/.claude/CLAUDE.md
```

</Tab>
<Tab title="Codex">

```bash
TARGET="$HOME/AGENTS.md"
grep -q "loopany skill resolver" "$TARGET" 2>/dev/null || \
  cat ~/loopany-src/injections/resolver-memory.md >> "$TARGET"
```

</Tab>
<Tab title="Claude Code (per-project)">

Append to `./CLAUDE.md` in the project root, or to `~/.claude/projects/<slug>/memory/MEMORY.md` where `<slug>` is the project absolute path with `/` replaced by `-` (e.g. `/Users/x/dev/y` → `-Users-x-dev-y`).

</Tab>
</Tabs>

### Agent platforms (Hermes, OpenClaw, …)

Register the same text via the host memory primitive:

```bash
/memory add "$(cat ~/loopany-src/injections/resolver-memory.md)"
```

If multi-line values are rejected, collapse newlines: `tr '\n' ' ' < ~/loopany-src/injections/resolver-memory.md`.

### Verify injection

File checks (concrete, not self-query):

```bash
grep -l "loopany skill resolver" ~/.claude/CLAUDE.md ~/AGENTS.md 2>/dev/null
test -f ~/loopany-src/skills/loopany-resolver/SKILL.md && echo "RESOLVER present"
```

Start a **fresh** session and ask the agent where the loopany resolver lives. A correct answer cites `~/loopany-src/skills/loopany-resolver/SKILL.md` without searching — otherwise the memory file exists but did not load (check host memory scope rules).

<Info>
Re-running the `grep -q … || cat …` install is a no-op when the marker string is already present. `git pull` in `~/loopany-src` updates skill content; you do not need to re-inject unless the pointer path changed. A `doctor` check for resolver registration is noted as roadmap, not implemented yet.
</Info>

## Runtime integration model

```mermaid
flowchart LR
  subgraph repo ["~/loopany-src (checkout)"]
    CLI["src/cli.ts / loopany bin"]
    SK["skills/loopany-*"]
    INJ["injections/resolver-memory.md"]
  end
  subgraph ws ["$LOOPANY_HOME"]
    CFG["config.yaml"]
    KD["kinds/*.md"]
    ART["artifacts/**"]
    REF["references.jsonl"]
  end
  subgraph host ["Agent host memory"]
    MEM["CLAUDE.md / AGENTS.md / /memory"]
  end
  CLI -->|"loopany init"| ws
  INJ -->|"append once"| MEM
  MEM -.->|"Read resolver before final reply"| SK
  SK -->|"loopany artifact *"| CLI
  CLI --> ws
```

## Post-install: onboarding and cadence

After `init` with no mission artifacts:

1. Read `ONBOARDING.md` and run the conversation (creates `self` person + active `mission` artifacts).
2. Choose cadence **without asking the user** on coding CLIs: default is session-boundary prompts (`loopany followups --due today`, weekly sweep/reflect proposals) rather than host cron — Claude Code recurring cron expires after ~7 days per `INSTALL_FOR_AGENTS.md`.

`loopany doctor` treats incomplete onboarding as **fail** until `self` exists and at least one `mission` has `status: active`.

## Verify installation

```bash
loopany doctor
```

| Exit code | Meaning |
|-----------|---------|
| `0` | All non-fail checks passed |
| `1` | At least one check failed (schema version, kinds parse, artifacts, references, onboarding, …) |

Machine-readable report:

```bash
loopany doctor --format json
```

Typical checks after a full agent install: `workspace`, `schema version`, `kinds`, `artifacts`, `references`, `onboarding` (and warnings for `mission coverage`, `domain coverage`).

### Schema version mismatch

If `config.yaml#schemaVersion` lags the binary’s `SCHEMA_VERSION`, normal commands raise `SchemaVersionMismatchError` and point at `loopany migrate`. `doctor` and `migrate` bypass the guard to report or fix the workspace.

<ParamField body="LOOPANY_SKIP_VERSION_CHECK" type="string">
Set to `1` to skip schema guard (migration scripts and tests).
</ParamField>

## Environment variables

| Variable | Default | Role |
|----------|---------|------|
| `LOOPANY_HOME` | `~/loopany` | Workspace root for all CLI operations |
| `LOOPANY_SKIP_VERSION_CHECK` | unset | When `1`, skips `schemaVersion` vs binary check |
| `LOOPANY_EDITOR` | OS default | Optional editor override for `factory` UI (see `src/ui/editor.ts`) |

## Troubleshooting

| Symptom | Likely cause | Fix |
|---------|----------------|-----|
| `loopany: command not found` | Bun bin not on `PATH` | Add `$HOME/.bun/bin` to shell rc |
| `No loopany workspace at …` | Missing `init` or wrong `LOOPANY_HOME` | `export LOOPANY_HOME=…` then `loopany init` |
| `SchemaVersionMismatchError` | Stale workspace | `loopany migrate` per error message |
| Resolver not followed | Injection missing or wrong scope | Re-run idempotent append; fresh session; verify `grep` |
| `doctor` onboarding fail | No `self` / no active mission | Complete `ONBOARDING.md` Phase 3 |
| Kind file missing after init | Init skipped copy because file existed | Compare `$LOOPANY_HOME/kinds/` with `skills/loopany-core/kinds/` manually |

## Related pages

<CardGroup>
<Card title="Overview" href="/overview">
What loopany exposes (CLI, workspace, skills) and the shortest path from init to a mission-backed brain.
</Card>
<Card title="Quickstart" href="/quickstart">
First session: mission, task, reference edge, and doctor.
</Card>
<Card title="Workspace setup" href="/workspace-setup">
Post-init verification, onboarding completion, and cadence registration.
</Card>
<Card title="Configuration reference" href="/configuration-reference">
`config.yaml` keys and environment variables in depth.
</Card>
<Card title="Doctor and troubleshooting" href="/doctor-and-troubleshooting">
Full doctor check list and recovery steps.
</Card>
<Card title="Skills library" href="/skills-library">
Resolver routing table and skill pack layout.
</Card>
</CardGroup>

---

## 03. Quickstart

> First successful session: init workspace, create a mission and task, add a reference edge, run doctor, and optional reindex/search or factory UI.

- Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/03-quickstart.md
- Generated: 2026-06-05T18:56:50.466Z

### Source Files

- `src/cli.ts`
- `src/commands/init.ts`
- `src/commands/artifact-create.ts`
- `src/commands/doctor.ts`
- `skills/loopany-core/kinds/mission.md`
- `skills/loopany-core/kinds/task.md`
- `test/scenario.e2e.test.ts`

---
title: "Quickstart"
description: "First successful session: init workspace, create a mission and task, add a reference edge, run doctor, and optional reindex/search or factory UI."
---

`loopany init` scaffolds a workspace at `$LOOPANY_HOME` (default `~/loopany`), copies bundled kind definitions from `skills/loopany-core/kinds/`, and writes `config.yaml` with the current `schemaVersion`. Every other command in this guide assumes that workspace exists and that at least one `mission` with `status: active` plus a `self` person artifact are present — without them, `loopany doctor` fails the onboarding check and `init` prints a pointer to `ONBOARDING.md`.

## Prerequisites

| Requirement | Notes |
|-------------|-------|
| [Bun](https://bun.sh) | Runtime for `src/cli.ts` and tests |
| `loopany` on `PATH` | From repo: `bun install` then `bun link`, or `bun run src/cli.ts` |
| Writable `$LOOPANY_HOME` | Override with `export LOOPANY_HOME=/path/to/workspace` for isolated runs |

<Note>
CLI commands that mutate or query artifacts print **JSON on stdout** (one object or array). Human-readable text appears only for `init`, `doctor` (default format), `factory`, and `--help`.
</Note>

## Session overview

```mermaid
sequenceDiagram
  participant You
  participant CLI as loopany CLI
  participant WS as $LOOPANY_HOME
  participant Graph as references.jsonl

  You->>CLI: init
  CLI->>WS: kinds/, artifacts/, config.yaml
  You->>CLI: artifact create (person, mission, task)
  CLI->>WS: artifacts/people/self.md, missions/*.md, tasks/*.md
  You->>CLI: refs add
  CLI->>Graph: append edge
  You->>CLI: doctor
  CLI->>WS: validate kinds, artifacts, graph, onboarding
  opt Search / UI
    You->>CLI: reindex
    CLI->>WS: search.db
    You->>CLI: search / factory
  end
```

After a successful pass, the workspace holds markdown artifacts under `artifacts/<dirName>/<id>.md`, hard edges in `references.jsonl`, and operational lines in `audit.jsonl`.

## Initialize the workspace

<Steps>
<Step title="Run init">

```bash
loopany init
```

Creates (if missing): workspace root, `kinds/`, `artifacts/`, and `config.yaml` with `schemaVersion` matching the binary. Copies any bundled `*.md` kind files not already present in `kinds/`.

<RequestExample>

```bash
loopany init
```

</RequestExample>

<ResponseExample>

```text
Initialized loopany workspace at /Users/you/loopany
Created 12 file(s).

NEXT — read ONBOARDING.md and start the onboarding conversation.
Without an active mission artifact, the brain has no reason to exist.
```

</ResponseExample>

The onboarding hint appears when `artifacts/missions/` has no `.md` files yet. Re-running `init` is idempotent — existing files are left untouched.

</Step>
<Step title="Confirm layout">

```text
$LOOPANY_HOME/
  config.yaml
  kinds/           # mission.md, task.md, person.md, …
  artifacts/       # empty until you create artifacts
  audit.jsonl      # written after first mutating command
```

Bundled kinds after init include: `brief`, `journal`, `learning`, `mission`, `note`, `person`, `signal`, `skill-proposal`, `task`.

</Step>
</Steps>

For a disposable workspace (matches e2e tests):

```bash
export LOOPANY_HOME="$(mktemp -d)"
loopany init
```

## Minimum viable brain

Doctor’s **onboarding** check requires:

1. A `person` artifact with id `self`
2. At least one `mission` with `status: active`

<Steps>
<Step title="Create self">

```bash
loopany artifact create \
  --kind person \
  --slug self \
  --name "Your Name"
```

<ResponseField name="stdout" type="object">
JSON with `id`, `kind`, `path`. For `--slug self`, `id` is `self` and the file is `artifacts/people/self.md`.
</ResponseField>

</Step>
<Step title="Create an active mission">

```bash
loopany artifact create \
  --kind mission \
  --slug ship-v1 \
  --title "Ship loopany v1" \
  --status active \
  --hypothesis "Dogfood CLI daily and fix friction as it appears"
```

Required frontmatter for `mission`: `title` (string), `status` (`active` | `paused` | `satisfied` | `abandoned`). Optional: `hypothesis`, `domain`.

Mission files live at `artifacts/missions/<slug>.md`. The slug becomes the global artifact id used in `refs`, `mentions`, and `[[wiki-links]]`.

</Step>
<Step title="Create a task linked to the mission">

```bash
loopany artifact create \
  --kind task \
  --slug quickstart-smoke \
  --title "Verify loopany quickstart path" \
  --status todo \
  --priority medium \
  --mentions ship-v1
```

`task` defaults `status` to `todo` if omitted. `mentions` is a comma-separated list of artifact ids (here, the mission slug).

</Step>
</Steps>

<ParamField body="--slug" type="string">
Stable id for citations. Omitted: derived from `--title` with collision suffixes, or a time-based slug when no title is usable.
</ParamField>

<ParamField body="--content / --content-file" type="string">
Optional markdown body at create time. Use `--content-file -` to read stdin.
</ParamField>

Field flags are per-kind. Unknown flags produce an error listing valid fields for that kind (see `~/loopany/kinds/<kind>.md` or `loopany kind list`).

## Add a reference edge

Hard graph edges are append-only records in `references.jsonl`, written by the CLI (not by editing artifact bodies alone).

Link a task to the mission with an explicit edge (in addition to `mentions` in frontmatter, which the index also treats as soft links):

```bash
loopany refs add \
  --from quickstart-smoke \
  --to ship-v1 \
  --relation mentions
```

<ResponseExample>

```json
{
  "from": "quickstart-smoke",
  "to": "ship-v1",
  "relation": "mentions",
  "ts": "2026-06-05T12:00:00.000Z",
  "actor": "cli"
}
```

</ResponseExample>

Query outgoing edges:

```bash
loopany refs quickstart-smoke
```

Common relations (open registry; these are conventions): `led-to`, `addresses`, `mentions`, `supersedes`, `follows-up`, `cites`. See the relations convention skill for direction rules — e.g. `led-to` runs **from** cause **to** effect (`signal` → `task`).

## Verify with doctor

```bash
loopany doctor
```

Exit code `0` only when no check has status `fail`. Typical human output:

```text
Workspace       /Users/you/loopany ✓
Schema version  v0.2.0 ✓
Kinds           9 loaded (brief, journal, …) ✓
Artifacts       3 total, all valid ✓
References      1 edges, no dangling ✓
Onboarding      self present, 1 active mission(s) ✓
Mission coverage all tasks mention a mission ✓
Domain coverage enabled: [none] ✓
```

| Check | Fail vs warn |
|-------|----------------|
| `workspace`, `schema version`, `kinds`, `artifacts`, `references`, `onboarding` | **fail** — fix before relying on the brain |
| `mission coverage` | **warn** — tasks without `mentions` pointing at a mission id |
| `domain coverage` | **warn** — artifact `domain` not in `enabled_domains` |

JSON report:

```bash
loopany doctor --format json
```

Doctor runs with schema version check bypassed so it can **report** mismatches and suggest `loopany migrate …` instead of crashing.

<Warning>
A fresh `init` workspace fails onboarding until `self` and an active `mission` exist. That is expected — not a broken install.
</Warning>

## Optional: search and factory

### Hybrid search

Search uses a derived SQLite index at `$LOOPANY_HOME/search.db`. Creates and appends do **not** auto-update the index; run `reindex` after bulk writes or when search feels stale.

```bash
loopany reindex --no-embed    # fast CI-style index; keyword FTS only
loopany reindex               # full index with embeddings (first run loads model)
loopany search "quickstart" --kind task --limit 5
```

If `search.db` is missing, `loopany search` returns `[]` and prints on stderr: `no search index found — run loopany reindex first.`

| Flag | Effect |
|------|--------|
| `--force` | Delete and rebuild `search.db` |
| `--no-embed` | Skip embedding model; FTS keyword path only |

### Factory UI

Read-only walkable view of the workspace:

```bash
loopany factory --port 4242 --no-open
```

Defaults: host `127.0.0.1`, port `4242`, opens browser on macOS/Windows/Linux unless `--no-open`. Ctrl-C stops the server.

## Complete copy-paste script

Sets `LOOPANY_HOME` to a temp dir, runs the full minimal path, and leaves doctor green:

```bash
export LOOPANY_HOME="$(mktemp -d)"
loopany init

loopany artifact create --kind person --slug self --name "Quickstart User"
loopany artifact create \
  --kind mission --slug ship-v1 \
  --title "Ship loopany v1" --status active
loopany artifact create \
  --kind task --slug quickstart-smoke \
  --title "Verify loopany quickstart path" \
  --status todo --mentions ship-v1

loopany refs add --from quickstart-smoke --to ship-v1 --relation mentions
loopany doctor
```

Inspect artifacts:

```bash
loopany artifact get ship-v1
loopany artifact list --kind task --status todo
```

## Agent-led onboarding

If you use an agent harness instead of hand-typing commands, point it at `ONBOARDING.md` after `init`. That script creates `self`, one or more active missions, and optional backfill artifacts — the same end state this quickstart builds manually. Do not run onboarding twice when an active mission already exists.

## Troubleshooting

| Symptom | Likely cause | Action |
|---------|----------------|--------|
| `Error: … run loopany init` | `$LOOPANY_HOME` missing or empty | `loopany init` or set `LOOPANY_HOME` |
| `Invalid input:` + Zod paths | Wrong `--field` for kind | `loopany kind list`; read `kinds/<kind>.md` |
| Doctor onboarding ✗ | No `self` or no active mission | Create artifacts above |
| Doctor schema version ✗ | Workspace older than binary | `loopany migrate` (see migration docs) |
| `Unknown field for kind X` | Typo in flag name | Use `--check-at` not `--checkAt` (kebab-case flags) |
| Search always empty | No index | `loopany reindex` |
| `refs add` error | Missing `--from` / `--to` / `--relation` | All three required |

<Info>
`LOOPANY_SKIP_VERSION_CHECK=1` bypasses the schema guard for emergency inspection; prefer `loopany migrate` for real version drift.
</Info>

## What you have now

```text
artifacts/
  people/self.md
  missions/ship-v1.md
  tasks/quickstart-smoke.md
references.jsonl    # mentions edge(s)
audit.jsonl         # init, creates, refs, doctor ops
config.yaml         # schemaVersion, enabled_domains (when set)
```

Next operational steps (not required for “first success”): complete a task with `artifact append --section Outcome` then `artifact status <id> done`, run `loopany followups --due today` when `checkAt` is set, or wire the resolver skill into your agent host per the installation page.

## Related pages

<CardGroup>
<Card title="Installation" href="/installation">
Clone, link the CLI, set `PATH` and `$LOOPANY_HOME`, and attach the resolver to your agent host.
</Card>
<Card title="Artifacts and workspace" href="/artifacts-and-workspace">
On-disk layout, slug-as-id rules, and immutable write semantics.
</Card>
<Card title="Reference graph" href="/reference-graph">
Relation verbs, `refs`/`trace` queries, and wiki-link conventions.
</Card>
<Card title="Artifact lifecycle example" href="/artifact-lifecycle-example">
Signal → task → outcome flow aligned with `test/scenario.e2e.test.ts`.
</Card>
<Card title="Doctor and troubleshooting" href="/doctor-and-troubleshooting">
Full check list, JSON format, and recovery patterns.
</Card>
<Card title="CLI reference" href="/cli-reference">
Complete command surface and stdout conventions.
</Card>
</CardGroup>

---

## 04. Artifacts and workspace

> On-disk layout under $LOOPANY_HOME: artifacts/<dirName>/<slug>.md, config.yaml, references.jsonl, audit.jsonl, optional search.db, and v0.2 slug-as-id storage rules.

- Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/04-artifacts-and-workspace.md
- Generated: 2026-06-05T18:57:39.562Z

### Source Files

- `src/core/artifact-store.ts`
- `src/core/engine.ts`
- `src/commands/init.ts`
- `src/core/config.ts`
- `src/core/audit.ts`
- `skills/loopany-core/kinds/journal.md`
- `INSTALL_FOR_AGENTS.md`

---
title: "Artifacts and workspace"
description: "On-disk layout under $LOOPANY_HOME: artifacts/<dirName>/<slug>.md, config.yaml, references.jsonl, audit.jsonl, optional search.db, and v0.2 slug-as-id storage rules."
---

The loopany workspace is a single directory tree (default `~/loopany`, overridable via `LOOPANY_HOME`) where markdown artifacts are the source of truth, append-only JSONL files record graph edges and CLI operations, and an optional SQLite `search.db` is a derived index rebuilt by `loopany reindex`.

## Workspace root

<ParamField body="LOOPANY_HOME" type="path">
Overrides the default workspace location. When unset, the root is `~/loopany` (from `os.homedir()`).
</ParamField>

Bootstrap treats a directory as a workspace when `kinds/` exists. If not, commands throw `WorkspaceNotFoundError` with a pointer to `loopany init`.

| Path | Created by | Role |
|------|------------|------|
| `config.yaml` | `loopany init` (if missing) | `schemaVersion`, `enabled_domains` |
| `kinds/*.md` | `loopany init` copies bundled defs | Kind schemas, status machines, `dirName` / `slugLayout` |
| `artifacts/` | `loopany init` | All artifact markdown files |
| `domains/<name>/kinds/` | Agent / user (not init) | Domain-scoped kind overrides when domain enabled |
| `references.jsonl` | First `refs add` or hand edit | Append-only explicit graph edges |
| `audit.jsonl` | First CLI command after workspace exists | Append-only operational log |
| `search.db` | `loopany reindex` | Derived hybrid search index (safe to delete) |

`loopany init` is idempotent: it creates missing directories and `config.yaml`, copies any bundled kind files from `skills/loopany-core/kinds/` that are not already present, and leaves existing files untouched.

<RequestExample>

```bash
loopany init
```

</RequestExample>

<ResponseExample>

```text
Workspace: /Users/you/loopany
Created: config.yaml, kinds/task.md, ...
Needs onboarding: true   # when artifacts/missions/ has no .md files
```

</ResponseExample>

## Directory layout (v0.2)

:::files
$LOOPANY_HOME/
├── config.yaml
├── kinds/                    # global kind definitions
├── artifacts/
│   ├── tasks/                # flat: artifacts/<dirName>/<slug>.md
│   ├── signals/
│   ├── briefs/
│   ├── learnings/
│   ├── skill-proposals/
│   ├── missions/
│   ├── notes/
│   ├── people/
│   └── journal/
│       └── 2026/             # year layout only
│           └── 2026-06-05.md
├── domains/                  # optional scope packs
│   └── <name>/kinds/
├── references.jsonl
├── audit.jsonl
└── search.db                 # optional; from reindex
:::

<Note>
v0.1 used date buckets such as `artifacts/2026-06/tsk-*.md` and kind-prefixed IDs (`tsk-`, `sig-`). Current binaries expect `schemaVersion: 0.2.0` and flat `artifacts/<dirName>/` paths. See [Schema migration](/schema-migration) if bootstrap reports a version mismatch.
</Note>

### Kind → `dirName` mapping

Each kind definition in `kinds/*.md` (or `domains/<name>/kinds/*.md`) declares storage under `artifacts/`. The registry parses `dirName` and `slugLayout` from kind frontmatter.

| Kind | `dirName` | `slugLayout` | Notes |
|------|-----------|--------------|-------|
| `task` | `tasks` | `flat` (default) | Default `dirName` = `{kind}s` |
| `signal` | `signals` | `flat` | |
| `brief` | `briefs` | `flat` | |
| `learning` | `learnings` | `flat` | |
| `skill-proposal` | `skill-proposals` | `flat` | Hyphenated kind → `{kind}s` |
| `mission` | `missions` | `flat` | Explicit in kind file |
| `note` | `notes` | `flat` | |
| `person` | `people` | `flat` | |
| `journal` | `journal` | `year` | `artifacts/journal/<YYYY>/<YYYY-MM-DD>.md` |

`slugLayout: year` adds a four-digit year subdirectory derived from the first four characters of the artifact id (used by `journal`, whose id is the calendar date).

```mermaid
flowchart TB
  subgraph workspace["$LOOPANY_HOME"]
    config["config.yaml"]
    kinds["kinds/*.md"]
    arts["artifacts/"]
    refs["references.jsonl"]
    audit["audit.jsonl"]
    search["search.db optional"]
  end
  subgraph engine["bootstrap()"]
    KR["KindRegistry.load"]
    AS["ArtifactStore"]
    RG["ReferenceGraph"]
  end
  kinds --> KR
  KR --> AS
  arts --> AS
  refs --> RG
  config --> engine
  AS --> arts
```

## Slug-as-id rules (v0.2)

The filename stem **is** the artifact id. There is no kind prefix in ids or paths. Global uniqueness is enforced at create time by scanning every registered kind directory for `<id>.md`.

### Allocation order

On `artifact create`, the store picks an id in this order:

1. **Caller slug** — `--slug` (validated, must not exist).
2. **Title-derived** — `slugifyTitle(title)` from validated frontmatter; collisions get `-2`, `-3`, … up to 99 attempts.
3. **Timestamp fallback** — `YYYYMMDD-HHMMSS-<3hex>` UTC when title slugify fails (emoji-only titles, empty slugify, or collision exhaustion).

### Slug validation

| Rule | Constraint |
|------|------------|
| Length | 1–60 Unicode codepoints |
| Charset | Letters, marks, digits, `-`, `_` (NFC-normalized) |
| Edges | No leading/trailing `-` or `_`; no `--` or `__` |
| Uniqueness | Global across all kinds (store-enforced) |

Wiki links and CLI commands use this bare slug: `loopany artifact get reddit-claude-design`, `[[reddit-claude-design]]` in body text.

### Resolution and timestamps

- **`get(id)`** — Probes each kind’s storage path until a file exists (~10 kinds, cheap stat calls).
- **`createdAt` / `updatedAt`** — Stamped on every write; `createdAt` set on create if omitted.
- **Built-in frontmatter fields** — `domain`, `createdAt`, `updatedAt`, `_backfilled` (migration marker) bypass per-kind field specs.

## Artifact file format

Each artifact is one markdown file: YAML frontmatter + body. Kind-specific frontmatter is validated with Zod schemas built from the kind’s `## Frontmatter` YAML block.

**Mutations** (enforced by store / CLI):

| Operation | Effect |
|-----------|--------|
| `artifact append` | Appends a `## Section` block to the body |
| `artifact status` | Updates `status` via the kind’s state machine |
| `artifact set` | Updates a non-status frontmatter field |
| In-place body rewrite for “cited” kinds | Avoided — append, status flip, or supersede |

<Warning>
Do not use `artifact set` on `status`; use `artifact status` so transitions are validated against the kind’s status machine.
</Warning>

### Journal (auto-managed spine)

The `journal` kind is not created via `artifact create --kind journal` (rejected at CLI). The store lazily creates `artifacts/journal/<YYYY>/<YYYY-MM-DD>.md` when the first non-journal artifact is created that day, and appends a wiki-link line to `## Activity` (or `## Backfilled` when `_backfilled: true`).

Journal ids equal the date string (`2026-06-05`). The year subdirectory is for human browsing only; resolution still uses the global slug.

## `config.yaml`

<ParamField body="schemaVersion" type="string">
Workspace format version. New workspaces get the binary’s `SCHEMA_VERSION` (`0.2.0`). If absent, assumed `0.1.0` (legacy). Mismatch with the binary throws `SchemaVersionMismatchError` unless `LOOPANY_SKIP_VERSION_CHECK=1` or bootstrap `skipVersionCheck`.
</ParamField>

<ParamField body="enabled_domains" type="string[]">
Domain names whose `domains/<name>/kinds/` packs are merged into the kind registry at boot.
</ParamField>

## `references.jsonl`

Append-only graph log at the workspace root. One JSON object per line:

```json
{"ts":"2026-06-05T12:00:00.000Z","from":"<slug>","to":"<slug>","relation":"<verb>","actor":"cli"}
```

`relation` is an open registry (conventions in skills, not a closed enum). Reverse edges are built in memory at load time, not stored.

**Implicit edges** — At index build, `mentions` in frontmatter and `[[id]]` wiki links in the body synthesize `mentions` edges in memory only. They do not append rows to `references.jsonl`; updating the artifact updates the implicit graph on the next boot.

## `audit.jsonl`

Every CLI invocation appends one row after dispatch:

```json
{"ts":"...","op":"artifact.create","actor":"cli","duration_ms":42}
```

Optional `error` field when the command failed. Audit write failures are silent so they never break user-facing operations.

## `search.db` (optional)

| Property | Detail |
|----------|--------|
| Location | `$LOOPANY_HOME/search.db` |
| Engine | Bun SQLite |
| Source of truth | Markdown artifacts only |
| Population | `loopany reindex` (optional `--force`) |
| Missing DB | `loopany search` returns empty results and stderr hint to run reindex |

The index chunks artifact bodies/titles, uses FTS5 + optional embeddings fused by reciprocal rank fusion. Deleting `search.db` does not lose artifacts; rebuild with reindex.

## Engine composition

`bootstrap()` wires the workspace for command handlers:

| Module | Path / object | Responsibility |
|--------|---------------|----------------|
| `Config` | `config.yaml` | Schema version, enabled domains |
| `KindRegistry` | `kinds/` + domain packs | Parse kind defs, Zod validators, status machines |
| `ArtifactStore` | `artifacts/` | Create, read, list, append, set field/status |
| `ReferenceGraph` | `references.jsonl` | Append/load explicit edges |
| `ArtifactIndex` | In-memory | Built per command via `engine.index()` — filters, refs, implicit mentions |

Storage (flat artifact pool) and organization (`domains/` curated views) are separate: one artifact can be tagged with `domain` frontmatter and referenced from multiple domain scopes without duplicating files.

## v0.1 → v0.2 migration summary

When `config.yaml#schemaVersion` is older than the binary’s `SCHEMA_VERSION`, bootstrap refuses normal operations and points at the migration skill and `loopany migrate`.

| v0.1 | v0.2 |
|------|------|
| `tsk-20260427-103045` ids | `20260427-103045` slug ids |
| `artifacts/{YYYY-MM}/...` buckets | `artifacts/<dirName>/<slug>.md` |
| `check_at`, snake_case FM | `checkAt`, camelCase FM |
| No journal | Auto `journal/<YYYY>/<date>.md` + backfill |
| Kind knobs: `idPrefix`, `storage`, `bodyMode` | Dropped; optional `slugLayout` only |

Migration scripts rewrite files, emit `.migration-id-map.json`, rebuild `references.jsonl`, and set `schemaVersion: 0.2.0`.

<Steps>
<Step title="Detect mismatch">
Run any command; `SchemaVersionMismatchError` names workspace vs binary versions and the migrate subcommand.
</Step>
<Step title="Snapshot">
Git-commit or copy `$LOOPANY_HOME` before applying migration scripts (see migration skill pre-flight).
</Step>
<Step title="Migrate and verify">
`loopany migrate v0.1.0-to-v0.2.0` (or run scripted steps), then `loopany doctor` for schema version, artifact validation, and dangling refs.
</Step>
</Steps>

## Verification

```bash
loopany doctor
loopany kind list          # dirName + slugLayout per kind
loopany artifact list --kind task
```

Doctor checks: workspace presence, schema version, kind parse health, artifact frontmatter validity, reference endpoint integrity, onboarding (`self` person + active mission), and warnings for mission/domain coverage.

<Check>
Exit code `0` from `loopany doctor` means the on-disk workspace matches what the binary expects for v0.2 storage and graph integrity.
</Check>

## Related pages

<CardGroup>
<Card title="Workspace setup" href="/workspace-setup">
Run `loopany init`, verify bundled kinds, onboarding, and doctor.
</Card>
<Card title="Kinds and validation" href="/kinds-and-validation">
Kind markdown defs, Zod frontmatter, status machines, immutable write rules.
</Card>
<Card title="Reference graph" href="/reference-graph">
`references.jsonl`, implicit mentions, `refs` / `trace` queries.
</Card>
<Card title="Configuration reference" href="/configuration-reference">
`LOOPANY_HOME`, `LOOPANY_SKIP_VERSION_CHECK`, schema version guard.
</Card>
<Card title="Schema migration" href="/schema-migration">
v0.1.0→v0.2.0 scripts, id map, journal backfill.
</Card>
<Card title="Artifact commands" href="/artifact-commands">
`create` / `get` / `list`, `--slug`, append and status flows.
</Card>
</CardGroup>

---

## 05. Kinds and validation

> Markdown kind definitions, dynamic Zod frontmatter schemas, status machines, indexedFields, slug rules, and immutable write semantics (append, status flip, supersede).

- Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/05-kinds-and-validation.md
- Generated: 2026-06-05T18:58:01.888Z

### Source Files

- `src/core/kind-registry.ts`
- `skills/loopany-core/kinds/task.md`
- `skills/loopany-core/kinds/signal.md`
- `skills/loopany-core/kinds/note.md`
- `src/core/slug.ts`
- `src/commands/artifact-status.ts`
- `test/kind-registry.test.ts`

---
title: "Kinds and validation"
description: "Markdown kind definitions, dynamic Zod frontmatter schemas, status machines, indexedFields, slug rules, and immutable write semantics (append, status flip, supersede)."
---

Loopany treats **kind** as an open registry: each kind is a Markdown file under `$LOOPANY_HOME/kinds/` (plus optional domain packs). At bootstrap, `KindRegistry` parses those files into Zod frontmatter validators, optional status machines, storage layout (`dirName`, `slugLayout`), and `indexedFields` for queries. `ArtifactStore` applies that metadata on every create, append, field update, and status transition.

## Kind definition format

A kind file has two layers of frontmatter:

| Layer | Location | Purpose |
| --- | --- | --- |
| Top YAML | Between the first `---` pair | `kind`, optional `dirName`, `slugLayout`, `indexedFields` |
| Field spec | `## Frontmatter` → fenced `yaml` block | Per-field types, `required`, `default`, enum `values` |
| Status machine | `## Status machine` → fenced `yaml` (optional) | `initial` + `transitions` map |
| Agent guidance | `## Required sections`, `## UI`, playbook prose | Conventions for agents; not parsed by the runtime |

`parseKindDefinition` in `src/core/kind-registry.ts` splits the body on `##` headings, extracts YAML from fenced blocks, and builds a `KindDefinition`.

**Defaults when omitted:**

| Field | Default |
| --- | --- |
| `dirName` | `{kind}s` (e.g. `task` → `tasks`) |
| `slugLayout` | `flat` |
| `indexedFields` | `[]` |

**`slugLayout` values:**

| Value | On-disk path |
| --- | --- |
| `flat` | `artifacts/<dirName>/<id>.md` |
| `year` | `artifacts/<dirName>/<YYYY>/<id>.md` (year = first four characters of `id`; used by `journal`) |

Example top-level kind header (`skills/loopany-core/kinds/note.md`):

```yaml
---
kind: note
dirName: notes
indexedFields: [tags]
---
```

Bundled kinds ship from `skills/loopany-core/kinds/`; `loopany init` copies any missing `*.md` into `~/loopany/kinds/` without overwriting existing files.

## Loading and extending the registry

```text
bootstrap (src/core/engine.ts)
  ├─ KindRegistry.load($LOOPANY_HOME/kinds)
  └─ merge packDirs: domains/<enabled>/kinds/*.md
       └─ duplicate kind or dirName → LoadIssue (first wins)
```

<ParamField body="packDirs" type="string[]">
  One directory per enabled domain from `config.yaml` `enabled_domains`. Missing domain `kinds/` dirs are skipped.
</ParamField>

`loopany kind list` prints JSON: `kind`, `dirName`, `slugLayout`, `indexedFields`, `hasStatusMachine`.

<Warning>
Broken kind files do not crash bootstrap. They appear in `registry.issues` and fail the `kinds` check in `loopany doctor`.
</Warning>

## Dynamic Zod frontmatter schemas

`buildZodSchema` maps each `## Frontmatter` field spec to a Zod type:

| `type` in kind file | Zod | Notes |
| --- | --- | --- |
| `string` | `z.string()` | |
| `enum` | `z.enum([...])` | Requires non-empty `values` |
| `date` | `z.string()` | ISO date string; no deep date parsing |
| `bool` | `z.boolean()` | |
| `string[]` | `z.array(z.string())` | CLI `set` accepts JSON array or comma-separated |
| `number` | `z.number()` | |

Fields with `default` get `.default(...)`; fields without `required: true` are `.optional()`. The object schema uses `.passthrough()`, so extra frontmatter keys (for example `createdAt`, `updatedAt`, `_backfilled`) are allowed.

**Built-in fields** accepted on every kind without appearing in the kind spec: `domain`, `createdAt`, `updatedAt`, `_backfilled`.

Validation runs on:

- `ArtifactStore.create` — after auto-filling `status` from the machine `initial`
- `ArtifactStore.setField` — after coercion
- `loopany doctor` — `safeParse` over every artifact in the index

<RequestExample>

```bash
loopany artifact create --kind task --title "Fix cache" --status todo
```

</RequestExample>

Missing `title` on a required field or an invalid enum value throws a Zod error at create time.

## Status machines

Kinds with a `## Status machine` block get runtime enforcement in `ArtifactStore.setStatus`. The CLI routes status changes through `artifact status`, not `artifact set`.

```mermaid
stateDiagram-v2
  [*] --> todo: create (initial)
  todo --> running
  todo --> done
  todo --> cancelled
  running --> in_review
  running --> done
  running --> failed
  running --> cancelled
  in_review --> done
  in_review --> failed
  in_review --> cancelled
```

(Transitions match bundled `skills/loopany-core/kinds/task.md`.)

| Behavior | Implementation |
| --- | --- |
| Initial status on create | Set to `statusMachine.initial` when `status` omitted |
| Legal transition | `newStatus` must be in `transitions[current]` |
| Illegal transition | Error: `Illegal transition: <current> → <newStatus>` |
| Kinds without a machine | `setStatus` throws; use `setField` for frontmatter-only kinds like `note` |
| `--reason` on CLI | Accepted by `artifact status` but **not** written to the artifact body |

**Signal-specific rule:** transitioning to `addressed` requires `--addressed-by <id>`. The CLI appends a hard `addresses` edge from that artifact to the signal (`src/commands/artifact-status.ts`).

**Terminal detection:** `loopany followups` hides artifacts whose current `status` has no outgoing transitions in the kind machine (unless `--include-done true`).

Kinds without status machines include `note`, `person`, and `journal`.

## Required body sections (agent-enforced)

Many bundled kinds declare `## Required sections` (for example `task`: `## Outcome` before `done` or `failed`). **The TypeScript runtime does not parse or enforce these rules** — `setStatus` does not inspect the body. Compliance is expected from agents following kind playbooks and capture/reflect skills. `loopany doctor` validates frontmatter only, not body shape.

Recommended terminal flow for `task`:

```bash
loopany artifact append <id> --section Outcome --content "..."
loopany artifact status <id> done --reason "one-line summary"
```

## Slug rules (v0.2)

The artifact **id is the slug** — globally unique across all kinds, no kind prefix. Files live at `artifacts/<dirName>/[<YYYY>/]<id>.md`.

`validateSlug` / `requireValidSlug` (`src/core/slug.ts`):

| Rule | Constraint |
| --- | --- |
| Length | 1–60 Unicode codepoints |
| Charset | Letters (`\p{L}`), marks (`\p{M}`), digits (`\p{N}`), `-`, `_` |
| Edges | No leading/trailing `-` or `_` |
| Runs | No `--` or `__` |
| Normalization | NFC before store/compare |

**ID allocation order** (`ArtifactStore.allocateId`):

1. Explicit `--slug` (validated, must not exist globally)
2. `slugifyTitle(title)` — lowercased, punctuation collapsed to `-`, max 40 codepoints; collisions get `-2`, `-3`, …
3. `generateFallbackSlug()` — `YYYYMMDD-HHMMSS-<3hex>` UTC when title slugify fails or is absent

<Info>
Cross-kind uniqueness is enforced by scanning every registered kind directory for `<id>.md` at create time.
</Info>

`note` and `person` playbooks expect human-chosen slugs (`project-phoenix`, `alice-chen`) for stable `[[wiki-links]]`.

## indexedFields

Declared in the kind file top frontmatter (for example `task`: `[status, priority, checkAt]`). At index build time, each listed field value is indexed per kind; array values index each element separately (`src/core/index.ts`).

Used by:

- `loopany artifact list --kind <K> --where <field>=<value>` when `<field>` is in that kind’s `indexedFields`
- In-memory `ArtifactIndex.byField(kind, field, value)`

Global indexes (not per-kind declaration): `status`, `domain`, `checkAt` (followups), kind name.

<Note>
`checkAt` is indexed for tasks but followups scans all artifacts with a `checkAt` frontmatter value regardless of kind.
</Note>

## Immutable write semantics

Loopany does not expose “edit artifact body in place” or “replace frontmatter wholesale.” Mutations are narrow operations:

```text
                    ┌─────────────────────────────────────┐
                    │  artifacts/<dirName>/[<Y>/]<id>.md  │
                    └─────────────────────────────────────┘
         create ──►│ frontmatter + body (initial)         │
         append ──►│ body: new H2 or append under H2      │  appendSection
         setField ─►│ frontmatter: one non-status field    │  re-validate Zod
         setStatus ►│ frontmatter: status only             │  machine check
         supersede ►│ NEW artifact + supersedes edge       │  (pattern, not store API)
                    │ OLD artifact: status flip only       │
```

| Operation | API | What changes |
| --- | --- | --- |
| Append body | `artifact append <id> --section <S> --content …` | Adds or extends `## <S>`; preserves other sections |
| Flip status | `artifact status <id> <status>` | `status` + `updatedAt`; body unchanged |
| Set field | `artifact set <id> --<field> <value>` | Single frontmatter field; **blocks `status`** |
| Supersede belief/version | Create new artifact + `refs add --relation supersedes` + `status` on old | Old file kept; graph records replacement (`learning`, `mission`, `brief` playbooks) |

`--reason` on status is for CLI/audit context only; it does not append a `## Status` section (see `test/artifact-store.test.ts`).

**Journal exception:** `journal` is auto-written by the store on other creates; `loopany artifact create --kind journal` is rejected at the CLI. Auto-append only touches `## Activity` (or `## Backfilled` for `_backfilled` artifacts).

**Entity kinds:** `person` documents append-only body timeline; frontmatter `name` / `aliases` may change via `setField`.

## Core bundled kinds (reference)

| Kind | `dirName` | Status machine | Notable `indexedFields` |
| --- | --- | --- | --- |
| `task` | `tasks` | todo → … → terminal | `status`, `priority`, `checkAt` |
| `signal` | `signals` | open / addressed / dismissed | `status` |
| `note` | `notes` | none | `tags` |
| `learning` | `learnings` | active / superseded / archived | `domain`, `status`, `checkAt` |
| `skill-proposal` | `skill-proposals` | pending → accepted/rejected | `status`, `targetSkill`, `domain` |
| `mission` | `missions` | yes | (see `kinds/mission.md`) |
| `brief` | `briefs` | yes | (see `kinds/brief.md`) |
| `person` | `people` | none | `aliases` |
| `journal` | `journal` | none; `slugLayout: year` | `date` |

## When to add a kind vs use `note`

Bundled `note` documents a four-question test: add a new kind only if you need a state machine, canonical identity/dedup, structured indexed queries, or a fixed body shape downstream code depends on. Otherwise use `note` or a domain scope (`skills/loopany-core/kinds/note.md`).

Domain-specific kinds belong under `domains/<name>/kinds/` and load after workspace kinds; conflicting `kind` or `dirName` values register as load issues.

## Verification

<Steps>
<Step title="List registered kinds">

```bash
loopany kind list
```

Expect bundled kinds with `hasStatusMachine` where applicable.

</Step>
<Step title="Run doctor">

```bash
loopany doctor
```

`kinds` and `artifacts` checks should pass on a healthy workspace.

</Step>
<Step title="Exercise validation">

```bash
loopany artifact create --kind task --title "Demo" --status bogus
```

Expect Zod/enum failure. Then create with `todo`, append `Outcome`, and `artifact status <id> running`.

</Step>
</Steps>

## Related pages

<CardGroup>
<Card title="Artifacts and workspace" href="/artifacts-and-workspace">
  v0.2 paths, `references.jsonl`, and slug-as-id storage layout.
</Card>
<Card title="Artifact commands" href="/artifact-commands">
  create, append, status, set flags and per-kind CLI fields.
</Card>
<Card title="Domains" href="/domains">
  Domain-scoped kind packs and `enabled_domains`.
</Card>
<Card title="Self-improvement loop" href="/self-improvement-loop">
  `## Outcome`, learnings, skill-proposals, and supersede flow.
</Card>
<Card title="Doctor and troubleshooting" href="/doctor-and-troubleshooting">
  Kind load failures, Zod errors, and recovery.
</Card>
</CardGroup>

---

## 06. Reference graph

> Append-only references.jsonl edges, implicit mentions from frontmatter, canonical relation verbs, refs/trace queries, and wiki-link [[id]] conventions.

- Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/06-reference-graph.md
- Generated: 2026-06-05T18:58:21.309Z

### Source Files

- `src/core/references.ts`
- `src/commands/refs.ts`
- `src/commands/trace.ts`
- `skills/loopany-core/conventions/relations.md`
- `src/core/link-parser.ts`
- `test/references.test.ts`

---
title: "Reference graph"
description: "Append-only references.jsonl edges, implicit mentions from frontmatter, canonical relation verbs, refs/trace queries, and wiki-link [[id]] conventions."
---

The loopany reference graph lives at `$LOOPANY_HOME/references.jsonl` as an append-only edge log. At index build time, `ArtifactIndex` loads those rows, synthesizes `mentions` edges from frontmatter and body `[[slug]]` links, and exposes the merged graph through `loopany refs` (BFS neighborhood) and `loopany trace` (signed causal walk). Reverse adjacency is computed in memory; only forward rows are persisted.

## Storage and index model

```text
$LOOPANY_HOME/
  references.jsonl     # explicit edges only (append-only)
  artifacts/...        # implicit mention sources (frontmatter + body)

Index build (each CLI command that calls engine.index()):
  1. ReferenceGraph.load() → forward + reverse maps from JSONL
  2. For each artifact:
       - frontmatter mentions: [...]  → implicit mentions edge
       - body [[slug]] via extractLinks → implicit mentions edge
  3. ArtifactIndex.refsOut / refsIn query the merged maps
```

<Note>
`relation` is an open registry in code — any string is accepted on `refs add`. Searchability depends on using the six convention verbs documented in `skills/loopany-core/conventions/relations.md`; synonyms fragment `refs` and `trace` results.
</Note>

### Explicit edge row shape

Each `refs add` or runtime append writes one JSON object per line:

| Field | Type | Meaning |
|-------|------|---------|
| `ts` | ISO-8601 string | Set automatically on append |
| `from` | artifact id (slug) | Source node |
| `to` | artifact id (slug) | Target node |
| `relation` | string | Verb (convention: see table below) |
| `actor` | string | Who wrote the edge (`cli`, `agent`, …) |

`ReferenceGraph.load()` skips blank lines and malformed JSON; rows missing `from`, `to`, or `relation` are ignored. Reverse edges are **not** stored — `ArtifactIndex` mirrors each row into both `forwardRefs` and `reverseRefs`.

### Implicit `mentions` edges

Implicit edges are **not** written to `references.jsonl`. They are rebuilt on every index from artifact content:

| Source | `actor` | `relation` | `ts` |
|--------|---------|------------|------|
| `mentions: [id, …]` in frontmatter | `frontmatter` | `mentions` | source file mtime |
| `[[slug]]` in body (outside code) | `body` | `mentions` | source file mtime |

All implicit edges carry `"implicit": true`. Editing frontmatter or body updates the graph on the next index without a `refs add` call. Query APIs return implicit and explicit edges together; filter by `implicit` or `actor` in JSON output when you need to distinguish them.

## Canonical relation verbs

Direction is always **A → verb → B** (active voice from `from`). To traverse the inverse, use `loopany refs <B> --direction in --relation <verb>` instead of writing a second edge.

| Verb | `from` → `to` reads as | Use when |
|------|------------------------|----------|
| `led-to` | cause → effect | Downstream artifact produced from upstream (default causal link) |
| `addresses` | action → concern | Task/signal pattern: handler resolves the observation |
| `mentions` | source → entity | Soft reference (frontmatter, `[[id]]`, or explicit add) |
| `supersedes` | new → old | Replacement artifact after mission/architecture shift |
| `follows-up` | continuation → original | Thread picked up later (often paired with `checkAt` tasks) |
| `cites` | summary → evidence | Brief or learning drawing on source artifacts |

<Warning>
Do not store both directions for the same fact (for example `led-to` plus `caused-by`). `trace` defaults exclude `caused-by`; use `refs --direction in` on the effect node instead.
</Warning>

### `addresses` via signal status

Closing a signal with responsibility emits a persisted `addresses` edge in one step:

```bash
loopany artifact status <signal-slug> addressed --addressed-by <task-slug>
```

The edge is `from: <task-slug>, to: <signal-slug>, relation: addresses` (action handles observation). `--addressed-by` is required for `addressed` and rejected for other statuses.

## Wiki-link `[[id]]` conventions

Body wiki links are parsed by `extractLinks()`:

- Pattern: `[[<slug>]]` where `<slug>` matches `^[\p{L}\p{M}\p{N}\-_]+

  
    
    
    loopany Documentation · Grok Docs
    
    
    
    
    
    
    
    
    
    
  
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
  
  
    
(Unicode letters, marks, numbers, hyphen, underscore). - No internal whitespace; characters like `.`, `/`, `:` are rejected. - Fenced code blocks and inline `` `...` `` spans are stripped before matching so examples do not create edges. - Every resolved link becomes an implicit `mentions` edge; **`[[id]]` cannot express `led-to`, `addresses`, or other verbs** — use `loopany refs add` for those. Validation is deferred to index time: slug-shaped links to missing artifacts still appear as edges whose `to` id is unknown. `loopany doctor` reports them as dangling (`from → to (relation)`). <Info> Skill cross-references use `[[path/to/SKILL.md]]` in skill bodies. Those paths are **not** ingested into `references.jsonl` or the artifact graph; agents follow them manually. Artifact wiki links always target artifact slugs/ids, not skill file paths. </Info> ## CLI: `refs` ### Add an explicit edge ```bash loopany refs add --from <id> --to <id> --relation <verb> ``` <ResponseExample> ```json { "ts": "2026-06-05T12:00:00.000Z", "from": "morning-signal", "to": "fix-cache-task", "relation": "led-to", "actor": "cli" } ``` </ResponseExample> `refs add` is recorded in `audit.jsonl` as `refs.add`. ### Query neighborhood (BFS) ```bash loopany refs <id> [--direction in|out|both] [--relation <verb>] [--depth <N>] [--domain <name>] ``` <ParamField body="direction" type="string" default="out"> `out` — edges where `<id>` is `from`. `in` — edges where `<id>` is `to`. `both` — union of outgoing and incoming at each hop. </ParamField> <ParamField body="depth" type="positive integer" default="1"> Maximum hop count. Depth 1 is one-hop neighborhood; larger values walk chains but dedupe edges by `from|to|relation|ts`. </ParamField> <ParamField body="domain" type="string"> When set, keeps only edges where **both** endpoints have `frontmatter.domain` equal to the flag value. </ParamField> Returns a JSON array of edge objects (implicit and explicit). Visited-node deduplication prevents re-expanding a node, but all unique edges encountered within the depth budget are included. ## CLI: `trace` `trace` walks **lineage predicates** to a fixed point, separate from the shallow `refs` neighborhood: ```bash loopany trace <id> [--direction forward|backward|both] [--relations <csv>] [--max-depth <N>] ``` Default `--relations`: `led-to`, `addresses`, `supersedes`, `follows-up`, `cites`. **`mentions` is excluded** (soft pointer, not causal lineage). Override with `--relations mentions` when you need mention chains. <ResponseExample> ```json { "root": "fix-cache-task", "nodes": [ { "id": "morning-signal", "kind": "signal", "distance": -1, "...": "..." }, { "id": "fix-cache-task", "kind": "task", "distance": 0, "...": "..." }, { "id": "cache-learning", "kind": "learning", "distance": 1, "...": "..." } ], "edges": [ "...Edge objects..." ] } ``` </ResponseExample> | Field | Meaning | |-------|---------| | `distance` | Signed hop count from root: negative = backward (causes), `0` = root, positive = forward (effects) | | `nodes` | Sorted by `distance`, then `id`; dangling edge targets omitted | | `edges` | All edges traversed, deduped by `from|to|relation|ts` | `--direction forward` follows `refsOut`; `backward` follows `refsIn`. Default `both` walks both sides. Unknown root id fails with `No artifact with id: <id>`. Cycles terminate without duplicating nodes (first-seen distance wins). ## Explicit vs implicit: when to use which ```mermaid flowchart LR subgraph persist["references.jsonl"] R1["refs add"] R2["artifact status --addressed-by"] end subgraph artifact["Artifact files"] F1["mentions: [...]"] F2["body [[slug]]"] end subgraph index["ArtifactIndex.build"] M["Merged forward/reverse maps"] end R1 --> persist R2 --> persist F1 --> index F2 --> index persist --> index index --> M M --> Q1["loopany refs"] M --> Q2["loopany trace"] ``` | Mechanism | Persisted | Best for | |-----------|-----------|----------| | `refs add --relation <verb>` | Yes | `led-to`, `addresses`, `supersedes`, `follows-up`, `cites` | | `mentions:` frontmatter | No (implicit) | Structural attribution (mission, stakeholders) | | `[[slug]]` in body | No (implicit) | In-prose name-drops | | `artifact status … addressed --addressed-by` | Yes (`addresses`) | Signal-close workflow | Explicit and implicit edges can coexist on the same pair with different relations (for example implicit `mentions` plus explicit `led-to` from the same task). ## Integrity and operations `loopany doctor` checks reference integrity: every edge’s `to` (and orphan `from`) must resolve to an existing artifact in the index. Unresolved `[[typo-slug]]` targets count as dangling. Mission coverage warns when tasks lack a mission in `mentions`, but that is separate from the references check. v0.1 → v0.2 migrations can rewrite `references.jsonl` ids to slug form (`skills/migrations/v0.1.0-to-v0.2.0/scripts/03-rebuild-references.ts` backs up the prior file). After migration, rebuild the index by running any command that loads the workspace. The read-only **factory** UI (`loopany factory`) visualizes the same merged graph, rendering implicit edges with lower opacity than explicit `references.jsonl` rows. ## Anti-patterns - **Duplicate direction edges** — store one canonical direction; query the inverse with `--direction in`. - **Synonym verbs** (`triggered`, `resolves`, `references`, …) — use the six canonical verbs or extend `relations.md` deliberately. - **`led-to` for everything** — prefer `addresses` for responsibility, `mentions` for referential links. - **`[[id]]` for causal links** — always `refs add` with the intended verb. ## Related pages <CardGroup> <Card title="Artifacts and workspace" href="/artifacts-and-workspace"> On-disk layout for artifacts, references.jsonl, audit.jsonl, and v0.2 slug-as-id rules. </Card> <Card title="Graph, search, and scheduling" href="/graph-search-commands"> Full refs/trace/followups command flags, factory UI, and audit side effects. </Card> <Card title="Artifact lifecycle example" href="/artifact-lifecycle-example"> End-to-end signal → task with led-to/addresses edges and status transitions. </Card> <Card title="Doctor and troubleshooting" href="/doctor-and-troubleshooting"> Dangling-edge detection, reference integrity checks, and recovery steps. </Card> </CardGroup> --- ## 07. Domains > Scope-local packs under domains/<name>/, enabled_domains in config.yaml, domain-scoped kind overrides, and when to propose a domain vs a note. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/07-domains.md - Generated: 2026-06-05T18:58:13.529Z ### Source Files - `src/core/config.ts` - `src/commands/domain.ts` - `src/core/engine.ts` - `src/core/kind-registry.ts` - `skills/loopany-core/conventions/taxonomy.md` - `CLAUDE.md` --- title: "Domains" description: "Scope-local packs under domains/<name>/, enabled_domains in config.yaml, domain-scoped kind overrides, and when to propose a domain vs a note." --- Domains in loopany combine two mechanisms: a **scope tag** on artifact frontmatter (`domain: <name>`) and optional **domain packs** under `$LOOPANY_HOME/domains/<name>/kinds/` whose kind definitions load only when the name appears in `config.yaml` → `enabled_domains`. Artifacts always live in the global `artifacts/` pool; enabling a domain changes validation and `loopany kind list`, not where files are stored. ## Two layers: tag vs pack | Layer | What it is | Runtime effect | |-------|------------|------------------| | **Domain tag** | Optional `domain` frontmatter on any artifact (built-in field, not per-kind schema) | Indexed for `artifact list --domain`, `followups --domain`, `refs --domain`, `search --domain`; surfaced in factory graph payload | | **Domain pack** | Directory `$LOOPANY_HOME/domains/<name>/kinds/*.md` | Extra kind definitions merged into `KindRegistry` when `<name>` is enabled | Storage stays flat under `artifacts/<dirName>/<id>.md`. The product doc in `CLAUDE.md` also describes `manifest.yaml`, `settings.yaml`, `view.json`, and `daily/` under each domain — those paths are **design targets**, not loaded by the current CLI. Today only `domains/<name>/kinds/` is wired in `bootstrap()`. ```mermaid flowchart TB subgraph workspace["$LOOPANY_HOME"] config["config.yaml\nenabled_domains"] globalKinds["kinds/*.md\ncore kinds"] artifacts["artifacts/**\nflat pool + domain tag"] pack["domains/{name}/kinds/*.md\ndomain kinds"] end bootstrap["bootstrap() in engine.ts"] registry["KindRegistry"] config --> bootstrap globalKinds --> bootstrap pack -->|"only if name ∈ enabled_domains"| bootstrap bootstrap --> registry registry --> artifacts ``` ## Workspace layout After `loopany init`, the workspace has `kinds/`, `artifacts/`, and `config.yaml`. Init does **not** create `domains/`; you add packs manually when a scope matures. :::files $LOOPANY_HOME/ config.yaml # schemaVersion, enabled_domains kinds/ # core kinds (always loaded) artifacts/ # all artifacts; optional domain: in frontmatter domains/ crm/ kinds/ deal.md # example domain-only kind ::: <Info> One artifact can carry a `domain` tag without any pack directory existing. Conversely, you can enable a domain name before writing `domains/<name>/kinds/` — missing pack dirs are skipped silently at load time. </Info> ## Configuration: `enabled_domains` `Config` reads and writes `$LOOPANY_HOME/config.yaml`. The only domain-related key today is `enabled_domains` (YAML array of strings, sorted on write). <ParamField body="enabled_domains" type="string[]"> Names of domain packs whose `kinds/` directories are merged at engine startup. Defaults to `[]` when absent. </ParamField> Fresh workspaces get `schemaVersion` only; domains start disabled: ```yaml # loopany init default schemaVersion: "0.2.0" ``` Example after enabling CRM and ads: ```yaml schemaVersion: "0.2.0" enabled_domains: - ads - crm ``` `enableDomain` / `disableDomain` are idempotent and persist immediately. There is **no** validation that `domains/<name>/` exists — enabling is purely a config flag until the next command bootstraps the registry. ## How domain packs load kinds On every command, `bootstrap()` builds pack paths from enabled names and passes them to `KindRegistry.load()`: 1. Load all `kinds/*.md` under the workspace root (core kinds from init). 2. For each entry in `enabled_domains`, load `domains/<name>/kinds/*.md` if that directory exists. 3. Register each kind once; duplicates (same `kind` or `dirName` from a second file) become registry **issues**, not overrides. Global kinds win precedence in practice: they load first, so a domain file reusing an existing `kind` name is rejected with a duplicate-kind issue. <Warning> Disabling a domain removes its kinds from the registry on the next CLI invocation. Existing artifact **files** remain on disk, but operations that need a registered kind (for example `artifact create --kind deal`) fail until you re-enable. `loopany doctor` may warn when artifacts reference kinds that are no longer loaded. </Warning> ### Example domain kind (`deal`) E2E tests scaffold a minimal CRM pack: ```markdown --- kind: deal dirName: deals indexedFields: [status] --- ## Frontmatter ```yaml title: { type: string, required: true } status: { type: enum, values: [open, won, lost] } ``` ``` Workflow: <Steps> <Step title="Write the pack"> Create `$LOOPANY_HOME/domains/crm/kinds/deal.md` (and any sibling kind files). </Step> <Step title="Enable the domain"> ```bash loopany domain enable crm ``` </Step> <Step title="Verify kinds"> ```bash loopany kind list ``` `deal` appears only after enable. </Step> <Step title="Create scoped artifacts"> ```bash loopany artifact create --kind deal --slug acme-q2 --title "Acme Q2" --status open --domain crm ``` </Step> </Steps> ## Domain tag on artifacts `domain` is a **built-in** store field accepted on every kind without appearing in each kind’s Frontmatter schema. Set at create time with `--domain`, or later with `artifact set <id> --field domain --value <name>`. Core kinds such as `task`, `signal`, `brief`, `mission`, `learning`, `note`, and `person` also declare optional `domain` in their kind specs and often list `domain` in `indexedFields` where filtering matters. The tag is organizational metadata: - Does **not** move files into `domains/` - Does **not** auto-enable a domain pack - Can differ from pack name in theory, but convention is to align tag and pack (e.g. both `crm`) ## CLI commands | Command | JSON shape | Behavior | |---------|------------|----------| | `loopany domain list` | `{ enabled, observed_only }` | `enabled` from config; `observed_only` = domain values seen on artifacts but not enabled | | `loopany domain enable <name>` | `{ enabled }` | Appends name to `enabled_domains`, sorted | | `loopany domain disable <name>` | `{ enabled }` | Removes name; no error if missing | <RequestExample> ```bash loopany domain list ``` </RequestExample> <ResponseExample> ```json { "enabled": ["crm"], "observed_only": ["ads"] } ``` </ResponseExample> `observed_only` helps you spot informal tagging before you enable packs — for example five tasks already carry `domain: sales` but `sales` is not in `enabled_domains`. ## Queries and filters Commands that accept `--domain <name>` filter on the artifact frontmatter tag: | Command | Filter semantics | |---------|------------------| | `artifact list --domain D` | Index `byDomain(D)` | | `followups --due … --domain D` | Due items where `frontmatter.domain === D` | | `search <query> --domain D` | Search index `domain` column | | `refs <id> --domain D` | Keeps edges where **both** endpoints have `domain: D` | `refs --domain` is stricter than list/search: cross-domain edges are dropped unless both artifacts share the tag. ## Doctor: domain coverage `loopany doctor` includes a **domain coverage** check (warning, not fail). Any artifact with a `domain` string not present in `enabled_domains` is listed, e.g. `my-task: domain "unknown-d" not in enabled_domains`. <Tip> After you start tagging artifacts informally, run `domain list` and `doctor` together: enable packs you are ready to support, or remove stray tags. </Tip> ## Factory UI `loopany factory` builds a graph payload with: - `domains` — distinct non-null `domain` values on nodes - `enabledDomains` — copy of `config.enabled_domains` The UI uses these for labeling; it does not read `domains/*/settings.yaml`. ## When to propose a domain vs a note Default decision path lives in `skills/loopany-core/conventions/taxonomy.md` (skill: **new-concept**). Short version: **Write a `note` first** when “I want to track X” and no existing kind fits — unless structure pays back (state machine, dedup identity, typed queries, fixed body sections). **Propose a domain** only when usage shows a **separable scope** — a vertical (sales, ads), a rhythm (weekly review), or a workflow that wants its own kinds/settings/skills, and that does not belong in the global pool. Practical signals from taxonomy: - ≥ **5** artifacts already use the same informal `domain: <name>`, **or** - A new kind would **only** make sense inside one scope **Prefer domain over orphan domain kind:** if torn between “new kind globally” and “new domain that owns the kind”, choose the domain so vertical kinds stay in `domains/<name>/kinds/`. | Situation | Recommendation | |-----------|----------------| | One-off preference (“client likes async standups”) | `note` | | Published entity with identity + metrics (`post`, `customer`) | Domain kind inside e.g. `content` or `crm` | | Cadence/workstream (“weekly investor update”) | Domain reusing core `brief` + `task`, not a new core kind | | Vertical entity (`deal`, `order`) | `kind` in domain pack, not core `kinds/` | Domain scaffolding after user agreement is still maturing in skills: surface the proposal, then create `domains/<name>/kinds/` and run `domain enable`. ### Anti-patterns - Proposing a **core** kind for verticals (`deal`, `recipe`, `campaign`) — belongs in a domain pack. - Creating a **domain** when an extra field on a `note` suffices. - Using **`task`** to hold entities (tweets, customers) — use `note` or a domain kind; link with graph relations. ## Core kinds vs domain kinds | | Core kinds (`kinds/`) | Domain kinds (`domains/<n>/kinds/`) | |--|----------------------|-------------------------------------| | Shipped | Bundled at `loopany init` from `skills/loopany-core/kinds` | Never pre-shipped; user/agent authored | | Load rule | Always | Only when domain enabled | | Purpose | Agent lifecycle slots (`task`, `signal`, `mission`, …) | Vertical entities (`deal`, `contact`, …) | Promotion flow for a new domain kind mirrors global kinds: author `domains/<name>/kinds/<kind>.md`, enable domain, use `loopany artifact create --kind <kind> …`. Global kind proposal via `loopany kind propose` applies to workspace `kinds/`, not domain paths. ## Mental model: storage vs organization ```text artifacts/ ← canonical pool (immutable write rules) domains/<name>/ ← scope-local kind definitions (+ planned config) config.yaml ← which packs are active frontmatter.domain ← lightweight scope label on any artifact ``` An artifact can be tagged `domain: crm` while also referenced from graph edges to mission-wide artifacts with no domain tag. Multiple domains do not duplicate storage — they filter views and extend validation. <Note> `src/core/domain-loader.ts` is listed in project docs as a future module; loading is inline in `engine.ts` today. Do not expect manifest/settings files to affect CLI behavior until a later release wires them. </Note> ## Verification checklist <Steps> <Step title="List domains"> `loopany domain list` — confirm `enabled` and any `observed_only` names. </Step> <Step title="Enable and list kinds"> `loopany domain enable <name>` then `loopany kind list` — pack kinds appear. </Step> <Step title="Create tagged artifact"> `loopany artifact create --kind task --title "..." --status todo --domain <name>` then `artifact list --domain <name>`. </Step> <Step title="Doctor"> `loopany doctor` — domain coverage should be `ok` when every used tag is enabled. </Step> </Steps> ## Related pages <CardGroup> <Card title="Kinds and validation" href="/kinds-and-validation"> Markdown kind definitions, dynamic schemas, and immutable artifact writes — including how domain packs register kinds. </Card> <Card title="Configuration reference" href="/configuration-reference"> `config.yaml` keys, `LOOPANY_HOME`, and schema version guards. </Card> <Card title="Artifact commands" href="/artifact-commands"> `--domain` on create, `artifact set`, and list filters. </Card> <Card title="CLI reference" href="/cli-reference"> Full `loopany domain` subcommand surface and JSON stdout conventions. </Card> <Card title="Doctor and troubleshooting" href="/doctor-and-troubleshooting"> Domain coverage warnings and disabled-pack edge cases. </Card> </CardGroup> --- ## 08. Skills library > Agent-readable SKILL.md packs (resolver, core, capture, reflect, review), routing table, cross-skill chaining, and harness-neutral memory injection. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/08-skills-library.md - Generated: 2026-06-05T18:58:48.693Z ### Source Files - `skills/loopany-resolver/SKILL.md` - `skills/loopany-core/SKILL.md` - `skills/loopany-capture/SKILL.md` - `skills/loopany-reflect/SKILL.md` - `skills/loopany-review/SKILL.md` - `injections/resolver-memory.md` - `INSTALL_FOR_AGENTS.md` --- title: "Skills library" description: "Agent-readable SKILL.md packs (resolver, core, capture, reflect, review), routing table, cross-skill chaining, and harness-neutral memory injection." --- The loopany CLI does not load or execute skills — judgment lives in markdown under `skills/` in the cloned repo (conventionally `~/loopany-src/skills/`), while `loopany init` copies only kind definitions from `skills/loopany-core/kinds/` into `$LOOPANY_HOME/kinds/`. Agents read `loopany-resolver` first, follow its routing table to the right pack, then call deterministic `loopany` commands documented in each skill. ## Architecture ```mermaid flowchart TB subgraph harness["Agent harness"] MEM["Host memory file<br/>CLAUDE.md / AGENTS.md / /memory add"] AGT["Agent session"] end subgraph repo["~/loopany-src (repo)"] INJ["injections/resolver-memory.md"] RES["skills/loopany-resolver/SKILL.md"] CORE["skills/loopany-core/"] CAP["skills/loopany-capture/"] REF["skills/loopany-reflect/"] REV["skills/loopany-review/"] MIG["skills/migrations/"] end subgraph workspace["$LOOPANY_HOME (workspace)"] KINDS["kinds/*.md"] ART["artifacts/"] REFS["references.jsonl"] end subgraph cli["loopany CLI"] OPS["artifact / refs / trace / followups / doctor"] end INJ --> MEM MEM --> AGT AGT -->|"Read at task end"| RES RES --> CORE & CAP & REF & REV CORE --> OPS CAP --> OPS REF --> OPS REV --> OPS init["loopany init"] --> KINDS OPS --> ART & REFS ``` <Note> Skills are **provider- and harness-neutral**: any agent that can read files from disk (or a catalog mirror of the repo) can follow the same packs. No API keys, hosted skill registry, or model-specific runtime is required — only a stable path to `loopany-resolver/SKILL.md` and the injection snippet. </Note> ## Pack inventory | Pack | Path | Role | |------|------|------| | **Resolver** | `skills/loopany-resolver/SKILL.md` | First read on any loopany interaction; bootstrap gate + routing table + chaining rules | | **Core** | `skills/loopany-core/SKILL.md` | Artifact CRUD routing; kind playbooks under `kinds/`; conventions under `conventions/` | | **Capture** | `skills/loopany-capture/SKILL.md` | Post-work capture: event→kind routing, quality gate, subagent dispatch | | **Reflect** | `skills/loopany-reflect/SKILL.md` | Pattern discovery → `learning` / `skill-proposal`; accept/reject apply flow | | **Review** | `skills/loopany-review/SKILL.md` | Parameterized daily / weekly / monthly reviews; frequency detail in `references/` | | **Migrations** | `skills/migrations/v*-to-v*/SKILL.md` | Agent-executable schema migration narratives + standalone `scripts/` | Each operational pack uses Anthropic Agent Skills frontmatter (`name`, `description` with explicit triggers). The runtime never parses these files; they are instructions for the agent only. ## Bootstrap gate Before any skill-specific work, the resolver requires an active mission: ```bash loopany artifact list --kind mission --status active ``` No active mission → run `ONBOARDING.md` and **stop**. No capture, reflect, or review skill fires without a mission-backed workspace. ## Resolver routing table Match the trigger, read the target `SKILL.md`, then act. When two rows match, read both — they chain. | Trigger | Target pack | Read | |---------|-------------|------| | Any artifact CRUD (`create` / `get` / `list` / `append` / `status` / `set`) | core | `skills/loopany-core/SKILL.md` | | Substantive work just ended (PR shipped, incident resolved, decision made) | capture | `skills/loopany-capture/SKILL.md` | | `reflect` / `what have we learned` / ≥3 tasks done / writing `learning` or `skill-proposal` | reflect | `skills/loopany-reflect/SKILL.md` | | `accept <proposal-slug>` / `reject <proposal-slug>` / review proposals | reflect | `skills/loopany-reflect/SKILL.md` | | `what's due today` / daily check-in / session start | review (daily) | `skills/loopany-review/SKILL.md` + `references/daily.md` | | `what's slipping` / weekly check / workspace health | review (weekly) | `skills/loopany-review/SKILL.md` + `references/weekly.md` | | Mission drift / structural questions / monthly cadence | review (monthly) | `skills/loopany-review/SKILL.md` + `references/monthly.md` | | Choosing a relation verb / `loopany refs add` | core conventions | `skills/loopany-core/conventions/relations.md` | | Note vs kind vs domain decision | core conventions | `skills/loopany-core/conventions/taxonomy.md` | ### Disambiguation rules - **Most specific wins** — e.g. `create a task` → core, not capture. - **Conventions stack** — core does not exempt `refs add` from the six canonical relation verbs. - **When in doubt, ask** before committing an artifact. ## Core kind routing After loading core, route to the kind playbook under `$LOOPANY_HOME/kinds/` (copied from `skills/loopany-core/kinds/` at init). Read the kind file — schema **and** `§ Playbook` — before create or modify. | Trigger | Kind playbook | |---------|---------------| | Committing to work, `[change]` / `[incident]` | `kinds/task.md` | | Writing `## Outcome`, flipping task status | `kinds/task.md § Playbook` | | Noticed something, can't act now | `kinds/signal.md` | | Dismissing / upgrading a signal | `kinds/signal.md § Playbook` | | Writing a learning | `kinds/learning.md` | | Writing a skill-proposal | `kinds/skill-proposal.md` | | Mission create/update | `kinds/mission.md` | | Brief / person / journal | respective kind file | | Anything else | `kinds/note.md` | Pass `--slug` whenever the artifact will be cited in `[[wiki-links]]` or graph edges. Omit only for batch ingestion. ## Cross-skill chaining Some workflows intentionally span packs in sequence: | Chain | Flow | |-------|------| | **Capture → Core** | Capture picks the kind; core's playbook governs creation | | **Capture → Reflect** | After ≥3 captures in a session, suggest reflect | | **Reflect → Core** | Reflect writes learnings/proposals via core playbooks | | **Review → Core** | Review dispatches each surfaced item to its kind playbook | | **Review → Reflect** | Weekly review with ≥3 resolutions → suggest reflect | ```text Session end ──► resolver-memory (inject) ──► resolver ──► capture? │ ┌─────────────────────────┼─────────────────────────┐ ▼ ▼ ▼ core reflect review │ │ │ └──────────── loopany CLI (artifacts + graph) ──────┘ ``` Skill-to-skill references in markdown bodies use `[[<path>/SKILL.md]]` (path relative to the linking file, always ending in `SKILL.md`). Bare `[[slug]]` in artifact bodies is reserved for artifact IDs and becomes `mentions` edges — not skill links. ## Harness-neutral memory injection The CLI has no skill loader. Persistent trigger discipline comes from appending one block into the host's memory primitive. <Steps> <Step title="Locate the canonical snippet"> `injections/resolver-memory.md` — instructs the agent to **Read** `~/loopany-src/skills/loopany-resolver/SKILL.md` at the end of every user-requested task, before the final reply. </Step> <Step title="Append idempotently (coding CLIs)"> <CodeGroup> ```bash title="Claude Code" grep -q "loopany skill resolver" ~/.claude/CLAUDE.md 2>/dev/null || \ cat ~/loopany-src/injections/resolver-memory.md >> ~/.claude/CLAUDE.md ``` ```bash title="Codex / AGENTS.md" TARGET="$HOME/AGENTS.md" grep -q "loopany skill resolver" "$TARGET" 2>/dev/null || \ cat ~/loopany-src/injections/resolver-memory.md >> "$TARGET" ``` </CodeGroup> </Step> <Step title="Register on agent platforms"> For hosts with a `/memory add` primitive, register the same file contents (collapse newlines if the platform rejects multiline values). </Step> <Step title="Verify without self-query"> ```bash grep -l "loopany skill resolver" ~/.claude/CLAUDE.md ~/AGENTS.md 2>/dev/null test -f ~/loopany-src/skills/loopany-resolver/SKILL.md && echo "RESOLVER present" ``` Start a **fresh** session and ask where the resolver lives. A correct answer without file search means injection worked. </Step> </Steps> <Info> `git pull` on `~/loopany-src` updates skill **content** without re-running injection — the memory block only holds a pointer to the resolver path. A future `loopany doctor` check for resolver registration is planned but not implemented yet. </Info> ## Cadence and recurring packs | Cadence | Pack | Typical trigger | |---------|------|-------------------| | Daily | `loopany-review` | `loopany followups --due today`; session start | | Weekly | `loopany-review` | overdue sweep + `loopany doctor` + parking lots | | Weekly | `loopany-reflect` | fresh outcomes; pattern thresholds | | Monthly | `loopany-review` | mission alignment + structural drift | On coding CLIs (Claude Code, Codex), durable platform cron is unreliable — default to **session-boundary** prompts instead of silent cron registration. Agent platforms with durable cron may register jobs silently; record job IDs for re-install audits. See `INSTALL_FOR_AGENTS.md` Step 4 for the full decision tree. ## Skill evolution (never direct edits) Agents **must not** edit files under `skills/` except through an accepted `skill-proposal` artifact: 1. Reflect writes `learning` (+ optional `skill-proposal` citing evidence). 2. User accepts or rejects via reflect's proposal-apply flow. 3. On accept: apply only the described change, append `## Outcome` to the proposal, `loopany artifact status … accepted`, git-commit target + proposal together. 4. `changeType: add` requires `## Skill draft` and `## Resolver entry` so new packs are reachable from the routing table. Rejected proposals record reasons in `## Outcome` so reflect does not re-suggest the same rule. ## Conventions sub-packs | File | Use when | |------|----------| | `conventions/relations.md` | `loopany refs add --relation` — six canonical verbs: `led-to`, `addresses`, `mentions`, `supersedes`, `follows-up`, `cites` | | `conventions/taxonomy.md` | Choosing `note` vs new kind vs new domain (default: `note`) | `relation` remains an open registry in code, but synonyms fragment `refs` / `trace` queries — conventions live in skills, not TypeScript (`src/core/references.ts`). ## Migrations as skills Schema jumps ship under `skills/migrations/v<from>-to-v<to>/` with a readable `SKILL.md` and ordered `scripts/*.ts`. `loopany migrate` discovers migrations; the **agent** reads the skill and runs scripts — the CLI does not execute them. This keeps transforms separable from the thin harness. ## Verification | Check | Command / artifact | |-------|-------------------| | Resolver on disk | `test -f ~/loopany-src/skills/loopany-resolver/SKILL.md` | | Injection landed | `grep -l "loopany skill resolver" ~/.claude/CLAUDE.md …` | | Workspace health | `loopany doctor` | | End-to-end skill behavior | `./test/skill-regression.sh` (symlinks core/reflect/review/capture into a temp Claude skills dir; 10 scenarios via `claude -p`) | <Warning> Skill regression requires the `claude` CLI, an API key, and ~5 minutes for a full pass. It validates artifact routing and capture/review/reflect behavior — not resolver injection into user memory files. </Warning> ## Anti-patterns (resolver-level) - Acting without reading the resolver routing table first - Skipping a pack's anti-patterns section (failure modes live there) - Trusting session memory over re-reading skills (skills change via proposals) - Writing artifacts without reading the target kind playbook - Exposing loopany vocabulary (`kind`, `slug`, CLI flags) to end users during normal conversation — translate in user-facing text per `INSTALL_FOR_AGENTS.md` ## Related pages <CardGroup> <Card title="Installation" href="/installation"> Clone, link CLI, inject resolver memory into your agent host. </Card> <Card title="Workspace setup" href="/workspace-setup"> `loopany init`, bundled kinds, onboarding, cadence, and `doctor`. </Card> <Card title="Capture workflow" href="/capture-workflow"> Event→kind routing, quality gate, and subagent dispatch. </Card> <Card title="Reflect workflow" href="/reflect-workflow"> Evidence gathering, pattern thresholds, learnings, and proposals. </Card> <Card title="Periodic review" href="/periodic-review"> Daily, weekly, and monthly review scopes and closure gate. </Card> <Card title="Self-improvement loop" href="/self-improvement-loop"> Task outcomes → reflect → accept/reject → git-backed skill diffs. </Card> <Card title="Kinds and validation" href="/kinds-and-validation"> Workspace kind files, Zod schemas, and immutable write rules. </Card> </CardGroup> --- ## 09. Self-improvement loop > Task ## Outcome evidence, learning beliefs, skill-proposal accept/reject flow, checkAt followups, and the rule that agents never edit skills directly. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/09-self-improvement-loop.md - Generated: 2026-06-05T18:59:25.759Z ### Source Files - `skills/loopany-reflect/SKILL.md` - `skills/loopany-core/kinds/learning.md` - `skills/loopany-core/kinds/skill-proposal.md` - `skills/loopany-core/kinds/task.md` - `CLAUDE.md` - `src/commands/followups.ts` --- title: "Self-improvement loop" description: "Task ## Outcome evidence, learning beliefs, skill-proposal accept/reject flow, checkAt followups, and the rule that agents never edit skills directly." --- loopany’s self-improvement loop is implemented as markdown artifacts plus skills (`loopany-capture`, `loopany-reflect`, `loopany-review`): completed `task` records supply `## Outcome` evidence, `loopany-reflect` distills `learning` beliefs and optional `skill-proposal` artifacts, humans accept or reject proposals before any `SKILL.md` edit is applied and committed, and `checkAt` on learnings or accepted proposals drives `loopany followups` closure via daily review. ## Components | Piece | Kind / command | Role | |-------|----------------|------| | Evidence | `task` (`done` / `failed`) | Immutable `## Outcome` body; substrate for reflect | | Belief | `learning` (`active` → `superseded` / `archived`) | Declarative hypothesis with `evidence[]` | | Behavior change | `skill-proposal` (`pending` → `accepted` / `rejected`) | Contract for a skill edit; only path to mutate skills | | Discovery | `skills/loopany-reflect/SKILL.md` | Pattern thresholds, write learnings/proposals, apply accept/reject | | Scheduling | `checkAt` + `loopany followups` | Due-item query; daily review closes the loop | | Capture gate | `skills/loopany-capture/SKILL.md` | End-of-work routing; refuses weak outcomes | Kinds are defined under `skills/loopany-core/kinds/` and copied into `$LOOPANY_HOME/kinds/` at init. Runtime parses them into Zod frontmatter validators and status machines (`src/core/kind-registry.ts`); the CLI enforces **status transitions**, not body-section presence (section rules are kind contracts enforced by agent skills). ## End-to-end flow ```mermaid sequenceDiagram participant Work as Agent work participant Cap as loopany-capture participant Store as artifacts/*.md participant Ref as loopany-reflect participant User as Human participant Skill as skills/*/SKILL.md participant Git as git Work->>Cap: substantive work ends Cap->>Store: append ## Outcome; status done|failed Ref->>Store: list done tasks, dismissed signals Ref->>Store: create learning (+ optional skill-proposal) User->>Ref: accept|reject proposal alt accepted Ref->>Skill: apply ## Proposed change only Ref->>Store: append ## Outcome; status accepted Ref->>Git: commit skill + proposal else rejected Ref->>Store: append ## Outcome; status rejected end Store->>Store: checkAt due Note over Store: loopany followups + loopany-review daily ``` <Note> Reflect looks **forward** (new patterns). `loopany-review` daily revalidates **due** `checkAt` on existing learnings and proposals — do not mix scopes. </Note> ## Task outcomes as evidence Every terminal `task` must carry a `## Outcome` section before `done` or `failed` (kind contract in `skills/loopany-core/kinds/task.md`). The section answers what shipped or failed, whether observable evidence moved, and what you would do differently. Optional `## Before` is strongly recommended for metric work so reflect can falsify beliefs. Terminal transition pattern: ```bash loopany artifact append <task-slug> --section Outcome --content "..." loopany artifact status <task-slug> done --reason "<one line>" ``` `loopany-capture` is the quality gate at work boundaries: skip capture when you cannot write a one-sentence outcome, when the event is internal tooling noise, or when `loopany artifact list --contains "<phrase>"` finds a duplicate. Beliefs from fewer than two data points route to reflect, not ad-hoc `learning` creation during capture. <Warning> `artifact status` validates the kind **status machine** only (`src/core/artifact-store.ts` `setStatus`). It does not block `done` without `## Outcome` — agents must append Outcome first or pollute the reflect substrate. </Warning> ## Reflect: evidence → pattern → artifacts `skills/loopany-reflect/SKILL.md` runs in two modes: **reflect** (discover) and **proposal-apply** (accept/reject). ### When to reflect - User: “reflect”, “what have we learned”, “improve yourself” - Weekly cadence (paired with `loopany-review`; monthly structural review stays in review) - After ≥ 3 tasks reach `done` in a short window Do **not** reflect after every single task. ### Gather and dedupe ```bash loopany artifact list --kind task --status done loopany artifact list --kind signal loopany artifact list --kind signal --status dismissed loopany artifact list --kind learning --status active loopany artifact list --kind skill-proposal --status rejected ``` Filter to a recent window (~1 week, `createdAt` newest first). Subtract artifact IDs already listed in `evidence` on active learnings and non-rejected proposals so reflect does not duplicate work. ### Pattern thresholds | Pattern | Threshold | |---------|-----------| | Same class of outcome | ≥ 3 tasks | | Belief refuted | ≥ 2 tasks contradicting an active learning | | Belief needs caveat | ≥ 2 tasks | | Dismissed signal keeps recurring | ≥ 3 dismissals over ≥ 2 weeks | Below threshold: report insufficient evidence (skill regression scenario 8 expects **no** new `lrn-*` files). Do not create a `learning` from one bad outcome or re-propose a `rejected` skill-proposal. ## Learning artifacts `learning` stores a **belief**, not a behavior change. Title is the belief sentence. Frontmatter (camelCase since v0.2): | Field | Notes | |-------|-------| | `title` | Required — declarative belief | | `evidence` | String array; ≥ 2 artifact slugs at creation time | | `checkAt` | Date; 1–3 months; concrete revalidation question in body | | `supersedes` | Slug of prior learning when belief shifts | | `status` | `active` (default) → `superseded` → `archived` | | `mentions` | Optional structural links | Required body sections at creation: `## Observation`, `## Evidence`, `## Scope`, `## Check-at`. Create example: ```bash loopany artifact create --kind learning \ --slug short-attention-spans-2026 \ --title "Deals with more than three stakeholders close 2.5x slower" \ --evidence "task-a,task-b,task-c" \ --mentions "fundraising-2027" \ --check-at 2026-07-22 \ --content "$(cat <<'EOF' ## Observation ... ## Evidence - task-a — "..." - task-b — "..." ## Scope ... ## Check-at ... EOF )" ``` When understanding changes, **do not edit** the old learning. Create a new learning, link `loopany refs add --from <new> --to <old> --relation supersedes`, and `loopany artifact status <old> superseded --reason "superseded by <new>"`. Terminal `superseded` / `archived` require `## Outcome` on the learning kind. Many learnings stop at “now we know” with no skill change. ```mermaid stateDiagram-v2 [*] --> active active --> superseded: belief revised active --> archived superseded --> archived ``` ## Skill-proposal artifacts `skill-proposal` is the **only** path from agent judgment to skill file changes (`skills/loopany-core/kinds/skill-proposal.md`, `CLAUDE.md` architectural constraint). Agents must not edit `SKILL.md` files directly. | Field | Notes | |-------|-------| | `targetSkill` | Path to `SKILL.md` (existing for `modify`/`remove`; planned path for `add`) | | `changeType` | `modify` (default), `add`, `remove` | | `status` | `pending` → `accepted` \| `rejected` | | `evidence` | Supporting artifact slugs | | `checkAt` | On accept: schedule “did this help?” review | Required body before pending review: `## Motivation` (cite learning), `## Proposed change`, `## Expected effect`, `## Check-at`. For `changeType: add`, also `## Skill draft` (full new `SKILL.md`) and `## Resolver entry` (row for `skills/loopany-resolver/SKILL.md`). After accept or reject, body must include `## Outcome` before the status flip. ```mermaid stateDiagram-v2 [*] --> pending pending --> accepted: human accept + skill edit + git commit pending --> rejected: human reject + Outcome reason ``` Verify the chain: ```bash loopany trace <proposal-slug> --direction backward loopany refs <proposal-slug> --direction out --relation mentions ``` ## Accept and reject flows Proposal-apply mode (`loopany-reflect` Part 2): ```bash loopany artifact list --kind skill-proposal --status pending loopany artifact get <proposal-slug> ``` <Steps> <Step title="Accept"> Read proposal, cited learning (`refs` / `mentions`), and current `targetSkill` file. Apply **only** the described change. Append `## Outcome` to the proposal (literal diff summary). Run `loopany artifact status <proposal-slug> accepted --reason "..."`. Git-commit the skill file and proposal artifact together. </Step> <Step title="Reject"> Read proposal. Append `## Outcome` with a clear reason (future reflect reads this). `loopany artifact status <proposal-slug> rejected --reason "..."`. </Step> </Steps> Edge cases from the reflect skill: reject if target file missing, cited learning was superseded, or multiple pending proposals touch the same file (accept one at a time, re-read target between). <Tip> Resolver routing for proposals: `skills/loopany-resolver/SKILL.md` maps “accept &lt;proposal-slug&gt;” / “reject &lt;proposal-slug&gt;” to `loopany-reflect`. </Tip> ## checkAt and followups `checkAt` is indexed frontmatter on `task`, `learning`, and other kinds that declare it. Set only when there is a concrete future question; missing dates are better than ignored ones. ```bash loopany followups --due today loopany followups --due overdue loopany followups --due next-7d loopany followups --due today --include-done true ``` Implementation (`src/commands/followups.ts`, `src/core/index.ts` `followups()`): - Returns artifacts whose `checkAt` date is on or before the cutoff. - **Default:** hides artifacts in a **terminal** status (no outgoing transitions in the kind status machine — e.g. `task` `done`, `skill-proposal` `accepted`). - `overdue` further restricts to dates strictly before today. Daily review (`skills/loopany-review/references/daily.md`) runs `loopany followups --due today` only (not overdue — weekly’s job). For each due learning, `loopany trace <learning-slug> --direction backward` informs revalidation. Closure gate: every surfaced item ends resolved, deferred (`checkAt` pushed with reason), or retired (`checkAt` removed with note). Weekly review nudges pending proposals (`artifact list --kind skill-proposal --status pending`; >5 pending → nudge) and suggests reflect when ≥3 resolutions occurred in the pass. ## Cadence (skills, not CLI cron) | Cadence | Skill | Self-improvement role | |---------|-------|------------------------| | End of substantive work | `loopany-capture` | Write/refine task outcomes | | Weekly | `loopany-reflect` | New learnings + proposals | | Daily | `loopany-review` | Close due `checkAt` items | | Weekly | `loopany-review` | Overdue sweep, doctor, feed reflect | | On demand | `loopany-reflect` proposal-apply | Accept/reject pending proposals | Coding CLI hosts often use session-boundary prompts instead of durable cron (`INSTALL_FOR_AGENTS.md`); judgment stays in skills, queries stay in `loopany` CLI. ## Quick reference ```text CAPTURE: work ends → quality gate → append Outcome → task done|failed REFLECT: list evidence → pattern? → learning → (optional) skill-proposal pending ACCEPT: read spr → read lrn → edit targetSkill → Outcome → accepted → git commit REJECT: read spr → Outcome (reason) → rejected FOLLOWUP: checkAt due → followups → review classify → resolve|defer|retire INVARIANT: never edit skills except via accepted skill-proposal ``` <Check> Self-improvement needs no separate “lesson” entity: outcomes, learnings, proposals, graph edges (`supersedes`, `mentions`, `cites`), and git-backed skill files are the full loop. </Check> ## Related pages <CardGroup> <Card title="Reflect workflow" href="/reflect-workflow"> Step-by-step reflect and proposal-apply procedures. </Card> <Card title="Capture workflow" href="/capture-workflow"> Event routing and the Outcome quality gate at task close. </Card> <Card title="Periodic review" href="/periodic-review"> Daily followups, weekly doctor/overdue, monthly mission checks. </Card> <Card title="Self-improvement example" href="/self-improvement-example"> Recipe: three done tasks → reflect → accept proposal → git diff. </Card> <Card title="Kinds and validation" href="/kinds-and-validation"> Kind markdown, status machines, and frontmatter validators. </Card> <Card title="Skills library" href="/skills-library"> Resolver, core, capture, reflect, and review skill packs. </Card> </CardGroup> --- ## 10. Workspace setup > Run loopany init, verify bundled kinds copied, complete ONBOARDING.md once, register cadence (cron vs session-boundary), and confirm doctor passes. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/10-workspace-setup.md - Generated: 2026-06-05T19:00:21.263Z ### Source Files - `src/commands/init.ts` - `ONBOARDING.md` - `INSTALL_FOR_AGENTS.md` - `src/commands/doctor.ts` - `skills/loopany-core/kinds/mission.md` - `skills/loopany-core/kinds/person.md` - `skills/loopany-resolver/SKILL.md` --- title: "Workspace setup" description: "Run loopany init, verify bundled kinds copied, complete ONBOARDING.md once, register cadence (cron vs session-boundary), and confirm doctor passes." --- `loopany init` scaffolds the per-user brain at `$LOOPANY_HOME` (default `~/loopany`): directories, `config.yaml` with the current `schemaVersion`, and a one-time copy of bundled kind definitions from `skills/loopany-core/kinds/`. A healthy workspace still needs a single onboarding pass (`ONBOARDING.md`), an agent-chosen cadence for review/reflect skills, and `loopany doctor` exit `0` before routine capture and graph work. ## Prerequisites | Requirement | Notes | |-------------|--------| | Bun | Runtime for the CLI (`bun install && bun link` from the repo checkout) | | `loopany` on `PATH` | Typically `~/.bun/bin` after `bun link` | | Writable brain directory | Default `~/loopany`; override with `LOOPANY_HOME` | <Note> `LOOPANY_HOME` is read on every command via `getWorkspaceRoot()` — the workspace is per user, not per shell cwd. </Note> ## Workspace layout after init `runInit()` creates the root, `kinds/`, and `artifacts/`, writes `config.yaml` if missing, and copies any bundled `*.md` kind files not already present. It does **not** create `references.jsonl` or seed artifacts; those appear on first `refs add` / `artifact create`. ```text $LOOPANY_HOME/ # default ~/loopany ├── config.yaml # schemaVersion: <SCHEMA_VERSION> (currently 0.2.0) ├── kinds/ # copied from repo skills/loopany-core/kinds/ │ ├── brief.md, journal.md, learning.md, mission.md, note.md │ ├── person.md, signal.md, skill-proposal.md, task.md ├── artifacts/ # empty until onboarding / capture ├── audit.jsonl # first line written on first CLI op (e.g. init) └── references.jsonl # created when first edge is added ``` ## Initialize the workspace <Steps> <Step title="Run init"> ```bash loopany init ``` With a custom root: ```bash LOOPANY_HOME=/path/to/brain loopany init ``` </Step> <Step title="Confirm CLI output"> | Outcome | stdout signal | |---------|----------------| | First run | `Initialized loopany workspace at …` and `Created N file(s).` | | Idempotent re-run | `Workspace already initialized — nothing to do.` | | Needs onboarding | Blank line, then `NEXT — read ONBOARDING.md…` | Init is idempotent: existing files (including kind copies) are left untouched; only missing pieces are added. </Step> <Step title="Verify bundled kinds"> ```bash ls "$LOOPANY_HOME/kinds" loopany kind list ``` E2E expects exactly nine files under `kinds/`: `brief.md`, `journal.md`, `learning.md`, `mission.md`, `note.md`, `person.md`, `signal.md`, `skill-proposal.md`, `task.md`. `loopany kind list` prints JSON with one entry per loaded kind — use it when you need parsed schemas, not just filenames. </Step> </Steps> ### Init implementation constraints - **Source of kind defs:** `skills/loopany-core/kinds/` in the repo checkout (not TypeScript enums). - **Mission probe:** `needsOnboarding` is true when `artifacts/missions/` has no `*.md` files (any mission file suppresses the nag, not only `active`). - **Audit:** `init` writes an `audit.jsonl` entry (`op: init`, ISO `ts`). ## Onboarding (run once) `ONBOARDING.md` is the agent script for the first conversation. Rules that affect workspace health: | Rule | Behavior | |------|----------| | Run at most once | If a `mission` with `status: active` already exists, exit immediately | | Pre-read | `loopany --help` + skim `$LOOPANY_HOME/kinds/*.md` before speaking | | Phase 3 artifacts | Person for the user + one or more `mission` artifacts with `status: active` and `hypothesis` set | | Backfill | Optional silent capture with `[backfill]` prefix per `skills/loopany-capture/SKILL.md` | ### Artifacts doctor expects `loopany doctor` onboarding check requires: 1. **Person id `self`** — `idx.byId('self')` must exist (v0.2 slug-as-id). Create with: ```bash loopany artifact create --kind person --slug self --name "Your Name" ``` <Warning> `ONBOARDING.md` Phase 3 still says `prs-self`; that was the v0.1 id. On v0.2 workspaces, use `--slug self` or doctor fails with `self person artifact missing`. </Warning> 2. **≥1 active mission** — e.g.: ```bash loopany artifact create --kind mission --slug ship-v1 \ --title "Ship loopany v1" --status active ``` After onboarding, `loopany init` no longer prints the `ONBOARDING` hint (any mission file under `artifacts/missions/` is enough). ### Resolver gate `skills/loopany-resolver/SKILL.md` blocks all other skills until bootstrap passes: ```bash loopany artifact list --kind mission --status active ``` No active mission → read `ONBOARDING.md` and stop. ## Cadence: cron vs session-boundary Recurring work is defined in skills, not in `loopany init`. `INSTALL_FOR_AGENTS.md` Step 4 assigns **how** those skills fire — without asking the user to choose cron mechanics. | Cadence | Skill | Role | |---------|-------|------| | Daily | `skills/loopany-review/SKILL.md` | `loopany followups --due today`; close items with state transitions | | Weekly | `skills/loopany-review/SKILL.md` | Overdue sweep + `loopany doctor` + parking-lot queries | | Weekly | `skills/loopany-reflect/SKILL.md` | Learnings + skill-proposals from recent outcomes | | Monthly | `skills/loopany-review/SKILL.md` | Mission-drift and structural-drift detection | ```mermaid flowchart LR subgraph durable ["§A Durable cron hosts"] CRON[Platform cron] CRON --> REV[loopany-review] CRON --> REF[loopany-reflect] end subgraph session ["§B Coding CLIs default"] START[Session start] START --> FU["followups --due today"] END[Week / month boundary] END --> SWEEP[Weekly review + reflect suggest] end ``` <Tabs> <Tab title="§A — Durable cron (Hermes, OpenClaw, …)"> Register each skill on its schedule using the host’s cron mechanism. Run inside an agent session (judgment-heavy), not as bare shell-only cron unless you build a wrapper task. Record job IDs / config paths for re-install audits. Do not describe registration to the user — only the plain-English cadence line below. </Tab> <Tab title="§B — Coding CLIs (default)"> Do **not** register platform cron on Claude Code–class hosts: recurring tasks expire (~7 days on Claude Code), so jobs die silently. **Default:** prompt at session boundaries — start of day → `loopany followups --due today`; end of week → propose weekly review + reflect; month-end → mission review. System-level `launchd` / crontab is optional future work captured as a `task` artifact if the user asks; not an onboarding question. </Tab> </Tabs> ### User-facing cadence line End onboarding with one sentence (from `INSTALL_FOR_AGENTS.md`): > I'll check in at the start of each day on what's due, propose a sweep + reflect at week's end, and surface mission-drift around month's end. You can run `loopany factory` anytime for a visual map of what's in here. No registration menus or host-quality discussion during setup. ## Resolver injection (agent hosts) Workspace files alone do not load skills. Append `injections/resolver-memory.md` into the host memory file so every task ends with a read of `skills/loopany-resolver/SKILL.md`: ```bash grep -q "loopany skill resolver" ~/.claude/CLAUDE.md 2>/dev/null || \ cat ~/loopany-src/injections/resolver-memory.md >> ~/.claude/CLAUDE.md ``` <Info> `loopany doctor` does not yet verify resolver registration (noted as roadmap in `INSTALL_FOR_AGENTS.md`). Verify with `grep -l "loopany skill resolver"` on the target memory file. </Info> ## Verify with doctor ```bash loopany doctor # machine-readable: loopany doctor --format json ``` | Exit code | Meaning | |-----------|---------| | `0` | No check with `status: fail` | | `1` | At least one failed check (stdout still prints the report) | Doctor runs `bootstrap({ skipVersionCheck: true })` so it can **report** schema mismatch instead of throwing `SchemaVersionMismatchError`. ### Check inventory | Check | Fail? | What it validates | |-------|-------|-------------------| | `workspace` | — | Always ok if doctor ran (path echoed) | | `schema version` | fail | `config.yaml` `schemaVersion` vs binary `SCHEMA_VERSION` | | `kinds` | fail | Every kind file parses; lists loaded kinds | | `artifacts` | fail | All indexed frontmatter passes per-kind Zod | | `references` | fail | No dangling `from` / `to` in `references.jsonl` | | `onboarding` | fail | `self` person + ≥1 `mission` with `status: active` | | `mission coverage` | warn | Tasks without a mission in `mentions` | | `domain coverage` | warn | Artifact `domain` not in `enabled_domains` | Fresh init → doctor fails onboarding (`self` / active mission missing). Minimal green path (matches e2e): ```bash loopany artifact create --kind person --slug self --name "Test User" loopany artifact create --kind mission --slug g1 --title "Test mission" --status active loopany doctor ``` ### Schema mismatch before doctor is green If `schema version` fails, run the migration named in `problems`, e.g. `loopany migrate v0.1.0-to-v0.2.0`, following `skills/migrations/v0.1.0-to-v0.2.0/SKILL.md`. Do not hand-edit `schemaVersion` alone. ## Setup flow (end-to-end) ```text install CLI (bun link) → loopany init → verify kinds/ + kind list → ONBOARDING.md (once) → self + active mission(s) → pick cadence §A or §B silently → optional resolver-memory injection → loopany doctor (exit 0) → resolver bootstrap OK → routine capture / review ``` ## Troubleshooting | Symptom | Likely cause | Fix | |---------|--------------|-----| | `No loopany workspace at … Run loopany init` | Missing `kinds/` under `$LOOPANY_HOME` | `loopany init` | | Init keeps printing `ONBOARDING` | No `artifacts/missions/*.md` | Complete onboarding | | Doctor: `self person artifact missing` | No person id `self` | `artifact create --kind person --slug self …` | | Doctor: `no active mission` | Missions missing or none `active` | Create mission with `--status active` | | Kind list empty / errors | Broken files in `kinds/` | Compare to bundled set; fix YAML in kind frontmatter | | Doctor schema fail | Workspace older than binary | `loopany migrate …` per doctor `problems` | | Cron “stopped working” on coding CLI | Session-scoped cron expiry | Switch to session-boundary prompts (§B) | <Warning> Warnings (`mission coverage`, `domain coverage`) do not change the exit code. Treat fails (`onboarding`, `references`, `schema version`, etc.) as blocking. </Warning> ## Related pages <CardGroup> <Card title="Installation" href="/installation"> Clone, Bun, `bun link`, `PATH`, and resolver injection into Claude Code / Codex / other hosts. </Card> <Card title="Quickstart" href="/quickstart"> First session after setup: mission, task, reference edge, optional factory. </Card> <Card title="Artifacts and workspace" href="/artifacts-and-workspace"> On-disk layout, slug-as-id rules, and `config.yaml` fields. </Card> <Card title="Doctor and troubleshooting" href="/doctor-and-troubleshooting"> Per-check failures, dangling refs, and recovery commands. </Card> <Card title="Periodic review" href="/periodic-review"> Daily followups, weekly doctor sweep, monthly mission drift. </Card> <Card title="Configuration reference" href="/configuration-reference"> `LOOPANY_HOME`, `LOOPANY_SKIP_VERSION_CHECK`, `schemaVersion`, `enabled_domains`. </Card> </CardGroup> --- ## 11. Capture workflow > End-of-task capture quality gate, event→kind routing (task/signal/note), subagent dispatch pattern, and duplicate detection via artifact list --contains. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/11-capture-workflow.md - Generated: 2026-06-05T18:59:39.908Z ### Source Files - `skills/loopany-capture/SKILL.md` - `skills/loopany-core/kinds/task.md` - `skills/loopany-core/kinds/signal.md` - `src/commands/artifact-list.ts` - `src/commands/artifact-create.ts` - `skills/loopany-resolver/SKILL.md` --- title: "Capture workflow" description: "End-of-task capture quality gate, event→kind routing (task/signal/note), subagent dispatch pattern, and duplicate detection via artifact list --contains." --- `skills/loopany-capture/SKILL.md` defines when an agent records completed work into `$LOOPANY_HOME`. The resolver routes here after substantive work ends; capture picks the artifact kind, runs a quality gate, deduplicates with `loopany artifact list --contains`, and delegates writes to a subagent when the host supports it. Creation and status transitions still go through `loopany-core` kind playbooks and the CLI. ## Placement in the skill stack ```text User request completes (substantive boundary) │ ▼ loopany-resolver ──trigger: PR shipped, incident resolved, decision made──► │ ▼ loopany-capture quality gate → kind choice → duplicate check │ ├──► loopany-core (artifact create / append / status / refs) │ └──► loopany-reflect (suggest after ≥3 captures in one session) ``` <Info> Read `skills/loopany-resolver/SKILL.md` first in any loopany session. Capture does not replace core: it decides *whether* and *what kind*; core’s kind playbooks govern *how* to write the artifact. </Info> Bootstrap still applies before capture: an active `mission` must exist (`loopany artifact list --kind mission --status active`). Without one, onboarding (`ONBOARDING.md`) blocks other skills. ## When capture runs Capture is **end-of-task**, not per message. | Run capture | Skip capture | |-------------|--------------| | PR shipped or merged | Typo fix, whitespace, one-liner | | Incident resolved | Internal agent work (grep, read files, answer questions) | | Decision with rationale worth finding later | Feelings or opinions without observable evidence | | Problem noticed but deferred | Work still in progress (no `## Outcome` yet) | | User says “record this” / “log this outcome” | Speculation (at most a concrete `signal`) | **Substantive** means the session produced evidence or a decision a future reader would need—not routine exploration. ## Event → kind routing | Event | Target kind | Playbook | |-------|-------------|----------| | PR shipped / merged | `task` (typically `--status done`) | `skills/loopany-core/kinds/task.md` | | Incident resolved | `task` | same | | Problem noticed, not acting now | `signal` | `skills/loopany-core/kinds/signal.md` | | Outcome for an existing open task | append `## Outcome`, then terminal status | `task.md` § Playbook | | Decision with rationale | `note` or closed `task` | flowchart below | | Belief from ≥ 2 data points | `learning` | via `loopany-reflect`, not inline capture | ### Decision branches ```mermaid flowchart TD A[Work concluded] --> B{Resolved a pending signal?} B -->|yes| C["task --status done + addressed edge"] B -->|no| D{Produced something shippable?} D -->|yes| E["task --status done with ## Outcome"] D -->|no| F{Reasoned preference / principle?} F -->|yes| G["note: decided: X over Y because Z"] F -->|no| H{Belief from ≥2 data points?} H -->|yes| I[learning via loopany-reflect] H -->|no| J[Default: note or skip quality gate] ``` <Note> When unsure between `note` and a structured kind, default to `note` (`skills/loopany-capture/SKILL.md`). Use `task` only when you can write a one-sentence `## Outcome`. </Note> Core’s parallel routing table (`skills/loopany-core/SKILL.md`) aligns: acting now → `task`; deferred → `signal`; pattern beliefs → `learning` through reflect; duplicate → append, don’t fork. ## Quality gate Apply **before** any `artifact create`. Skip when any gate fails: | Gate | Rule | |------|------| | Observable | Must be fact-backed (path, session, commit, metric)—not mood | | Already captured | `loopany artifact list --contains "<phrase>"` → append to match | | Too small | Trivial edits don’t belong in the brain | | Speculation | No concrete signal; weak “might be” items stay out | | Internal work | Tooling and Q&A are not capture events | **Outcome test:** if you cannot draft a one-sentence `## Outcome`, do not create a closing `task`. `test/skill-regression.sh` scenario 4 encodes this: a README typo fix must not increase artifact count after the gate. Scenario 5 expects a shipped PR (rate limiting, metrics) to become a `task`. <Warning> Skipping the gate pollutes the workspace and downstream reflect. Noisy artifacts dilute pattern detection. </Warning> ## Duplicate detection with `--contains` Dedup is a **search-then-append** step, not a separate command. <ParamField body="--contains" type="string"> Case-insensitive substring filter on `loopany artifact list`. Applied **after** `--kind`, `--status`, `--domain`, and other field filters narrow the candidate set. </ParamField> **Search order** (`src/commands/artifact-list.ts`): 1. Seed candidates from indexes (`byKind`, `byStatus`, `byDomain`, or `all`). 2. Apply frontmatter field filters (indexed fields use O(1) intersection when `--kind` is set). 3. For each remaining row: match string/`string[]` frontmatter values, else read body and match substring. **Recommended queries** (kind playbooks): ```bash # Capture skill — broad dedup before create loopany artifact list --contains "<key phrase>" # Task duplicates (prefer kind-scoped) loopany artifact list --kind task --contains "<key phrase>" # Signal duplicates loopany artifact list --kind signal --contains "<symptom or path>" ``` | Match found | Action | |-------------|--------| | Same topic, open `task` in `todo`/`running` | Append evidence or `loopany refs add … --relation follows-up` | | Same signal symptom | `loopany artifact append <sig-id> --section Recurrences` | | Near-duplicate `person` | Check aliases via `--kind person --contains "<name>"` | | No match | `loopany artifact create` per kind playbook | E2E tests verify body match (`retention`), combined filters (`--kind task --status todo --contains retention`), title-only frontmatter (`orbit`), and `string[]` aliases (`stargaz` → `alice-chen`). ## Subagent dispatch pattern Long sessions should **not** capture inline—the main agent loses focus and writes weaker artifacts. ```mermaid sequenceDiagram participant Main as Main session participant Gate as Quality gate participant Sub as Subagent (optional) participant CLI as loopany CLI participant Core as loopany-core playbooks Main->>Gate: Work boundary reached Gate->>Gate: Observable? Already captured? Outcome test? alt Skip Gate-->>Main: No artifact else Capture Main->>Main: Compose 3–5 sentence context Main->>Sub: Record via loopany; read loopany-core for kind Sub->>Core: Read kind playbook Sub->>CLI: artifact create / append / status CLI-->>Sub: id, path Sub-->>Main: artifact ID Main->>Main: Continue primary task end ``` <Steps> <Step title="Filter in the main session"> Run the quality gate and `--contains` dedup. Decide kind from the event table. </Step> <Step title="Compose context"> Write 3–5 sentences: what happened, why it matters, numbers, and any artifact IDs in scope. </Step> <Step title="Dispatch"> Prompt: “Record this via loopany. Read `loopany-core` to pick the right kind. Return the artifact ID.” </Step> <Step title="Subagent writes"> Subagent runs CLI commands from the kind playbook (create, append `Outcome`, `status`, `refs add`). </Step> <Step title="Resume"> Main session stores the returned ID; no context switch for markdown authoring. </Step> </Steps> <Tip> If the host has no subagent primitive, capture inline but keep commands minimal—one create or one append, not a long authoring detour. </Tip> ## Typical CLI sequences ### Shipped work → `task` ```bash loopany artifact list --kind task --contains "rate limiting" loopany artifact create --kind task \ --title "Ship API rate limiting (token bucket, Redis)" \ --status done \ --content "## Outcome Added 100 req/min per user; staging 429 errors down ~80%." # Or two-step if body grows: loopany artifact create --kind task --title "..." --status running --content "## Plan ..." loopany artifact append <id> --section Outcome --content "..." loopany artifact status <id> done --reason "Merged PR #42" ``` Terminal `task` statuses (`done`, `failed`) require a `## Outcome` section in the kind playbook; the CLI enforces status **transitions**, not body shape—agents must append Outcome before flipping status. ### Deferred problem → `signal` ```bash loopany artifact list --kind signal --contains "PayloadTooLargeError" loopany artifact create --kind signal \ --title "upload_image base64-encodes video → >5MB → PayloadTooLargeError" \ --content "Observed in staging. **Risk:** large uploads fail." ``` Signals need a concrete observable title (≤ 160 chars). Close with `addressed` + graph edge, or `dismissed` + reason. ### Decision → `note` ```bash loopany artifact create --kind note \ --slug "redis-over-memcached-sessions" \ --title "decided: Redis over Memcached for sessions because TTL is built-in" \ --content "Evaluated both; Redis ops already in stack." ``` Pass `--slug` when the artifact will be cited in `[[wiki-links]]`. ### Signal resolved by new task ```bash loopany artifact create --kind task --title "Fix upload size path" --status todo # … work … loopany artifact append <tsk-id> --section Outcome --content "..." loopany artifact status <tsk-id> done --reason "Deployed fix" loopany artifact status <sig-id> addressed --addressed-by <tsk-id> ``` `artifact status … addressed --addressed-by <id>` emits `<addresser> addresses <signal>` in `references.jsonl` (`src/commands/artifact-status.ts`). ## Capture → core → reflect chaining | Chain | When | |-------|------| | Capture → Core | Every capture: kind from capture, schema/status from core playbooks | | Capture → Reflect | Resolver suggests reflect after **≥ 3 captures in one session** | | Reflect threshold | Reflect skill runs after **≥ 3 tasks** reach `done` in a short window—not after every single capture | Beliefs and skill edits never originate in capture alone; route to `loopany-reflect` when evidence crosses the learning threshold. ## Anti-patterns | Anti-pattern | Why it hurts | |--------------|--------------| | Capture on every message | Noise; breaks end-of-task discipline | | Capture mid-work | No Outcome; premature `task` | | Skip quality gate | Pollutes reflect and search | | Inline capture in long sessions | Weak artifacts; use subagent dispatch | | Fork duplicate signals/tasks | Use `--contains` then append | | Create `learning` from one task | Wait for patterns; use reflect | ## Verification | Check | Command / artifact | |-------|-------------------| | List dedup | `bun test` → `loopany artifact list` `--contains` cases in `test/cli.e2e.test.ts` | | Quality gate | `./test/skill-regression.sh 4` (trivial work → no new files) | | PR → task routing | `./test/skill-regression.sh 5` | | Workspace health | `loopany doctor` after bulk capture | ## Related pages <CardGroup> <Card title="Skills library" href="/skills-library"> Resolver, capture, core, reflect, and review packs; routing table and harness-neutral injection. </Card> <Card title="Artifact commands" href="/artifact-commands"> `create`, `append`, `status`, `--addressed-by`, and Outcome conventions for terminal tasks. </Card> <Card title="Kinds and validation" href="/kinds-and-validation"> `task`, `signal`, and `note` schemas, status machines, and indexed fields used by `list`. </Card> <Card title="Reflect workflow" href="/reflect-workflow"> When capture should hand off to learnings and skill-proposals instead of writing them inline. </Card> <Card title="Artifact lifecycle example" href="/artifact-lifecycle-example"> End-to-end signal → task → brief flow aligned with e2e scenarios. </Card> </CardGroup> --- ## 12. Reflect workflow > Gather recent outcomes and dismissed signals, pattern thresholds, write learning and skill-proposal artifacts, and accept/reject proposals with git-backed skill diffs. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/12-reflect-workflow.md - Generated: 2026-06-05T19:00:02.604Z ### Source Files - `skills/loopany-reflect/SKILL.md` - `skills/loopany-core/kinds/learning.md` - `skills/loopany-core/kinds/skill-proposal.md` - `src/commands/artifact-list.ts` - `src/commands/artifact-status.ts` - `src/commands/artifact-get.ts` --- title: "Reflect workflow" description: "Gather recent outcomes and dismissed signals, pattern thresholds, write learning and skill-proposal artifacts, and accept/reject proposals with git-backed skill diffs." --- The reflect workflow is agent-driven through `skills/loopany-reflect/SKILL.md`: it queries the workspace via `loopany artifact list` / `get`, applies pattern thresholds before writing `learning` or `skill-proposal` artifacts, and completes skill changes only through proposal-apply (accept/reject) with `artifact append`, `artifact status`, and a git commit — never by editing `SKILL.md` files directly. <Info> Reflect is not a CLI subcommand. The harness supplies deterministic artifact I/O and graph edges; judgment (pattern detection, belief synthesis, scoped edits) stays in the agent skill. </Info> ## Modes and routing `loopany-resolver` routes these triggers to `loopany-reflect`: | Trigger | Mode | |---------|------| | `reflect`, `what have we learned`, `improve yourself`, ≥3 tasks done recently | Reflect (discover → write) | | `accept <proposal-slug>`, `reject <proposal-slug>`, `review proposals` | Proposal apply | | Writing a `learning` or `skill-proposal` from any source | Reflect (creation rules) | Cross-skill chaining: capture may suggest reflect after ≥3 captures in a session; weekly review suggests reflect after ≥3 resolutions. Learning `checkAt` revalidation belongs in `loopany-review` (daily), not reflect. ```mermaid sequenceDiagram participant User participant Resolver as loopany-resolver participant Reflect as loopany-reflect participant CLI as loopany CLI participant Store as artifacts + references.jsonl participant Git as git (skills/) User->>Resolver: reflect / accept proposal Resolver->>Reflect: load SKILL.md alt Reflect mode Reflect->>CLI: artifact list / get CLI->>Store: indexed + body reads Reflect->>Reflect: pattern thresholds Reflect->>CLI: artifact create (learning, skill-proposal) Reflect->>CLI: refs add / trace (optional) else Proposal apply Reflect->>CLI: artifact get / refs Reflect->>Git: edit target SKILL.md (accept only) Reflect->>CLI: artifact append Outcome Reflect->>CLI: artifact status accepted|rejected Reflect->>Git: commit skill + proposal artifact end ``` ## When to run reflect Run reflect when: - The user explicitly asks to reflect or improve the agent. - Weekly cadence (structural mission review stays in `loopany-review` monthly). - At least three tasks have flipped to `done` in a short window. <Warning> Do not run reflect reactively after every single task. `test/skill-regression.sh` scenario 8 expects agents to decline creating a `learning` when fewer than three completed tasks with outcomes exist. </Warning> ## Step 1 — Gather evidence List candidates, then narrow by recency and deduplication. <Steps> <Step title="List artifact pools"> ```bash loopany artifact list --kind task --status done loopany artifact list --kind signal loopany artifact list --kind signal --status dismissed loopany artifact list --kind learning --status active loopany artifact list --kind skill-proposal --status rejected ``` `artifact list` returns a JSON array of `{ id, kind, path, frontmatter }`. With `--kind`, other flags map to frontmatter fields; fields listed in the kind’s `indexedFields` use O(1) index lookup (`status`, `targetSkill`, `checkAt`, etc.). </Step> <Step title="Filter to recent window"> Sort by `createdAt` (newest first). Default window ≈ one week. </Step> <Step title="Subtract already-processed evidence"> Union `evidence` from active learnings and non-rejected skill-proposals; exclude those artifact IDs from new pattern candidates. </Step> <Step title="Load bodies"> For each fresh candidate: ```bash loopany artifact get <id> # or --format json for frontmatter + body + audit history ``` Task evidence must include `## Outcome` before terminal `done`/`failed` (kind playbook; append before status flip). </Step> </Steps> Optional body filter when the candidate set is still large: ```bash loopany artifact list --kind task --status done --contains "baseline" ``` `--contains` runs last and scans bodies (and string frontmatter) case-insensitively. ## Step 2 — Pattern thresholds Reflect only promotes **patterns**, not single anecdotes. | Pattern | Threshold | |---------|-----------| | Same class of outcome | ≥ 3 tasks | | Belief refuted | ≥ 2 tasks where an active learning predicts wrong | | Belief needs caveat | ≥ 2 tasks | | Signal dismissed but recurs | ≥ 3 dismissals over ≥ 2 weeks | Good example: four metric tasks ship without `## Before` baselines → learning that outcomes are unfalsifiable → proposal requiring `## Before` in the task playbook. Bad example: one failed outcome → no learning. A proposal whose change was already `rejected` → do not re-suggest. ## Step 3 — Write a learning Beliefs live in `learning` artifacts (`skills/loopany-core/kinds/learning.md`). Title is the declarative belief; behavior change is optional and goes through `skill-proposal`. <ParamField body="--kind" type="learning" required> </ParamField> <ParamField body="--slug" type="string" required> Stable id for heavy `[[wiki-link]]` citation. </ParamField> <ParamField body="--title" type="string" required> The belief sentence itself. </ParamField> <ParamField body="--evidence" type="string[]" required> Comma-separated artifact slugs; minimum two IDs per playbook. </ParamField> <ParamField body="--check-at" type="date"> Revalidation date, typically 1–3 months out. </ParamField> <ParamField body="--mentions" type="string[]"> Soft links (also creatable via body `[[id]]`). </ParamField> <RequestExample> ```bash loopany artifact create --kind learning \ --slug short-attention-spans-2026 \ --title "Metric tasks without baselines produce unfalsifiable outcomes" \ --evidence "tsk-alpha,tsk-beta,tsk-gamma" \ --mentions "mission-default" \ --check-at 2026-07-22 \ --content "$(cat <<'EOF' ## Observation Four completed metric tasks recorded outcomes without before/after evidence. ## Evidence - tsk-alpha — "Latency improved" - tsk-beta — "Conversion up" - tsk-gamma — "No baseline captured" ## Scope Applies to quantitative tasks; not exploratory spikes. ## Check-at On 2026-07-22: did new metric tasks include ## Before? EOF )" ``` </RequestExample> Required body sections: `## Observation`, `## Evidence`, `## Scope`, `## Check-at`. Status machine: `active` → `superseded` | `archived`. When understanding changes, **create a new learning** — do not edit the old file in place. ### Supersede an older belief ```bash loopany refs add --from <new-learning> --to <old-learning> --relation supersedes loopany artifact status <old-learning> superseded --reason "superseded by <new-learning>" ``` Append `## Outcome` to the superseded learning when flipping to `superseded` (kind required-sections rule; enforced by agent discipline). ## Step 4 — Write a skill-proposal (optional) Create a proposal only when the learning implies a **concrete** `SKILL.md` edit. Many learnings stop at updated belief. <ParamField body="--kind" type="skill-proposal" required> </ParamField> <ParamField body="--target-skill" type="string" required> Path to `SKILL.md` (existing for `modify`/`remove`; planned path for `add`). </ParamField> <ParamField body="--change-type" type="enum" default="modify"> `modify` | `remove` | `add` </ParamField> <ParamField body="--evidence" type="string[]"> Citing tasks and/or the backing learning slug. </ParamField> <ParamField body="--check-at" type="date"> Schedules “did this change help?” review after accept. </ParamField> Required body sections: 1. `## Motivation` — cite the learning 2. `## Proposed change` — target file, intent, location, approximate content 3. `## Expected effect` — short- and long-term 4. `## Check-at` — why that date For `changeType: add`, also include `## Skill draft` (full new `SKILL.md`) and `## Resolver entry` (row for `skills/loopany-resolver/SKILL.md`) or the skill is unreachable. Status machine: ```mermaid stateDiagram-v2 [*] --> pending pending --> accepted: human accept + Outcome pending --> rejected: human reject + Outcome ``` CLI enforces transitions (`artifact status`); terminal `accepted`/`rejected` require `## Outcome` in the body per kind definition. ## Step 5 — Verify evidence chain ```bash loopany trace <proposal-slug> --direction backward ``` Default trace relations: `led-to`, `addresses`, `supersedes`, `follows-up`, `cites` (excludes soft `mentions`). Output: `{ root, nodes[{ id, kind, distance }], edges[] }` sorted cause → root → effect. To read explicit learning links on a proposal: ```bash loopany refs <proposal-slug> --direction out --relation mentions ``` ## Proposal apply (accept / reject) Agents never patch skills outside this flow. <Steps> <Step title="List pending proposals"> ```bash loopany artifact list --kind skill-proposal --status pending ``` </Step> <Step title="Accept"> 1. `loopany artifact get <proposal-slug>` 2. `loopany refs <proposal-slug> --direction out --relation mentions` → cited learning 3. Read current `targetSkill` file 4. Apply **only** the described change 5. `loopany artifact append <proposal-slug> --section Outcome --content "..."` 6. `loopany artifact status <proposal-slug> accepted --reason "..."` 7. Git commit the skill file and proposal artifact together </Step> <Step title="Reject"> 1. Read proposal 2. `loopany artifact append <proposal-slug> --section Outcome --content "<reason>"` (future reflect reads this) 3. `loopany artifact status <proposal-slug> rejected --reason "..."` </Step> </Steps> ### Accept edge cases | Condition | Action | |-----------|--------| | Target `SKILL.md` missing | Reject | | Cited learning `superseded` | Reject | | Multiple pending proposals same file | Accept one at a time; re-read target between | <Check> Accepted or rejected proposals must have a non-empty `## Outcome` before status flip — empty outcomes break the self-improvement audit trail. </Check> ## CLI surfaces reflect depends on | Command | Role in reflect | |---------|-----------------| | `artifact list` | Evidence pools, dedup, pending proposals | | `artifact get` | Outcomes, motivations, audit (`--format json`) | | `artifact create` | New `learning` / `skill-proposal` | | `artifact append` | `## Outcome` on accept/reject | | `artifact status` | `accepted`/`rejected`/`superseded`; illegal transitions error | | `refs add` / `refs` | `supersedes`, `mentions` | | `trace` | Backward evidence chain | <Note> Kind required sections (`## Outcome` on terminal statuses) are declared in `skills/loopany-core/kinds/*.md` and enforced by agent workflow; `artifact status` enforces the status machine but does not yet parse body sections in TypeScript. </Note> ## Anti-patterns | Don't | Do instead | |-------|------------| | Reflect on one task | Wait for threshold table | | Skip fresh-evidence filter | Union prior `evidence` fields first | | `skill-proposal` without learning | Create learning first | | Re-propose rejected change | Read `--status rejected` list | | Edit `SKILL.md` directly | Proposal → accept → commit | | Edit beyond proposal scope | Proposal body is the contract | | Accept without reading learning | Verify scope against evidence | | Run learning `checkAt` closure here | Use `loopany-review` daily + `followups` | ## Quick reference ```text REFLECT: list/get evidence → pattern? → learning → (optional) skill-proposal → user ACCEPT: get spr → refs → edit target → append Outcome → status accepted → git commit REJECT: get spr → append Outcome (reason) → status rejected ``` ## Related pages <CardGroup> <Card title="Self-improvement loop" href="/self-improvement-loop"> End-to-end model: task outcomes, beliefs, proposals, and the no-direct-skill-edits rule. </Card> <Card title="Self-improvement example" href="/self-improvement-example"> Worked recipe from three done tasks through accept and git diff. </Card> <Card title="Skills library" href="/skills-library"> Resolver routing and reflect skill placement in the pack. </Card> <Card title="Kinds and validation" href="/kinds-and-validation"> `learning` and `skill-proposal` schemas, status machines, indexed fields. </Card> <Card title="Artifact commands" href="/artifact-commands"> `create`, `append`, `status`, `--slug`, `--content-file` details. </Card> <Card title="Reference graph" href="/reference-graph"> `refs`, `trace`, relation verbs including `supersedes` and `cites`. </Card> <Card title="Periodic review" href="/periodic-review"> Daily `checkAt` and weekly feed into reflect; scope boundaries. </Card> </CardGroup> --- ## 13. Periodic review > Daily followups (--due today), weekly doctor + overdue sweep, monthly mission-drift checks, and closure rules for checkAt-bearing artifacts. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/13-periodic-review.md - Generated: 2026-06-05T19:00:54.642Z ### Source Files - `skills/loopany-review/SKILL.md` - `skills/loopany-review/references/daily.md` - `skills/loopany-review/references/weekly.md` - `skills/loopany-review/references/monthly.md` - `src/commands/followups.ts` - `src/commands/doctor.ts` --- title: "Periodic review" description: "Daily followups (--due today), weekly doctor + overdue sweep, monthly mission-drift checks, and closure rules for checkAt-bearing artifacts." --- Periodic review is implemented as the `loopany-review` skill pack (`skills/loopany-review/`) plus deterministic CLI queries (`loopany followups`, `loopany doctor`). The harness returns candidates; the agent classifies, surfaces only judgment-needed items, dispatches to per-kind playbooks, and must close every surfaced item before ending the session. ## Role in the loop | Layer | Responsibility | |-------|----------------| | `loopany-resolver` | Routes phrases like "what's due today", "weekly check", or "is this still the right mission?" to `loopany-review` | | `loopany-review` | One skill, three frequencies; shared classify → surface → dispatch → **close** gate | | `loopany followups` | Index scan: artifacts with `checkAt` on or before a cutoff date | | `loopany doctor` | Integrity checks (workspace, schema, kinds, artifacts, references, onboarding, warnings) | <Note> Review is judgment-heavy and runs inside an agent session. `INSTALL_FOR_AGENTS.md` treats bare shell cron as insufficient for weekly/monthly work; coding CLIs default to session-boundary prompts instead of durable cron registration. </Note> ## Cadence routing Do not mix scopes across frequencies — one day of overdue is not a weekly pattern; one week cannot prove mission drift. | Frequency | Scope | Typical trigger | Reference | |-----------|-------|-----------------|-----------| | **Daily** | `checkAt` due **today** (not overdue backlog) | Session start, "what's due today" | `skills/loopany-review/references/daily.md` | | **Weekly** | Overdue `checkAt`, `doctor`, parking-lot sweeps | "What's slipping", "weekly check", workspace health | `skills/loopany-review/references/weekly.md` | | **Monthly** | Active mission alignment, structural drift (domains/kinds) | "Right mission?", "anything structural?" | `skills/loopany-review/references/monthly.md` | ```mermaid flowchart TB subgraph triggers [Resolver triggers] T1["what's due today"] T2["weekly check / slipping"] T3["right mission? / structural?"] end subgraph skill [loopany-review] D[daily.md] W[weekly.md] M[monthly.md] U["Unified flow: classify → surface → dispatch → close"] end subgraph cli [Deterministic CLI] F["loopany followups"] DOC["loopany doctor"] AL["loopany artifact list/get/status/set"] end T1 --> D --> U T2 --> W --> U T3 --> M --> U D --> F W --> F W --> DOC M --> AL U --> AL ``` ## Unified review flow All frequencies share the same pipeline after frequency-specific queries. <Steps> <Step title="Query"> Run the commands from the daily, weekly, or monthly reference file. </Step> <Step title="Classify"> For each candidate: `loopany artifact get <id>`. - **A — Silently resolvable:** extend `checkAt` with a one-line reason (no user prompt). - **B — Needs the user:** judgment or data only the user has. - **C — Defer:** push `checkAt` forward **with a reason** ("not yet" is invalid). For `learning` artifacts, add context: `loopany trace <learning-slug> --direction backward`. </Step> <Step title="Surface"> Emit only bucket **B** items. One line each. If empty, say so and stop — quiet is correct. </Step> <Step title="Dispatch"> Route to kind playbooks under `skills/loopany-core/kinds/` (`task`, `learning`, `signal`, `mission`). </Step> <Step title="Close (gate)"> Every surfaced item must end as **resolved**, **deferred**, or **retired** before the session ends. A digest without state changes is noise. </Step> </Steps> ### Closure outcomes | Outcome | Meaning | Typical writes | |---------|---------|----------------| | **Resolved** | Question answered or work finished | `loopany artifact status`, required `## Outcome` on terminal task statuses; signal → `addressed` with `--addressed-by` | | **Deferred** | Revisit later with intent | `loopany artifact set <id> --field checkAt --value <YYYY-MM-DD>` plus reason in body or append | | **Retired** | No further scheduled check | Remove follow-up obligation (`checkAt` cleared or artifact reaches terminal status) with a note | ```mermaid stateDiagram-v2 [*] --> Surfaced: bucket B item Surfaced --> Resolved: status transition + evidence Surfaced --> Deferred: checkAt pushed with reason Surfaced --> Retired: checkAt removed or terminal status Resolved --> [*] Deferred --> [*] Retired --> [*] note right of Surfaced No zombie items: session must not end with open surfaced items end note ``` ## Daily review **Query** ```bash loopany followups --due today ``` **Rules** - Process only items whose `checkAt` date equals **today**. The CLI returns every artifact with `checkAt <= today`, which includes overdue rows — leave those for weekly (`--due overdue`). - At most once per day. - Empty result → report "nothing due today" and stop. **Example surface format** (from the skill; slugs are illustrative): ``` 3 things need you today: 1. [<task-slug>] 2-week recheck — did $/session settle? 2. [<learning-slug>] "short deals close 2.5x faster" — still true? 3. [<signal-slug>] recurring churn signal, 3rd time. Upgrade? ``` `learning` revalidation on `checkAt` belongs in daily review, not `loopany-reflect` (reflect looks forward; review looks back at scheduled checks). ## Weekly review **Query** ```bash loopany followups --due overdue loopany doctor --format json ``` Plus parking-lot lists (agent-run `artifact list`, not a separate CLI): | Parking lot | Command | Threshold | |-------------|---------|-----------| | Stalled tasks | `artifact list --kind task --status running` | ≥ 14 days, no recent append | | Stale signals | `artifact list --kind signal --status open` | ≥ 7 days, no action | | Pending proposals | `artifact list --kind skill-proposal --status pending` | any → mention; > 5 → nudge | **Doctor handling** Summarize by category and propose fixes. Do **not** auto-fix — failures may reflect an in-progress decision. Stalled `running` tasks should move to `in_review`, `failed`, or `cancelled` with `## Outcome`. "Still working on it" is not closure. **Feed reflect** If the weekly pass resolves ≥ 3 items, suggest `loopany-reflect` (pattern extraction is reflect's job, not doctor's). ## Monthly review **Query** ```bash loopany artifact list --kind mission --status active ``` Plus recent tasks (~30 days) for alignment math. **Mission alignment** (from `skills/loopany-core/kinds/mission.md`): ``` alignment = tasks mentioning this mission / total recent tasks ``` | Alignment | Action | |-----------|--------| | ≥ 60% | Healthy | | 30–60% | Partial drift — backfill `mentions` or add a second mission? | | < 30% | Clear drift — propose re-onboarding | Monthly surfaces hypotheses; the user decides. Do not auto-abandon missions. **Structural drift — domains** (all three required before proposing a `[change]` task): 1. ≥ 5 artifacts tagged with an unenabled domain 2. ≥ 2 weeks of accumulation 3. Scope distinguishable from existing domains **Structural drift — kinds** (both required): 1. ≥ 3 `note` artifacts sharing a body skeleton for ≥ 2 weeks 2. Passes the four-question kind test in `skills/loopany-core/conventions/taxonomy.md` Forward-only: propose new structure; do not migrate existing artifacts in review. ## `checkAt` and `loopany followups` `checkAt` is optional frontmatter on kinds that index it (`task`, `learning`, `skill-proposal`, …). Set it only with a concrete future question — missing `checkAt` is better than a date you will ignore. The in-memory index (`src/core/index.ts`) selects artifacts where `checkAt` (YYYY-MM-DD prefix) is on or before the cutoff date. `src/commands/followups.ts` adds filters: <ParamField body="--due" type="today | overdue | next-7d" default="today"> Cutoff for the index scan. `next-7d` extends the cutoff seven days ahead (implementation supports it; `--help` lists `today` and `overdue` only). </ParamField> <ParamField body="--include-done" type="true"> When `true`, includes artifacts whose `status` has no outgoing transitions in the kind status machine (e.g. `done`, `cancelled`, `failed` on tasks). </ParamField> <ParamField body="--domain" type="string"> Restricts results to matching `frontmatter.domain`. </ParamField> | `--due` value | Index cutoff | Extra filter | |---------------|--------------|--------------| | `today` (default) | `checkAt <= today` | None | | `overdue` | `checkAt <= today` | `checkAt` date **strictly before** today | | `next-7d` | `checkAt <= today + 7 days` | None | **Output:** JSON array of `ArtifactMeta` on stdout (same as other query commands). **Create / defer examples** ```bash loopany artifact create --kind task --title "Recheck API rate limits" \ --status todo --check-at 2026-06-12 loopany artifact set <id> --field checkAt --value 2026-07-01 ``` E2E coverage: `test/cli.e2e.test.ts` (`loopany followups` describes terminal-status filtering); lifecycle scenario in `test/scenario.e2e.test.ts` (signal → task with `checkAt` → `followups --due today` → `done`). ## `loopany doctor` Doctor is integrity-only: deterministic checks, no semantic body scans (TODO hunting belongs in reflect). | Check | Pass | Fail / warn behavior | |-------|------|----------------------| | `workspace` | Bootstrap path + kinds dir | — | | `schema version` | `config.schemaVersion` matches binary `SCHEMA_VERSION` | Fail; message points to `loopany migrate` | | `kinds` | All kind defs parse | Fail lists broken files | | `artifacts` | Every frontmatter passes kind Zod schema | Fail lists invalid IDs | | `references` | No dangling `from`/`to` | Fail lists broken edges | | `onboarding` | `self` person + ≥1 active `mission` | Fail with onboarding hint | | `mission coverage` | (warn) every `task` mentions a mission | Warn lists orphans | | `domain coverage` | (warn) artifact `domain` ∈ `enabled_domains` | Warn lists unenabled domains | <CodeGroup> ```bash title="Human-readable" loopany doctor ``` ```bash title="JSON (weekly review)" loopany doctor --format json ``` </CodeGroup> Exit code is non-zero when any check has `status: fail`. Warnings do not fail the run. Version check is bypassed during doctor bootstrap so a mismatch is reported instead of crashing early. ## Boundaries and anti-patterns | Mistake | Why it fails | |---------|----------------| | Daily pass processes overdue backlog | Steals weekly scope; skill explicitly forbids | | Weekly pass re-runs daily due-today | Nothing should stagnate in 24h at weekly granularity | | Monthly pass duplicates weekly parking lots | One week cannot prove structural drift | | Defer without reason | Items rot silently | | Surface without close | Worse than no digest | | Dump raw `followups` JSON | Agent job is judgment, not relay | | Run reflect for due `learning` checks | Scheduled revalidation is daily review | ## Registering cadence On install, agents pick one path silently (`INSTALL_FOR_AGENTS.md`): - **Durable cron hosts (Hermes, OpenClaw, …):** register `loopany-review` (and `loopany-reflect` weekly) on schedule inside agent sessions; record job IDs for audit. - **Coding CLIs (default):** do not register cron (e.g. Claude Code `CronCreate` expires ~7 days). Instead: session start → daily `followups`; week-end → weekly doctor + overdue; month-end → mission review. The user hears a single plain-language cadence sentence at onboarding close — not host-internals menus. ## Related pages <CardGroup> <Card title="Graph, search, and scheduling" href="/graph-search-commands"> `followups` flags, JSON output, and terminal-status filtering in full command context. </Card> <Card title="Doctor and troubleshooting" href="/doctor-and-troubleshooting"> Per-check failures, exit codes, and recovery alongside `migrate` / `reindex`. </Card> <Card title="Reflect workflow" href="/reflect-workflow"> Weekly feed when ≥3 resolutions; learnings and skill-proposals — not daily `checkAt` revalidation. </Card> <Card title="Self-improvement loop" href="/self-improvement-loop"> `checkAt` on accepted skill-proposals and learning beliefs that daily/weekly review closes. </Card> <Card title="Workspace setup" href="/workspace-setup"> `loopany init`, onboarding, and cadence registration expectations after install. </Card> <Card title="Skills library" href="/skills-library"> Resolver routing table and cross-skill chaining (review → core, review → reflect). </Card> </CardGroup> --- ## 14. Schema migration > schemaVersion vs binary VERSION, SchemaVersionMismatchError recovery, loopany migrate discovery, and v0.1.0→v0.2.0 script-driven workspace transforms. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/14-schema-migration.md - Generated: 2026-06-05T19:00:35.872Z ### Source Files - `src/version.ts` - `src/core/engine.ts` - `src/commands/migrate.ts` - `skills/migrations/v0.1.0-to-v0.2.0/SKILL.md` - `skills/migrations/README.md` - `test/migration-v0.1-to-v0.2.e2e.test.ts` - `test/migration-framework.test.ts` --- title: "Schema migration" description: "schemaVersion vs binary VERSION, SchemaVersionMismatchError recovery, loopany migrate discovery, and v0.1.0→v0.2.0 script-driven workspace transforms." --- Every `loopany` command except `migrate` and `doctor` calls `bootstrap()` in `src/core/engine.ts`, which compares `$LOOPANY_HOME/config.yaml` `schemaVersion` to the compile-time `SCHEMA_VERSION` constant in `src/version.ts`. A mismatch throws `SchemaVersionMismatchError` and blocks artifact, graph, and search operations until you run the Bun scripts under `skills/migrations/v<from>-to-v<to>/` — the CLI only discovers and documents those scripts; it does not execute them. ## Two independent version axes | Identifier | Location | Meaning | | --- | --- | --- | | `VERSION` | `src/version.ts` | Semver of the **binary** (`loopany --version`). Bumps on any release. | | `SCHEMA_VERSION` | `src/version.ts` | On-disk **workspace format** the binary expects. Bumped only when kinds, frontmatter, or references change in a way that needs migration. | | `schemaVersion` | `$LOOPANY_HOME/config.yaml` | The workspace’s **claimed** format version. | Both `VERSION` and `SCHEMA_VERSION` are currently `0.2.0`, but they are documented to drift independently: you can ship CLI `0.2.1` without changing workspace layout, or bump `SCHEMA_VERSION` to `0.3.0` and ship a migration skill without a binary feature release. <Note> `src/core/search-store.ts` defines a separate SQLite `SCHEMA_VERSION` for `search.db`. That index schema is unrelated to workspace `config.yaml#schemaVersion`. </Note> ### Legacy default If `config.yaml` exists but omits `schemaVersion`, `Config.load()` assumes `0.1.0` (`ASSUMED_LEGACY_VERSION` in `src/core/config.ts`). Pre-framework workspaces never wrote the field. `loopany init` always writes the current `SCHEMA_VERSION` when it creates a new config file; it does not overwrite an existing `config.yaml`. ## Bootstrap guard and recovery ```mermaid stateDiagram-v2 [*] --> Stale: schemaVersion ≠ SCHEMA_VERSION Stale --> Blocked: bootstrap() without skip Blocked --> MismatchError: SchemaVersionMismatchError MismatchError --> Discover: loopany migrate Discover --> RunScripts: bun run skills/migrations/.../scripts/*.ts --apply RunScripts --> Current: schemaVersion = SCHEMA_VERSION Current --> [*]: normal CLI commands succeed Stale --> DoctorReport: loopany doctor (skipVersionCheck) DoctorReport --> Discover: schema version check fails with migrate hint ``` On mismatch, `SchemaVersionMismatchError` embeds the workspace version, the binary’s expected version, the path to the matching migration skill, and the suggested CLI name: ``` Workspace schema is v0.1.0 but this binary expects v0.2.0. Read `skills/migrations/v0.1.0-to-v0.2.0/SKILL.md` and run `loopany migrate v0.1.0-to-v0.2.0`. ``` `src/cli.ts` catches this error class, prints the message to stderr, and exits with code `1` — the same path used for `WorkspaceNotFoundError` and Zod validation failures. ### Commands that bypass the guard | Surface | Bypass mechanism | | --- | --- | | `loopany migrate` | `bootstrap({ skipVersionCheck: true })` | | `loopany doctor` | `bootstrap({ skipVersionCheck: true })` — reports mismatch as a **fail** check instead of crashing | | Migration scripts | Set `LOOPANY_SKIP_VERSION_CHECK=1` in the environment (also honored by `bootstrap`) | <Warning> Do not leave `LOOPANY_SKIP_VERSION_CHECK=1` in your shell profile. It disables the guard for every command in that session. </Warning> After migration completes and `schemaVersion` matches `SCHEMA_VERSION`, regular commands (`artifact list`, `refs`, `search`, etc.) bootstrap normally again. ## `loopany migrate` — discovery only `src/commands/migrate.ts` scans `skills/migrations/` next to the repo’s `skills/loopany-core/`. Directory names must match `v<semver>-to-v<semver>` (full three-part semver, e.g. `v0.1.0-to-v0.2.0`). | Invocation | Behavior | | --- | --- | | `loopany migrate` | Prints workspace `schemaVersion`, binary `SCHEMA_VERSION`, all known migrations, and **next** migration where `from` equals the workspace version | | `loopany migrate <name>` | Prints migration metadata, ordered `scripts/*.ts` paths, and the full `SKILL.md` body | The command **does not** run migration logic. Scripts stay standalone so you can dry-run, apply step-by-step, inspect intermediate disk state, and abort without the binary partially mutating runtime state through coupled `--run` flags. <RequestExample> ```bash loopany migrate ``` </RequestExample> <ResponseExample> ```text Workspace schema: v0.1.0 Binary expects: v0.2.0 Next migration: v0.1.0-to-v0.2.0 loopany migrate v0.1.0-to-v0.2.0 Available migrations (1): v0.1.0-to-v0.2.0 ``` </ResponseExample> On an up-to-date workspace, list output includes `Up to date — no migration needed.` If the workspace version has no matching `from` directory, you get `No migration found from v…` (file an issue — the binary should ship a chain for every supported jump). ## Migration pack layout :::files skills/migrations/ README.md v<from>-to-v<to>/ SKILL.md scripts/ 01-<step>.ts ... 05-bump-version.ts 06-refresh-kinds.ts ::: Authoring rules from `skills/migrations/README.md`: - **One migration = one minor version jump** — chain `v0.1.0-to-v0.2.0` then `v0.2.0-to-v0.3.0` instead of multi-hop scripts. - **Scripts are standalone** — no imports from `src/core/`; they parse the **old** on-disk format the current binary may no longer understand. - **Dry-run by default** — pass `--apply` to commit writes. - **Idempotent** — reruns should be no-ops once the workspace is at the target shape. - **Workspace-rooted** — read `LOOPANY_HOME` (default `~/loopany`), not the process cwd. - **Final step must bump `schemaVersion`** — e.g. `05-bump-version.ts` writes `0.2.0` to `config.yaml`. The skill (`SKILL.md`) carries rationale, pre-flight, ordered steps, rollback, and verification. The CLI is an index; the agent (or operator) runs `bun run` on each script. ## v0.1.0 → v0.2.0 transforms The shipped migration `skills/migrations/v0.1.0-to-v0.2.0/` is the first major workspace simplification: slug-as-ID storage, flat `artifacts/<dirName>/` paths, camelCase frontmatter, auto timestamps, and a `journal` kind as the time spine. | Area | v0.1.0 | v0.2.0 | | --- | --- | --- | | Kind defs | `idPrefix`, `bodyMode`, `storage`, `idStrategy` | Optional `slugLayout`; slugs are global IDs | | Artifact paths | `artifacts/{YYYY-MM}/` for date-bucketed kinds | `artifacts/<dirName>/<slug>.md` | | IDs | Prefixed (`tsk-20260428-091500`) | Bare slug (`20260428-091500`) | | Frontmatter | snake_case (`check_at`) | camelCase (`checkAt`) + `createdAt` / `updatedAt` | | Provenance | — | `_backfilled: true` on migrated rows | | Time index | Implicit in paths | `artifacts/journal/<YYYY>/<YYYY-MM-DD>.md` backfilled from `createdAt` | | Graph | `references.jsonl` with old IDs | Rewritten via `.migration-id-map.json` | Crewlet-specific kinds and fields are intentionally **not** migrated — only loopany core shapes. ### Script pipeline | Step | Script | Role | | --- | --- | --- | | 1 | `01-snapshot.ts` | Read-only counts, collision detection (exit `1` if two old IDs map to the same slug) | | 2 | `02-transform-artifacts.ts` | Rewrite frontmatter, flatten paths, rewrite `[[wiki-links]]`, write `.migration-id-map.json` | | 3 | `03-rebuild-references.ts` | Rewrite `references.jsonl` using the id map; keeps `references.jsonl.v0.1.bak` | | 4 | `04-build-journal.ts` | One journal file per distinct `createdAt` date | | 5 | `05-bump-version.ts` | Sets `config.yaml` `schemaVersion: 0.2.0` — **gate** for bootstrap | | 6 | `06-refresh-kinds.ts` | Overwrites `kinds/*.md` from bundled v0.2 defs (`init` never overwrites existing kind files) | Shared helpers live in `scripts/_lib.ts` and intentionally duplicate v0.1 parsing logic. <Steps> <Step title="Pre-flight snapshot"> ```bash cd $LOOPANY_HOME git init -q 2>/dev/null || true git add -A && git commit -q -m "pre-migration snapshot v0.1.0" || true loopany doctor > /tmp/loopany-pre-migration-doctor.txt 2>&1 || true ``` Fix any existing `doctor` **fail** checks before migrating — scripts assume a healthy v0.1 workspace. </Step> <Step title="Discover migration"> ```bash loopany migrate loopany migrate v0.1.0-to-v0.2.0 # prints SKILL.md + script list ``` </Step> <Step title="Run scripts (dry-run, then apply)"> ```bash SKILL=skills/migrations/v0.1.0-to-v0.2.0/scripts export LOOPANY_HOME=~/loopany export LOOPANY_SKIP_VERSION_CHECK=1 bun run $SKILL/01-snapshot.ts bun run $SKILL/02-transform-artifacts.ts bun run $SKILL/02-transform-artifacts.ts --apply bun run $SKILL/03-rebuild-references.ts --apply bun run $SKILL/04-build-journal.ts --apply bun run $SKILL/05-bump-version.ts --apply bun run $SKILL/06-refresh-kinds.ts --apply ``` </Step> <Step title="Verify"> ```bash unset LOOPANY_SKIP_VERSION_CHECK loopany doctor loopany artifact list --kind task loopany artifact list --kind journal ``` Expect `schema version: ok`, no `tsk-`/`sig-` prefixes on IDs, flat `artifacts/` dirs (no `{YYYY-MM}/` buckets), and journal entries under `artifacts/journal/<year>/`. </Step> </Steps> ### Slug collisions If step 02 would assign the same slug to two artifacts (e.g. `mis-self` and `prs-self` both → `self`), `01-snapshot.ts` fails loud. Rename one source file before `--apply`, then rerun from snapshot. ### Rollback ```bash cd $LOOPANY_HOME git reset --hard HEAD rm -f .migration-id-map.json ``` All migration mutations stay inside the workspace tree; the pre-flight git commit is the primary recovery path. <Info> `loopany init` is idempotent for existing files — it will not refresh stale `kinds/*.md`. Step `06-refresh-kinds.ts` exists precisely because post-migration kind defs must match v0.2 validators. </Info> ## Authoring a future migration When changing on-disk format: 1. Bump `SCHEMA_VERSION` in `src/version.ts` and add `skills/migrations/v<from>-to-v<to>/` with `SKILL.md` + numbered scripts. 2. Use **minor** bumps for additive or transformable changes; reserve **major** for breaks no script can bridge. 3. End with a script that calls `config.setSchemaVersion('<to>')` or equivalent YAML write. 4. Add `test/migration-*.e2e.test.ts` that builds a fixture old workspace, runs scripts via `bun run` with `LOOPANY_HOME` set, and asserts `loopany doctor` passes. There are **no backwards-compat shims** in the binary for old formats — migration is the only supported forward path once a new `SCHEMA_VERSION` ships. ## Test coverage | Test file | What it proves | | --- | --- | | `test/migration-framework.test.ts` | `schemaVersion` load/default/persist, init writes version, bootstrap guard, `LOOPANY_SKIP_VERSION_CHECK`, `migrate` list/describe, doctor surfaces mismatch | | `test/migration-v0.1-to-v0.2.e2e.test.ts` | Full six-script pipeline on a synthetic v0.1 workspace; id map, paths, camelCase, refs rewrite, journal backfill, kind refresh, doctor clean, CLI unblocked | ## Related pages <CardGroup> <Card title="Configuration reference" href="/configuration-reference"> `schemaVersion`, `LOOPANY_HOME`, and `LOOPANY_SKIP_VERSION_CHECK` in config and environment. </Card> <Card title="CLI reference" href="/cli-reference"> `migrate` and `doctor` flags, JSON output, and command inventory. </Card> <Card title="Artifacts and workspace" href="/artifacts-and-workspace"> v0.2 slug-as-id layout, `artifacts/<dirName>/`, and journal storage after migration. </Card> <Card title="Doctor and troubleshooting" href="/doctor-and-troubleshooting"> Schema version check in doctor output and recovery when commands refuse to run. </Card> <Card title="Workspace setup" href="/workspace-setup"> Fresh `loopany init` writes current `schemaVersion` without migrating. </Card> </CardGroup> --- ## 15. CLI reference > Full command surface from loopany --help: init, artifact/*, refs, trace, domain, followups, search, reindex, factory, kind list, doctor, migrate, and JSON stdout conventions. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/15-cli-reference.md - Generated: 2026-06-05T19:01:26.703Z ### Source Files - `src/cli.ts` - `src/version.ts` - `src/core/operations.ts` - `src/commands/argv.ts` - `test/cli.e2e.test.ts` - `test/helpers/cli.ts` --- title: "CLI reference" description: "Full command surface from loopany --help: init, artifact/*, refs, trace, domain, followups, search, reindex, factory, kind list, doctor, migrate, and JSON stdout conventions." --- The `loopany` binary (currently **0.2.0**, workspace schema **0.2.0**) is a Bun entrypoint at `src/cli.ts` that dispatches hand-parsed argv to command modules, bootstraps `$LOOPANY_HOME` (default `~/loopany`), and prints **pretty-printed JSON** on stdout for most mutating and query operations. Human prose is reserved for `init`, default `doctor`, `migrate`, and the long-running `factory` server. ```text loopany [--help|-h] [--version|version] init kind list artifact create|get|list|append|status|set refs <id> | refs add trace <id> followups domain list|enable|disable search <query> | reindex factory doctor | migrate [<name>] ``` <Note> There is no global `--json` flag. JSON vs text is per command (see [Stdout conventions](#stdout-conventions)). Flags always take a value (`--flag value`); exceptions are bare switches on `reindex` (`--force`, `--no-embed`) and `factory` (`--no-open`). </Note> ## Runtime and workspace | Input | Effect | | --- | --- | | `LOOPANY_HOME` | Workspace root; default `~/loopany` | | `LOOPANY_SKIP_VERSION_CHECK=1` | Skips `config.yaml` `schemaVersion` guard (migration scripts) | | `LOOPANY_EDITOR` | Used by factory UI when opening artifact files (not by core CLI) | Bootstrap (`bootstrap()` in `src/core/engine.ts`) requires `kinds/` under the workspace. If `schemaVersion` ≠ binary `SCHEMA_VERSION` and the skip flag is unset, commands fail with `SchemaVersionMismatchError` and a pointer to `loopany migrate`. `doctor` and `migrate` bypass that guard so they can report or plan upgrades. Every completed invocation appends one line to `audit.jsonl` with `op` (dotted, e.g. `artifact.create`), `actor: cli`, `duration_ms`, and command-specific metadata; failures include `error`. Body content is never logged. ## Argv parsing `src/commands/argv.ts` implements a minimal parser: positional tokens first, then repeating `--key value` pairs. Unknown flags on `artifact create` produce a field list derived from the kind definition. Kebab-case flags map to camelCase frontmatter (`--check-at` → `checkAt`). Comma-separated values become `string[]` (`--mentions alice,bob`). ## Command reference ### `loopany init` Scaffolds the workspace: `kinds/`, `artifacts/`, `config.yaml` with current `schemaVersion`, and copies bundled kinds from `skills/loopany-core/kinds/`. Idempotent — existing files are left untouched. | Stream | Shape | | --- | --- | | stdout | Human lines: path, created count, optional ONBOARDING hint | | stderr | — | | exit | `0` | Onboarding hint appears when no `artifacts/missions/*.md` exists. ### `loopany kind list` | Stream | Shape | | --- | --- | | stdout | JSON array of `{ kind, dirName, slugLayout, indexedFields, hasStatusMachine }` | | exit | `0`; non-zero if workspace missing | Domain pack kinds appear only when the domain is enabled (`domain enable`). ### Artifact commands All artifact subcommands except `get` (default) emit JSON. Per-kind fields come from `~/loopany/kinds/<kind>.md`; reserved flags on create: `--kind`, `--slug`, `--domain`, `--content`, `--content-file`. #### `artifact create` ``` loopany artifact create --kind <K> [--slug <id>] [--domain <D>] [--content <text> | --content-file <path|->] [--<field> <value> ...] ``` <ParamField body="--kind" type="string" required> Kind name; must exist in the loaded registry. </ParamField> <ParamField body="--slug" type="string"> Stable id for `[[citations]]`. If omitted: slugified `--title` with collision suffixes, or timestamp-based id when no title. </ParamField> <ParamField body="--content / --content-file" type="string"> Body text. `-` on `--content-file` reads stdin. Inline `--content` rejects literal `\n` without real newlines (shell footgun guard). </ParamField> <ResponseField name="stdout" type="object"> `{ "id", "kind", "path" }` — path is the on-disk markdown file. </ResponseField> <Warning> `--kind journal` is rejected; journal entries are auto-managed when other artifacts are created. </Warning> #### `artifact get <id>` <ParamField body="--format" type="md | json" default="md"> `md`: raw file contents. `json`: `{ id, kind, path, frontmatter, body, audit }` where `audit` lists mutation ops for this id (timestamps as local `YYYY-MM-DD HH:MM`, no `actor`/`duration_ms`). </ParamField> #### `artifact list` ``` loopany artifact list [--kind K] [--status S] [--domain D] [--<field> V ...] [--contains Q] ``` <ResponseField name="stdout" type="array"> `[{ id, kind, path, frontmatter }, ...]`. With `--kind`, extra `--field` filters use the per-kind index when declared in `indexedFields`; otherwise linear scan. `--contains` is case-insensitive on body and string/`string[]` frontmatter; applied last. </ResponseField> #### `artifact append <id>` Requires `--section` and (`--content` or `--content-file`). Returns `{ "id" }`. #### `artifact status <id> <new-status>` <ParamField body="--reason" type="string"> Recorded in audit, not appended to the artifact body. </ParamField> <ParamField body="--addressed-by" type="string"> Required when transitioning to `addressed`; emits an `addresses` edge from that id to this artifact. </ParamField> <ResponseField name="stdout" type="object"> `{ id, status, reason?, addressedBy?, edgesEmitted }`. </ResponseField> Status transitions are enforced by each kind’s status machine in its kind file. #### `artifact set <id>` ``` loopany artifact set <id> --field <name> --value <value> ``` Updates one non-status frontmatter field. Use `artifact status` for `status`. <ResponseField name="stdout" type="object"> `{ id, field, value }`. </ResponseField> ### Graph commands #### `refs <id>` BFS over the in-memory graph (persisted `references.jsonl` plus implicit edges from `mentions` frontmatter and body `[[id]]` links). | Flag | Default | Meaning | | --- | --- | --- | | `--direction` | `out` | `in`, `out`, or `both` | | `--depth` | `1` | Positive integer hop count | | `--relation` | — | Filter by relation verb | | `--domain` | — | Both endpoints must have this `domain` frontmatter | <ResponseField name="stdout" type="array"> `Edge[]`: `{ ts, from, to, relation, actor, implicit? }`. </ResponseField> #### `refs add` ``` loopany refs add --from <id> --to <id> --relation <R> ``` Appends one row to `references.jsonl`. Returns the new edge object. #### `trace <id>` Walks causal predicates to a fixed point (not plain BFS depth). Default relations: `led-to`, `addresses`, `supersedes`, `follows-up`, `cites` (`mentions` excluded). | Flag | Default | | --- | --- | | `--direction` | `both` (`forward`, `backward`, `both`) | | `--relations` | default set (comma-separated override) | | `--max-depth` | unlimited | <ResponseField name="stdout" type="object"> `{ root, nodes: [{ ...ArtifactMeta, distance }], edges: Edge[] }` — `distance` negative = causes, `0` = root, positive = effects; nodes sorted by distance. </ResponseField> ### `followups` ``` loopany followups [--due today|overdue|next-7d] [--include-done true] [--domain D] ``` <ResponseField name="stdout" type="array"> `ArtifactMeta[]` with `checkAt` due per mode. Terminal statuses (no outgoing transitions in the kind machine) are excluded unless `--include-done true`. </ResponseField> ### Domain commands | Command | stdout | | --- | --- | | `domain list` | `{ enabled: string[], observed_only: string[] }` — observed = domains seen on artifacts but not in `config.yaml` | | `domain enable <name>` | `{ enabled: string[] }` | | `domain disable <name>` | `{ enabled: string[] }` | Enabling loads `domains/<name>/kinds/*.md` into the registry on next bootstrap. ### Search and index #### `reindex` ``` loopany reindex [--force] [--no-embed] ``` Rebuilds `$LOOPANY_HOME/search.db` from all artifacts. Incremental by file mtime unless `--force` (deletes DB first). `--no-embed` uses FTS5-only (`embedder: "noop"`). <ResponseField name="stdout" type="object"> `{ indexed, skipped, removed, embedder: "transformers" | "noop" }`. </ResponseField> Artifact writes do not auto-reindex; run `reindex` after bulk edits. #### `search <query>` ``` loopany search <query> [--kind K] [--domain D] [--status S] [--limit N] ``` Default `--limit` is `10`. Hybrid FTS5 + embeddings when the embedder is available. <ResponseField name="stdout" type="array"> `SearchResult[]`: `{ artifactId, kind, domain, status, path, section, snippet, score }`. </ResponseField> If `search.db` is missing: exit `0`, stdout `[]`, stderr warns to run `reindex` first. ### `factory` ``` loopany factory [--port N] [--host H] [--no-open] ``` Starts the pixel-factory UI (default port **4242**, host **127.0.0.1**). Prints URL and workspace path; opens the browser unless `--no-open`. Runs until SIGINT/SIGTERM — no JSON stdout. ### `doctor` ``` loopany doctor [--format json] ``` Checks: workspace, schema version, kinds parse, artifact frontmatter validation, reference integrity (no dangling edges), onboarding (`self` person + active mission), mission coverage (warn), domain coverage (warn). | `--format` | stdout | exit | | --- | --- | --- | | (default) | Human checklist (`✓` / `⚠` / `✗`) | `1` if any check `fail` | | `json` | `{ workspace, checks: [{ name, status, detail, problems? }], ok }` | same | Schema version mismatch is reported as `fail` with `loopany migrate …` in `problems`. ### `migrate` ``` loopany migrate loopany migrate <name> # e.g. v0.1.0-to-v0.2.0 ``` Does **not** run migration scripts — discovery and documentation only. Scripts live under `skills/migrations/v<from>-to-v<to>/scripts/` and are run with `bun` by the agent. | Invocation | stdout | | --- | --- | | no args | Human: workspace vs binary schema, next migration hint, available list | | `<name>` | Human: migration metadata, script paths, full `SKILL.md` body | Uses `bootstrap({ skipVersionCheck: true })`. ### Version `loopany --version` or `loopany version` → `loopany 0.2.0\n` (plain text). ## Stdout conventions ```mermaid flowchart LR subgraph human["Human stdout"] init[init] doctorH[doctor default] migrate[migrate] factory[factory server] version[--version] end subgraph json["JSON stdout pretty-printed"] kinds[kind list] artifacts[artifact mutators + list] graph[refs trace followups] domains[domain *] searchCmd[search reindex] doctorJ[doctor --format json] getJ[artifact get --format json] end cli[src/cli.ts dispatch] --> human cli --> json ``` | Pattern | Commands | | --- | --- | | `JSON.stringify(x, null, 2) + '\n'` | `kind list`, `artifact` (except default `get`), `refs`, `trace`, `followups`, `domain *`, `search`, `reindex` | | Raw markdown | `artifact get` (default) | | Human prose | `init`, `migrate`, default `doctor`, `factory`, `--version` | | stderr only (success) | `search` when index missing | <RequestExample> ```bash LOOPANY_HOME=/tmp/brain loopany artifact create \ --kind task --title "Follow up" --status todo --priority high ``` </RequestExample> <ResponseExample> ```json { "id": "follow-up", "kind": "task", "path": "/tmp/brain/artifacts/tasks/follow-up.md" } ``` </ResponseExample> ## Errors and exit codes | Error type | stderr | exit | | --- | --- | --- | | `WorkspaceNotFoundError` | `No loopany workspace at … Run loopany init` | `1` | | `SchemaVersionMismatchError` | Version mismatch + migrate hint | `1` | | `ZodError` | `Invalid input:` with `path: message` lines (not raw Zod JSON) | `1` | | Other `Error` | `error.message` | `1` | | `doctor` with failing checks | Human or JSON report | `1` | | Unknown command | `Unknown command` + help hint | `1` | Failed operations still append an audit row with `error`. ## Invocation examples <CodeGroup> ```bash title="Development" bun run src/cli.ts init LOOPANY_HOME=~/loopany bun run src/cli.ts doctor ``` ```bash title="Compiled binary" bun run build ./bin/loopany init loopany kind list ``` </CodeGroup> ```bash # Graph + scheduling loopany refs add --from task-a --to task-b --relation led-to loopany trace task-a --direction forward --max-depth 3 loopany followups --due overdue # Search pipeline loopany reindex --no-embed loopany search "billing refactor" --kind note --limit 5 ``` ## Related pages <CardGroup> <Card title="Artifact commands" href="/artifact-commands"> Per-subcommand flags, kind fields, body input, and Outcome rules. </Card> <Card title="Graph, search, and scheduling" href="/graph-search-commands"> refs/trace depth, followups modes, reindex and factory details. </Card> <Card title="Configuration reference" href="/configuration-reference"> `config.yaml`, `LOOPANY_HOME`, schema version guard. </Card> <Card title="Doctor and troubleshooting" href="/doctor-and-troubleshooting"> Check list, common errors, index recovery. </Card> <Card title="Schema migration" href="/schema-migration"> `loopany migrate` and v0.1→v0.2 scripts. </Card> <Card title="Quickstart" href="/quickstart"> First session command sequence. </Card> </CardGroup> --- ## 16. Artifact commands > create/get/list/append/status/set flags, per-kind --field validation, --slug and --content-file, journal auto-management, and required ## Outcome on terminal task statuses. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/16-artifact-commands.md - Generated: 2026-06-05T19:01:29.843Z ### Source Files - `src/commands/artifact-create.ts` - `src/commands/artifact-get.ts` - `src/commands/artifact-list.ts` - `src/commands/artifact-append.ts` - `src/commands/artifact-status.ts` - `src/commands/artifact-set.ts` - `src/commands/body-input.ts` - `test/artifact-store.test.ts` --- title: "Artifact commands" description: "create/get/list/append/status/set flags, per-kind --field validation, --slug and --content-file, journal auto-management, and required ## Outcome on terminal task statuses." --- `loopany artifact` is the single namespace for every artifact kind: create, read, list, append body sections, transition status, and patch frontmatter fields. Commands delegate to `ArtifactStore` (markdown files under `$LOOPANY_HOME/artifacts/`) and the kind registry (Zod frontmatter + status machines from `kinds/*.md`). Mutating commands emit JSON on stdout; `artifact get` defaults to raw markdown. ## Command surface | Command | Positional args | Primary flags | stdout | |---|---|---|---| | `artifact create` | — | `--kind`, `--slug`, `--domain`, `--content` \| `--content-file`, per-kind `--<field>` | `{ id, kind, path }` | | `artifact get` | `<id>` | `--format md` (default) \| `json` | Markdown file or JSON object | | `artifact list` | — | `--kind`, `--status`, `--domain`, `--contains`, per-kind filters | JSON array of `{ id, kind, path, frontmatter }` | | `artifact append` | `<id>` | `--section`, `--content` \| `--content-file` | `{ id }` | | `artifact status` | `<id> <new-status>` | `--reason`, `--addressed-by` | `{ id, status, reason?, addressedBy?, edgesEmitted }` | | `artifact set` | `<id>` | `--field`, `--value` | `{ id, field, value }` | Every flag takes exactly one value (no boolean shorthand). Kebab-case flags map to camelCase frontmatter keys (`--check-at` → `checkAt`). <Note> `loopany --help` shows `artifact set <id> --<field> <value>`, but the implementation requires `artifact set <id> --field <name> --value <value>`. </Note> ## `artifact create` Creates a new markdown artifact, validates frontmatter against the kind’s Zod schema, allocates a globally unique slug id, stamps `createdAt` / `updatedAt`, and (for non-journal kinds) best-effort appends a wiki-link into today’s journal. <ParamField body="--kind" type="string" required> Kind name (e.g. `task`, `signal`, `person`). Must exist in the loaded kind registry. </ParamField> <ParamField body="--slug" type="string"> Explicit artifact id used in `[[wiki-links]]` and graph edges. Validated (1–60 codepoints; Unicode letters/digits, `-`, `_`; no leading/trailing separators; no `..` path tricks). Must be globally unique across all kinds. </ParamField> <ParamField body="--domain" type="string"> Cross-kind scope tag. Not validated against a kind field spec; written as built-in frontmatter. </ParamField> <ParamField body="--content" type="string"> Initial body (inline). Mutually exclusive with `--content-file`. </ParamField> <ParamField body="--content-file" type="path | -"> File path or `-` for stdin. Preserves real newlines; preferred for multi-line bodies. </ParamField> ### Per-kind field flags Any flag that is not reserved is treated as a frontmatter field for the given `--kind`: - Unknown flags → error listing valid fields for that kind (type, required, enum values, defaults). - Types: `string`, `enum`, `date`, `number`, `bool`, `string[]` (comma-separated or JSON array for `string[]` on `artifact set`; create uses comma-split). Reserved flags (never written as frontmatter): `--kind`, `--slug`, `--domain`, `--content`, `--content-file`. If the kind has a status machine and `--status` is omitted, the store sets `status` to the machine’s `initial` value before validation. <RequestExample> ```bash loopany artifact create --kind task \ --slug q2-cache-spike \ --title "Investigate cache thrash" \ --status todo \ --priority high \ --domain ads \ --content-file body.md ``` </RequestExample> <ResponseExample> ```json { "id": "q2-cache-spike", "kind": "task", "path": "/Users/you/loopany/artifacts/tasks/q2-cache-spike.md" } ``` </ResponseExample> ### Slug allocation order When `--slug` is omitted: 1. Slugify `--title` (Unicode-aware lowercase, punctuation collapsed to `-`; collisions get `-2`, `-3`, …). 2. If title is missing or slugifies to empty → timestamp id `YYYYMMDD-HHMMSS-<3hex>`. Explicit `--slug` always wins over a title-derived id. ### Journal rejection `--kind journal` is rejected at the CLI: ```text Cannot create journal artifacts directly — they are auto-managed by the store. ``` ## Body input (`--content` / `--content-file`) Shared by `artifact create` and `artifact append` via `resolveBody`: | Source | Behavior | |---|---| | `--content <str>` | Inline string; rejects literal `\n` with no real newlines (shell escape footgun) | | `--content-file <path>` | Reads UTF-8 file | | `--content-file -` | Reads stdin (`Bun.stdin.text()`) | | Neither (create only) | Empty body allowed | <Warning> Passing both `--content` and `--content-file` fails. `artifact append` requires one of them and rejects empty section bodies. </Warning> ## Journal auto-management On every successful `artifact create` for a non-journal kind (when the `journal` kind is registered): 1. Resolve today’s date from `createdAt` (or current date). 2. Ensure `artifacts/journal/<YYYY>/<YYYY-MM-DD>.md` exists (skeleton with `## Activity`). 3. Append `- HH:MM [[<id>]] — <title>` under `## Activity` (or `## Backfilled` when `_backfilled: true`). Failures are logged to stderr as warnings and **do not** roll back the created artifact. Manual journal creates are blocked; direct file edits outside `## Activity` are preserved. ## `artifact get` <ParamField body="--format" type="md | json" default="md"> `md`: raw file bytes. `json`: `{ id, kind, path, frontmatter, body, audit }` where `audit` filters `audit.jsonl` for ops `artifact.create`, `artifact.append`, `artifact.status`, `artifact.set`, `refs.add` touching this id. </ParamField> ## `artifact list` Starts from the smallest indexed slice (`--kind` → `byKind`, else `--status` → `byStatus`, else `--domain` → `byDomain`, else all), then intersects additional filters. | Filter | Index path | |---|---| | `--kind <K>` + indexed field (e.g. `--status`, `--priority` on `task`) | Per-kind field index (`indexedFields` in kind def) | | Other frontmatter flags | Linear scan on candidates | | `--contains <Q>` | Case-insensitive substring in body (reads files) or string frontmatter values; applied last | `string[]` frontmatter matches if any element equals the query value. <RequestExample> ```bash loopany artifact list --kind task --status running --contains "cache" ``` </RequestExample> ## `artifact append` Appends or extends an H2 section in the artifact body (immutable pattern: no in-place body rewrite except section merge). <ParamField body="--section" type="string" required> Section name without `##` (e.g. `Outcome` → `## Outcome` in the file). </ParamField> If the section exists, new content is inserted before the next `##` heading; duplicate `## Outcome` headings are not created. ## `artifact status` Transitions `status` through the kind’s status machine. Illegal transitions throw (e.g. `todo` → `in_review` on `task`). <ParamField body="--reason" type="string"> Recorded in JSON stdout only. Does **not** append a `## Status` section to the body. </ParamField> <ParamField body="--addressed-by" type="artifact id"> Required when transitioning a `signal` to `addressed`. Emits `refs add` edge `<addressed-by> addresses <signal>` (returns `edgesEmitted: 1`). Invalid on other statuses. </ParamField> Kinds without a status machine reject all status changes. ### Task terminal workflow and `## Outcome` The `task` kind definition requires `## Outcome` in the body when `status` is `done` or `failed`. That rule is documented in `kinds/task.md` and enforced by agent workflows (capture, reflect); **the CLI store does not block** `artifact status … done` without an Outcome section. Recommended terminal sequence: <Steps> <Step title="Append Outcome"> ```bash loopany artifact append <task-id> --section Outcome --content "What shipped or failed, with evidence." ``` Or use `--content-file` for longer write-ups. </Step> <Step title="Transition status"> ```bash loopany artifact status <task-id> done --reason "one-line summary" # or loopany artifact status <task-id> failed --reason "blocked on X" ``` </Step> <Step title="Verify"> ```bash loopany artifact get <task-id> --format json ``` Confirm `frontmatter.status` and `body` contains `## Outcome`. </Step> </Steps> | Task status | Outcome expectation (kind contract) | |---|---| | `done` | `## Outcome` required (workflow) | | `failed` | `## Outcome` required (workflow) | | `cancelled` | `--reason` on status; no Outcome requirement in kind def | Other kinds with documented required sections (`skill-proposal` on accept/reject, `learning` on supersede/archive) follow the same pattern: append first, status second; runtime validates frontmatter and transitions only. ## `artifact set` Updates one non-status frontmatter field, re-validates with the kind schema, bumps `updatedAt`. <ParamField body="--field" type="string" required> Frontmatter key (camelCase as stored, e.g. `title`, `checkAt`, `domain`). </ParamField> <ParamField body="--value" type="string" required> Coerced per field spec (`string[]` also accepts JSON array syntax). </ParamField> - `status` → rejected; use `artifact status`. - Built-ins without kind specs: `domain`, `createdAt`, `updatedAt`, `_backfilled`. ## Errors and validation | Failure | Typical message / behavior | |---|---| | Unknown kind | `Unknown kind: …` | | Unknown field flag on create | Lists valid `--<field>` lines for that kind | | Zod frontmatter | `Invalid input:` with per-field paths (not raw Zod JSON) | | Duplicate slug | `Slug already exists: …` | | Illegal status transition | `Illegal transition: … → … (allowed: […])` | | Missing append body | `Missing body: pass --content … or --content-file …` | | Literal `\n` in `--content` | Actionable shell/newline guidance | Successful commands also append best-effort rows to `audit.jsonl` (`op` like `artifact.create`); failures may still log with `error`. ## Lifecycle (create → close) ```mermaid stateDiagram-v2 [*] --> todo: artifact create --kind task todo --> running: artifact status running running --> in_review: artifact status in_review in_review --> done: append Outcome then status done running --> failed: append Outcome then status failed todo --> cancelled: artifact status cancelled ``` ```text artifact create ──► ArtifactStore.create ──► artifacts/<dir>/<id>.md │ │ │ └──► journal auto-link (best-effort) │ artifact append ──► appendSection (body H2) artifact status ──► setStatus (frontmatter only) artifact set ──► setField (non-status FM) ``` ## Related pages <CardGroup> <Card title="Kinds and validation" href="/kinds-and-validation"> Markdown kind defs, Zod schemas, status machines, and slug rules behind `--field` flags. </Card> <Card title="Artifacts and workspace" href="/artifacts-and-workspace"> On-disk paths, v0.2 slug-as-id layout, and `$LOOPANY_HOME` structure. </Card> <Card title="Artifact lifecycle example" href="/artifact-lifecycle-example"> End-to-end signal → task → Outcome → done with graph edges. </Card> <Card title="Capture workflow" href="/capture-workflow"> When to append Outcome before closing tasks in agent sessions. </Card> <Card title="CLI reference" href="/cli-reference"> Full command tree, JSON conventions, and adjacent graph/search commands. </Card> </CardGroup> --- ## 17. Graph, search, and scheduling > refs add/query, trace BFS, followups --due, hybrid search/reindex (--no-embed), factory UI (--port/--no-open), and audit.jsonl side effects. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/17-graph-search-and-scheduling.md - Generated: 2026-06-05T19:01:45.746Z ### Source Files - `src/commands/refs.ts` - `src/commands/trace.ts` - `src/commands/followups.ts` - `src/commands/search.ts` - `src/commands/reindex.ts` - `src/core/search-store.ts` - `src/commands/factory.ts` - `src/ui/server.ts` --- title: "Graph, search, and scheduling" description: "refs add/query, trace BFS, followups --due, hybrid search/reindex (--no-embed), factory UI (--port/--no-open), and audit.jsonl side effects." --- `loopany refs`, `trace`, `followups`, `search`, `reindex`, and `factory` operate on three derived views of `$LOOPANY_HOME`: append-only `references.jsonl` (explicit edges), an in-memory `ArtifactIndex` rebuilt each invocation (explicit + implicit edges, `checkAt` scheduling), and optional `search.db` (FTS5 + embeddings). Every successful or failed CLI run appends one row to `audit.jsonl`. ```mermaid flowchart LR subgraph storage["$LOOPANY_HOME"] A["artifacts/*.md"] R["references.jsonl"] S["search.db"] AU["audit.jsonl"] end subgraph runtime["Per CLI invocation"] IDX["ArtifactIndex.build"] REFS["refs / trace"] FU["followups"] SR["SearchStore"] end A --> IDX R --> IDX IDX --> REFS IDX --> FU A --> SR SR --> S CLI["src/cli.ts"] --> AU REFS --> CLI FU --> CLI SR --> CLI ``` ## Command overview | Command | Mutates workspace | Primary output | |---------|-------------------|----------------| | `refs add` | Appends to `references.jsonl` | Single `Edge` JSON | | `refs <id>` | Read-only | `Edge[]` JSON | | `trace <id>` | Read-only | `{ root, nodes, edges }` JSON | | `followups` | Read-only | `ArtifactMeta[]` JSON | | `reindex` | Writes `search.db` | `{ indexed, skipped, removed, embedder }` JSON | | `search <query>` | Read-only | `SearchResult[]` JSON (empty if no index) | | `factory` | Read-only UI; `POST /api/open` spawns editor | Blocks until Ctrl-C | All JSON commands write pretty-printed JSON to stdout. Graph and scheduling commands bootstrap the workspace and rebuild `ArtifactIndex` on each run (O(n) over artifacts). ## Reference graph: `refs` ### Add an explicit edge ```bash loopany refs add --from <id> --to <id> --relation <verb> ``` <ParamField body="--from" type="string" required> Source artifact id (slug). </ParamField> <ParamField body="--to" type="string" required> Target artifact id. </ParamField> <ParamField body="--relation" type="string" required> Open-registry relation verb (for example `led-to`, `addresses`, `mentions`). </ParamField> Appends one JSON line to `references.jsonl`: ```json {"ts":"2026-06-05T12:00:00.000Z","from":"sig-q2","to":"tsk-ship","relation":"led-to","actor":"cli"} ``` Reverse edges are not stored; `ArtifactIndex` builds `refsIn` / `refsOut` maps at load time. <RequestExample> ```bash loopany refs add --from sig-q2 --to tsk-ship --relation led-to ``` </RequestExample> <ResponseExample> ```json { "ts": "2026-06-05T12:00:00.000Z", "from": "sig-q2", "to": "tsk-ship", "relation": "led-to", "actor": "cli" } ``` </ResponseExample> ### Query neighbors (BFS) ```bash loopany refs <id> [--direction in|out|both] [--relation R] [--depth N] [--domain D] ``` | Flag | Default | Behavior | |------|---------|----------| | `--direction` | `out` | Follow outgoing, incoming, or both edge sets per hop | | `--depth` | `1` | Positive integer; N hops from the start id | | `--relation` | (all) | Keep only edges matching this verb | | `--domain` | (all) | Both endpoint artifacts must have `frontmatter.domain` equal to D | Traversal is BFS over nodes. Each unique edge (`from|to|relation|ts`) is returned once even if reachable on multiple paths. Visited nodes are not re-expanded, but all edges encountered along the frontier are collected. <Note> Implicit edges appear in `refs` queries but are never written to `references.jsonl`. At index build time, `mentions` edges are synthesized from `frontmatter.mentions[]` and body `[[id]]` wiki links (`actor`: `frontmatter` or `body`, `implicit: true`). Updating frontmatter or body changes implicit edges on the next CLI run. </Note> ## Causal lineage: `trace` ```bash loopany trace <id> [--direction forward|backward|both] [--relations csv] [--max-depth N] ``` `trace` walks a **subset** of relations for lineage, not the full graph. Default predicates: `led-to`, `addresses`, `supersedes`, `follows-up`, `cites`. `mentions` is excluded (soft pointer, not lineage). `caused-by` is also excluded; use `--direction backward` on `led-to` edges to reach upstream work. | Flag | Default | Behavior | |------|---------|----------| | `--direction` | `both` | `forward` follows `refsOut`; `backward` follows `refsIn` | | `--relations` | causal set above | Comma-separated override; must list at least one verb | | `--max-depth` | unlimited | Positive integer cap per direction | <ResponseField name="root" type="string"> Start artifact id. </ResponseField> <ResponseField name="nodes" type="TraceNode[]"> Artifact metadata plus signed `distance`: negative = backward (causes), `0` = root, positive = forward (effects). Sorted by `distance`, then `id`. Dangling edge targets are dropped. </ResponseField> <ResponseField name="edges" type="Edge[]"> Edges used in the walk (deduped by `from|to|relation|ts`). </ResponseField> Cycle-safe: each node is assigned at most one distance; walks terminate on revisits. <Warning> `trace` requires the root id to exist. Unknown ids exit with `No artifact with id: …`. </Warning> <RequestExample> ```bash loopany trace tsk-ship --direction both --max-depth 3 ``` </RequestExample> ## Scheduling: `followups` ```bash loopany followups [--due today|overdue|next-7d] [--include-done true] [--domain D] ``` Selects artifacts whose `checkAt` frontmatter (ISO date string) is on or before a cutoff: | `--due` | Cutoff | Extra filter | |---------|--------|--------------| | `today` (default) | Today (UTC date portion) | None | | `next-7d` | Today + 7 days | None | | `overdue` | Today | Keeps only `checkAt` date **strictly before** today | Unless `--include-done true`, results exclude artifacts in a **terminal** status: the kind’s status machine has no outgoing transitions from the current status (for example `done`, `cancelled`, `failed` on `task`). Kinds without a status machine are never filtered this way. Output is JSON array of `ArtifactMeta` objects (`id`, `kind`, `path`, `frontmatter`) — same shape as `artifact list`, filtered by schedule. <Steps> <Step title="Daily review"> Run `loopany followups --due today` (optionally `--domain crm`) to list actionable `checkAt` items. </Step> <Step title="Overdue sweep"> Run `loopany followups --due overdue` for items past their date that are still non-terminal. </Step> <Step title="Weekly horizon"> Run `loopany followups --due next-7d` to see the next week’s scheduled reviews. </Step> </Steps> ## Hybrid search ### Rebuild index: `reindex` ```bash loopany reindex [--force] [--no-embed] ``` | Flag | Effect | |------|--------| | `--force` | Deletes `search.db` and rebuilds FTS5 + chunk tables from scratch | | `--no-embed` | Uses `NoopEmbedder` — FTS5-only index (no ONNX download; used in CI) | Incremental by default: compares each artifact file’s mtime to `artifact_mtimes` in `search.db` and skips unchanged files. After scanning disk, removes index rows for artifact ids no longer present. <Info> Artifact create/append/status does **not** auto-reindex in v1. Run `reindex` after bulk edits or when you want fresh embeddings. </Info> <ResponseExample> ```json { "indexed": 12, "skipped": 45, "removed": 1, "embedder": "transformers" } ``` </ResponseExample> `embedder` is `transformers` when `Xenova/all-MiniLM-L6-v2` loads, otherwise `noop` (keyword-only search still works). Indexing pipeline (`SearchStore`): - Chunks: optional title chunk + body split at `##` / `###` headings - Keyword: SQLite FTS5 (`chunks_fts`) with BM25 rank - Semantic: 384-dim embeddings when embedder is available; cosine similarity ≥ `0.3` - Fusion: Reciprocal Rank Fusion (`k = 60`) across keyword and semantic ranked lists; best chunk per artifact wins Derived file: `$LOOPANY_HOME/search.db` (safe to delete; markdown artifacts remain canonical). ### Query: `search` ```bash loopany search <query> [--kind K] [--domain D] [--status S] [--limit N] ``` | Flag | Default | |------|---------| | `--limit` | `10` | If `search.db` is missing, stdout is `[]`, stderr prints `no search index found — run loopany reindex first.`, exit code `0` (scripts can probe without failing). <ResponseField name="artifactId" type="string"> Matched artifact id. </ResponseField> <ResponseField name="snippet" type="string"> Truncated chunk text (~200 chars). </ResponseField> <ResponseField name="score" type="number"> RRF fused score (higher is better). </ResponseField> Additional fields: `kind`, `domain`, `status`, `path`, `section`. <RequestExample> ```bash loopany reindex loopany search "billing refactor" --kind note --limit 5 ``` </RequestExample> <Tip> Use `loopany reindex --no-embed` in automation or offline environments; use a full `reindex` (no flag) when semantic recall matters and the Hugging Face model can download to `HF_HOME`. </Tip> ## Factory UI: `factory` ```bash loopany factory [--port N] [--host H] [--no-open] ``` | Flag | Default | |------|---------| | `--port` | `4242` | | `--host` | `127.0.0.1` | | `--no-open` | (browser opens via `open` / `xdg-open` / `start`) | Starts a local Bun HTTP server (`src/ui/server.ts`) serving a read-only Kaplay pixel view. Binds to loopback only — no auth, no CORS. | Route | Method | Purpose | |-------|--------|---------| | `/` | GET | Factory HTML + game | | `/api/graph` | GET | Workspace graph JSON from `buildGraph()` | | `/api/open` | POST | `{ "id": "<artifact>" }` → opens artifact path in editor | Graph payload includes `nodes`, `edges` (explicit + implicit), `lanes` (kind ordering), `domains`, `enabledDomains`, `workspace`. Nodes carry card fields and a short body preview, not full markdown. Editor resolution for `/api/open`: 1. `$LOOPANY_EDITOR` (shell-split command, e.g. `cursor` or `code -n`) 2. Platform default: `open` (macOS), `xdg-open` (Linux), `start` (Windows) Process runs until SIGINT/SIGTERM; then the server stops. ## `audit.jsonl` side effects Every CLI invocation (including read-only graph/search queries) triggers a best-effort append to `$LOOPANY_HOME/audit.jsonl` after the command finishes. Audit failures are silent and never change the command exit code. Row shape: ```json { "ts": "2026-06-05T12:00:00.000Z", "op": "refs.add", "actor": "cli", "duration_ms": 42, "from": "sig-q2", "to": "tsk-ship", "relation": "led-to" } ``` `op` is the command key with spaces as dots (`refs`, `refs.add`, `trace`, `followups`, `search`, `reindex`, `factory`). Failed runs add `"error": "<message>"`. Large fields (artifact bodies) are never logged. | Command | Typical audit fields | |---------|---------------------| | `refs add` | `from`, `to`, `relation` | | `refs` | `count` | | `trace` | `nodes`, `edges` | | `followups` | `count` | | `search` | `count` | | `reindex` | `indexed`, `skipped`, `removed`, `embedder` | | `factory` | `op` only (duration spans entire session) | `artifact get <id> --format json` attaches filtered audit history for that id (`artifact.create`, `artifact.append`, `artifact.status`, `artifact.set`, `refs.add` where `id`, `from`, or `to` matches). <Check> Verify audit growth: run a command, then `tail -1 $LOOPANY_HOME/audit.jsonl | jq .op`. </Check> ## `refs` vs `trace` | | `refs <id>` | `trace <id>` | |---|-------------|--------------| | Relations | All (unless `--relation`) | Default causal set; `mentions` excluded | | Implicit edges | Included | Included if relation filter allows | | Output | Flat `Edge[]` | Nodes with signed `distance` + edges | | Use case | Neighborhood / degree-N graph | Cause → root → effect narrative | ## Errors and recovery | Symptom | Cause | Recovery | |---------|-------|----------| | `refs add requires --from, --to, --relation` | Missing flags | All flags require values (`--relation led-to`) | | `Invalid --depth` / `--max-depth` | Non-positive integer | Pass `1` or greater | | `No artifact with id` on `trace` | Unknown root | Confirm id via `artifact list` | | Empty `search` + stderr hint | No `search.db` | `loopany reindex` | | `embedder: "noop"` after reindex | Model load failed | Check stderr during reindex; FTS-only still works | | Stale search hits | Index not refreshed | `reindex` (or `reindex --force` after model change) | ## Related pages <CardGroup> <Card title="Reference graph" href="/reference-graph"> Append-only edges, implicit mentions, relation conventions, and wiki-link rules. </Card> <Card title="CLI reference" href="/cli-reference"> Full command surface and JSON stdout conventions. </Card> <Card title="Artifacts and workspace" href="/artifacts-and-workspace"> `references.jsonl`, `search.db`, `audit.jsonl`, and on-disk layout. </Card> <Card title="Periodic review" href="/periodic-review"> Cadence for `followups --due today` and overdue sweeps. </Card> <Card title="Doctor and troubleshooting" href="/doctor-and-troubleshooting"> Index health, dangling refs, and factory/reindex recovery. </Card> </CardGroup> --- ## 18. Configuration reference > config.yaml keys (schemaVersion, enabled_domains), environment variables LOOPANY_HOME, LOOPANY_SKIP_VERSION_CHECK, LOOPANY_EDITOR, and SCHEMA_VERSION guard behavior. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/18-configuration-reference.md - Generated: 2026-06-05T19:02:59.852Z ### Source Files - `src/core/config.ts` - `src/core/engine.ts` - `src/version.ts` - `src/ui/editor.ts` - `test/config.test.ts` - `INSTALL_FOR_AGENTS.md` --- title: "Configuration reference" description: "config.yaml keys (schemaVersion, enabled_domains), environment variables LOOPANY_HOME, LOOPANY_SKIP_VERSION_CHECK, LOOPANY_EDITOR, and SCHEMA_VERSION guard behavior." --- Runtime configuration for loopany splits across `$LOOPANY_HOME/config.yaml` (workspace schema version and enabled domain packs) and three environment variables that override workspace location, bypass the schema guard, or choose a GUI editor for the factory UI. Every command that loads the workspace goes through `bootstrap()` in `src/core/engine.ts`, which resolves the root, reads `config.yaml`, compares `schemaVersion` to the binary constant `SCHEMA_VERSION` from `src/version.ts`, and wires domain kind packs before artifact operations run. ## Workspace location `getWorkspaceRoot()` returns `process.env.LOOPANY_HOME` when set; otherwise it uses `~/loopany` (via `os.homedir()`). The same root is used for `loopany init`, artifact I/O, `references.jsonl`, `audit.jsonl`, and optional `search.db`. <ParamField body="LOOPANY_HOME" type="string"> Absolute path to the loopany workspace directory. When unset, defaults to `~/loopany`. Applies to every CLI invocation in the shell session — not per-project unless you export it per command. </ParamField> <Steps> <Step title="Use a custom brain directory"> ```bash export LOOPANY_HOME=/path/to/brain loopany init loopany doctor ``` </Step> <Step title="One-off override"> ```bash LOOPANY_HOME=/tmp/loopany-test loopany artifact list ``` </Step> </Steps> <Note> `loopany init` is idempotent: it creates missing directories and `config.yaml`, copies bundled kinds into `kinds/`, and leaves existing files untouched. Re-running init does not bump an existing `schemaVersion`. </Note> ## config.yaml The file lives at `$LOOPANY_HOME/config.yaml`. The `Config` class in `src/core/config.ts` loads it with YAML parsing; a missing file is treated as empty defaults. Writes go through `persist()`, which re-serializes the in-memory object (comments in the file are not preserved on update). ### Keys | Key | Type | Default when absent | Written by | |-----|------|---------------------|------------| | `schemaVersion` | string (semver-style) | `0.1.0` (`ASSUMED_LEGACY_VERSION`) | `loopany init` (new file only), migration final scripts via `setSchemaVersion()`, manual edit | | `enabled_domains` | string array | `[]` | `loopany domain enable` / `domain disable` | <ParamField body="schemaVersion" type="string" required={false}> Workspace on-disk format version. Compared to `SCHEMA_VERSION` in `src/version.ts` on every `bootstrap()` unless the version check is skipped. Pre-framework workspaces that never wrote this field are read as `0.1.0`. </ParamField> <ParamField body="enabled_domains" type="string[]" required={false}> Names of domain packs whose kind definitions are merged at bootstrap. Each enabled name loads `domains/<name>/kinds/` in addition to workspace-global `kinds/`. Entries are stored sorted; `enableDomain` is idempotent; `disableDomain` on an unknown name is a no-op. </ParamField> ### Example files Fresh init (`loopany init` when `config.yaml` does not exist): ```yaml # loopany workspace config schemaVersion: 0.2.0 ``` With domains enabled: ```yaml schemaVersion: 0.2.0 enabled_domains: - ads - crm ``` Legacy workspace (no `schemaVersion` key): ```yaml enabled_domains: - crm ``` At load time, `Config.schemaVersion()` returns `0.1.0` until a migration or `setSchemaVersion()` updates the file. ## VERSION vs SCHEMA_VERSION The binary carries two independent version constants in `src/version.ts`: | Constant | Current value | Meaning | |----------|-----------------|---------| | `VERSION` | `0.2.0` | Package / CLI semver (`loopany --version`) | | `SCHEMA_VERSION` | `0.2.0` | Expected workspace format; bump only when on-disk layout, kinds, frontmatter, or references require migration | `schemaVersion` in `config.yaml` tracks the workspace; `SCHEMA_VERSION` tracks what the running binary accepts. They can drift across releases until you migrate the workspace forward. ## Schema version guard On each `bootstrap()`: 1. Resolve root via `LOOPANY_HOME` or default. 2. Fail with `WorkspaceNotFoundError` if `kinds/` is missing (run `loopany init`). 3. Load `config.yaml` and compute `config.schemaVersion()`. 4. Unless skipped, if `config.schemaVersion() !== SCHEMA_VERSION`, throw `SchemaVersionMismatchError`. ```mermaid sequenceDiagram participant CLI as cli.ts participant Boot as bootstrap() participant CFG as config.yaml participant VER as SCHEMA_VERSION CLI->>Boot: dispatch command Boot->>CFG: Config.load(root) CFG-->>Boot: schemaVersion() alt versions equal or check skipped Boot-->>CLI: Engine else mismatch Boot-->>CLI: SchemaVersionMismatchError CLI-->>CLI: stderr + exit 1 end ``` ### Error shape `SchemaVersionMismatchError` message (paraphrased structure): ``` Workspace schema is v<workspace> but this binary expects v<binary>. Read `skills/migrations/v<workspace>-to-v<binary>/SKILL.md` and run `loopany migrate v<workspace>-to-v<binary>`. ``` `cli.ts` catches this error, prints `Error: …` to stderr, and exits with code `1`. Artifact commands (`list`, `get`, `create`, etc.) do not run on a stale workspace. ### Commands that bypass the guard | Surface | Mechanism | |---------|-----------| | `loopany doctor` | `bootstrap({ skipVersionCheck: true })` — reports `schema version` check fail instead of crashing | | `loopany migrate` | Same bypass — discovery works on stale workspaces | | Env `LOOPANY_SKIP_VERSION_CHECK=1` | `process.env.LOOPANY_SKIP_VERSION_CHECK === '1'` in `bootstrap()` | | Migration scripts | Typically run with `LOOPANY_SKIP_VERSION_CHECK=1` while transforming data | <Warning> Do not leave `LOOPANY_SKIP_VERSION_CHECK=1` set in your shell profile for normal use. It disables the safety gate that prevents a newer binary from reading an unmigrated workspace (or the reverse). </Warning> ### Recovery flow <Steps> <Step title="Confirm mismatch"> ```bash loopany doctor # or loopany migrate ``` `migrate` with no args prints workspace schema, binary expectation, and the next migration name when one exists whose `from` matches the workspace version. </Step> <Step title="Run migration"> ```bash loopany migrate v0.1.0-to-v0.2.0 ``` Read the printed `SKILL.md` and run scripts in order (migration scripts live under `skills/migrations/` in the repo; the CLI does not auto-execute them). </Step> <Step title="Verify"> Final migration step should call `config.setSchemaVersion('0.2.0')` (or the target `SCHEMA_VERSION`). Then: ```bash loopany doctor ``` Schema version check should pass when `config.yaml` matches `SCHEMA_VERSION`. </Step> </Steps> ## Environment variables ### LOOPANY_SKIP_VERSION_CHECK <ParamField body="LOOPANY_SKIP_VERSION_CHECK" type="string"> When set to `1`, skips the `schemaVersion === SCHEMA_VERSION` check in `bootstrap()`. Used by migration scripts and tests; also available as `bootstrap({ skipVersionCheck: true })` for `doctor` and `migrate`. </ParamField> ```bash LOOPANY_SKIP_VERSION_CHECK=1 loopany artifact list ``` ### LOOPANY_EDITOR <ParamField body="LOOPANY_EDITOR" type="string"> Shell-style command string for opening artifact markdown in a GUI editor from the factory UI (`openInEditor` in `src/ui/editor.ts`). First token is the executable; remaining tokens are arguments. Supports simple quoted segments (`code --wait`, `cursor`). </ParamField> Resolution order: 1. `LOOPANY_EDITOR` if non-empty after trim 2. Platform default: `open` (macOS), `xdg-open` (Linux), `start` (Windows) <Info> `$VISUAL` and `$EDITOR` are intentionally ignored. Terminal editors spawned from the factory server would not show a window; set `LOOPANY_EDITOR` explicitly for GUI tools, or use a wrapper script for complex invocations. </Info> ```bash export LOOPANY_EDITOR="cursor" loopany factory ``` ## enabled_domains and bootstrap When domains are enabled, bootstrap builds extra kind pack directories: ``` domains/<name>/kinds/ # for each name in enabled_domains ``` `KindRegistry.load()` merges workspace `kinds/` with those pack dirs. Disabling a domain removes its kinds from the registry on the next command; it does not delete artifacts tagged with that domain. CLI persistence: ```bash loopany domain enable crm loopany domain disable ads loopany domain list # enabled + observed-only (in artifacts but not enabled) ``` `loopany doctor` emits a **warn** (not fail) when an artifact’s `domain` frontmatter is not listed in `enabled_domains` — useful for catching typos or forgotten enables. ## Operational checklist | Goal | Action | |------|--------| | Point CLI at a test workspace | `LOOPANY_HOME=/tmp/ws loopany …` | | See version skew | `loopany migrate` or `loopany doctor` | | Fix version skew | Follow migration skill; bump `schemaVersion` to match binary | | Enable CRM kinds pack | `loopany domain enable crm` (requires `domains/crm/kinds/` on disk) | | Open artifacts from factory in VS Code | `export LOOPANY_EDITOR="code -n"` | <Check> Healthy steady state: `config.yaml` contains `schemaVersion: 0.2.0` (matching current `SCHEMA_VERSION`), `loopany doctor` exits `0`, and routine commands run without `LOOPANY_SKIP_VERSION_CHECK`. </Check> ## Related pages <CardGroup> <Card title="Schema migration" href="/schema-migration"> `schemaVersion` bumps, `loopany migrate` discovery, and v0.1.0→v0.2.0 script workflow. </Card> <Card title="Domains" href="/domains"> Domain packs under `domains/<name>/`, when to enable, and scope-local kinds. </Card> <Card title="Workspace setup" href="/workspace-setup"> `loopany init`, bundled kinds, onboarding, and doctor verification. </Card> <Card title="Artifacts and workspace" href="/artifacts-and-workspace"> Full on-disk layout under `$LOOPANY_HOME` beyond `config.yaml`. </Card> <Card title="Doctor and troubleshooting" href="/doctor-and-troubleshooting"> Schema version check in doctor output and common recovery paths. </Card> <Card title="Installation" href="/installation"> Clone, Bun, `bun link`, and initial `LOOPANY_HOME` setup. </Card> </CardGroup> --- ## 19. Artifact lifecycle example > End-to-end recipe: signal → task with led-to/addresses edges, status transitions, brief output, and journal linkage—mirroring test/scenario.e2e and skill-regression flows. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/19-artifact-lifecycle-example.md - Generated: 2026-06-05T19:02:46.889Z ### Source Files - `test/scenario.e2e.test.ts` - `test/skill-regression.sh` - `skills/loopany-core/kinds/signal.md` - `skills/loopany-core/kinds/task.md` - `skills/loopany-core/conventions/relations.md` - `src/commands/artifact-status.ts` - `src/core/artifact-store.ts` --- title: "Artifact lifecycle example" description: "End-to-end recipe: signal → task with led-to/addresses edges, status transitions, brief output, and journal linkage—mirroring test/scenario.e2e and skill-regression flows." --- The canonical end-to-end path is exercised by `test/scenario.e2e.test.ts`: record a `signal`, create a `person` and `task`, wire `led-to` and `mentions` edges, pick up work via `followups`, transition the task through `running` → `in_review` → `done` with a `## Outcome`, then verify graph queries. Every non-`journal` create also appends a wiki-link into today's auto-managed journal under `artifacts/journal/<YYYY>/<YYYY-MM-DD>.md`. ## Prerequisites | Requirement | Notes | |---|---| | Workspace | `loopany init` with `$LOOPANY_HOME` set (tests use an isolated temp dir) | | CLI | `bun run src/cli.ts` or a linked `loopany` binary | | Kinds | Bundled under `$LOOPANY_HOME/kinds/` after init (`signal`, `task`, `person`, `brief`, `journal`, …) | <Note> v0.2 IDs are globally unique slugs (no kind prefix). Title-derived slugs are the default (`Follow up with Alice` → `follow-up-with-alice`); explicit slugs use `--slug` (required for `person`). </Note> ## Canonical scenario: signal → task → done This mirrors the Alice contract follow-up in `test/scenario.e2e.test.ts`. <Steps> <Step title="Initialize workspace"> ```bash export LOOPANY_HOME=~/loopany # or a temp path for experiments loopany init ``` </Step> <Step title="Record the inbound signal"> ```bash loopany artifact create \ --kind signal \ --title "User wants to follow up with Alice about contract" ``` Stdout is JSON with `id`, `kind`, and `path`. The store stamps `status: open` (signal initial state) and appends `[[<id>]]` to today's journal `## Activity` section. </Step> <Step title="Create the person entity"> ```bash loopany artifact create \ --kind person \ --slug alice-chen \ --name "Alice Chen" \ --aliases "alice,a.chen" ``` File lands at `artifacts/people/alice-chen.md`. The slug is the durable ID for `[[alice-chen]]` citations. </Step> <Step title="Create the task with scheduling and mentions"> ```bash loopany artifact create \ --kind task \ --title "Follow up with Alice on contract" \ --status todo \ --priority high \ --check-at 2020-01-01 \ --mentions alice-chen ``` `checkAt` is indexed for `loopany followups`. In the e2e test the date is intentionally in the past so `--due today` returns the task immediately. </Step> <Step title="Wire reference edges"> Weak causal link (signal produced the task): ```bash loopany refs add --from <signal-id> --to <task-id> --relation led-to ``` Stakeholder mention (task talks about the person): ```bash loopany refs add --from <task-id> --to alice-chen --relation mentions ``` `mentions` can also be satisfied by frontmatter `--mentions` or body `[[alice-chen]]`; all three emit the same semantic edge (implicit edges from frontmatter/body vs persisted edges from `refs add`). </Step> <Step title="Pick up due work"> ```bash loopany followups --due today ``` Returns JSON metadata for artifacts whose `checkAt` is on or before the cutoff. Terminal statuses (`done`, `cancelled`, `failed` for tasks) are excluded unless `--include-done true`. </Step> <Step title="Run the task status machine"> ```bash loopany artifact status <task-id> running loopany artifact status <task-id> in_review ``` Illegal jumps are rejected by the store (e.g. `todo` → `in_review` fails with a transition error). </Step> <Step title="Close with Outcome and done"> ```bash loopany artifact append <task-id> \ --section Outcome \ --content "Alice signed today; contract effective 2026-04-29." loopany artifact status <task-id> done --reason signed ``` The kind playbook requires `## Outcome` before `done` or `failed`; the CLI does not block `done` without that section today—agents and capture skills treat missing Outcomes as a quality failure. `--reason` updates frontmatter only; it does not append a body section. </Step> <Step title="Verify end state"> ```bash loopany artifact get <task-id> --format json loopany refs <signal-id> loopany refs alice-chen --direction in loopany artifact list --kind task --status done ``` Expect `frontmatter.status: done`, body containing `## Outcome`, outbound `led-to` from the signal, and inbound `mentions` on the person. </Step> </Steps> ### Task and signal status machines ```mermaid stateDiagram-v2 direction LR state "signal" as sig { [*] --> open open --> addressed open --> dismissed addressed --> open dismissed --> open } state "task" as tsk { [*] --> todo todo --> running todo --> done todo --> cancelled running --> in_review running --> done running --> failed running --> cancelled in_review --> done in_review --> failed in_review --> cancelled } ``` ## Relation verbs in this flow | Verb | Direction in this recipe | Meaning | |---|---|---| | `led-to` | `signal` → `task` | Weak causal: the observation eventually produced the work item | | `mentions` | `task` → `person` | Soft reference to an entity involved in the work | | `addresses` | `task` → `signal` | Strong responsibility: the task resolves the observation | <Warning> Do not write inverse edges (`task` → `signal` with `led-to`). Query `--direction in` on the signal instead. </Warning> ### Closing the signal with `addresses` After the task ships, mark the signal `addressed` and emit the canonical `addresses` edge in one call: ```bash loopany artifact status <signal-id> addressed --addressed-by <task-id> ``` `artifact-status.ts` appends `<task-id> addresses <signal-id>` to `references.jsonl` and flips `status` atomically. Transitioning to `addressed` without `--addressed-by` errors. This pattern is covered in `test/cli.e2e.test.ts` and is what `test/skill-regression.sh` scenario 9 expects when upgrading a recurring signal to a task. `led-to` during intake plus `addresses` on close is the full pair: causality while open, responsibility when finished. ## Journal linkage (automatic) On every `artifact create` except `journal`, `ArtifactStore` calls `appendToTodayJournal`: - **Path:** `artifacts/journal/<YYYY>/<YYYY-MM-DD>.md` (`slugLayout: year`, id = date string) - **Section:** `## Activity` (or `## Backfilled` when `_backfilled: true`) - **Line format:** `HH:MM [[slug]] — title` (time omitted when unavailable) Manual `loopany artifact create --kind journal` is rejected at the CLI; the store is the sole writer of journal files. Each journal wiki-link also yields an implicit `mentions` edge from the journal id to the new artifact. When debugging graph queries, filter `implicit: true` edges if you only care about explicit `refs add` lines. **Read today's index two ways:** 1. Open the journal markdown file and scan `## Activity`. 2. `loopany refs <YYYY-MM-DD> --direction out` (same set via graph). For user-facing narrative, create a `brief` (see below)—not a hand-edited journal. ## Extension: brief output after completion A `brief` is the agent's point-in-time summary back to the user (`skills/loopany-core/kinds/brief.md`). It has no status machine; cite sources so the summary is drillable. ```bash loopany artifact create \ --kind brief \ --title "Morning briefing" \ --for-date 2026-06-05 \ --mentions "<task-id>,alice-chen" \ --content "$(cat <<'EOF' ## What's due today ## What changed since last briefing - [[<task-id>]] closed: Alice signed; contract effective 2026-04-29. ## Open threads ## Suggested next moves EOF )" ``` Persist lineage edges: ```bash loopany refs add --from <task-id> --to <brief-id> --relation led-to loopany refs add --from <brief-id> --to <task-id> --relation cites ``` The brief auto-links into the same day's journal like any other kind. To replace a briefing, create a new brief and `supersedes` the old—never rewrite cited briefs in place. ## Skill-regression cross-check `test/skill-regression.sh` runs Claude with loopany skills installed and asserts workspace shape: | Scenario | Behavior checked | |---|---| | 1 | Natural-language signal creation | | 2 | Task creation + flip to `running` | | 9 | Signal upgraded to task; signal status `addressed` | | 5 | Shipped PR captured as `task` with Outcome-quality work | Run locally (requires `claude` CLI and API key): ```bash ./test/skill-regression.sh --dry-run # inspect prompts only ./test/skill-regression.sh 9 # signal → task upgrade only ``` ## End-to-end sequence ```mermaid sequenceDiagram participant User participant CLI as loopany CLI participant Store as ArtifactStore participant Journal as journal YYYY-MM-DD participant Refs as references.jsonl User->>CLI: artifact create signal CLI->>Store: create(signal) Store->>Journal: append Activity [[signal-id]] User->>CLI: artifact create person / task Store->>Journal: append Activity lines User->>CLI: refs add led-to / mentions CLI->>Refs: append edges User->>CLI: followups --due today User->>CLI: artifact status running / in_review User->>CLI: artifact append Outcome User->>CLI: artifact status done User->>CLI: artifact status signal addressed --addressed-by task CLI->>Refs: task addresses signal opt Brief User->>CLI: artifact create brief + cites/led-to Store->>Journal: append [[brief-id]] end ``` ## Verification commands | Check | Command | |---|---| | E2E scenario | `LOOPANY_HOME=$(mktemp -d) bun test test/scenario.e2e.test.ts` | | Addressed edge | `bun test test/cli.e2e.test.ts -t "flips signal to addressed"` | | Followups filter | `bun test test/cli.e2e.test.ts -t "loopany followups"` | | Full suite | `bun run test` | <Check> A healthy lifecycle leaves: (1) markdown files under `artifacts/signals/`, `artifacts/tasks/`, `artifacts/people/`; (2) persisted edges in `references.jsonl`; (3) today's journal with wiki-linked Activity lines; (4) terminal task with `## Outcome` in the body. </Check> ## Common failures | Symptom | Cause | Fix | |---|---|---| | `Illegal transition` | Status jump not in kind machine | Follow `todo` → `running` → `in_review` → `done` for tasks | | `addressed-by` error | `addressed` without responsible artifact | Pass `--addressed-by <task-id>` | | Signal not in followups | `followups` only reads `checkAt` | Tasks use `--check-at`; signals have no `checkAt` field | | Extra graph edges | Auto-journal `mentions` | Filter implicit edges in JSON output | | `Cannot create journal` | Manual journal create | Create any other kind; journal is auto-managed | ## Related pages <CardGroup> <Card title="Quickstart" href="/quickstart"> First session: init, mission, task, one reference edge, doctor. </Card> <Card title="Reference graph" href="/reference-graph"> Relation verbs, implicit mentions, refs/trace queries. </Card> <Card title="Artifact commands" href="/artifact-commands"> create, append, status, set, and Outcome conventions. </Card> <Card title="Capture workflow" href="/capture-workflow"> End-of-task capture gate and event→kind routing. </Card> <Card title="Self-improvement example" href="/self-improvement-example"> Three done tasks with outcomes → reflect → skill-proposal. </Card> <Card title="Build and test" href="/build-and-test"> Unit/e2e tests and skill-regression.sh prerequisites. </Card> </CardGroup> --- ## 20. Self-improvement example > Recipe: three done tasks with outcomes → reflect writes learning + pending skill-proposal → user accepts → skill file diff and proposal Outcome recorded. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/20-self-improvement-example.md - Generated: 2026-06-05T19:02:51.281Z ### Source Files - `skills/loopany-reflect/SKILL.md` - `skills/loopany-core/kinds/task.md` - `skills/loopany-core/kinds/learning.md` - `skills/loopany-core/kinds/skill-proposal.md` - `test/skill-regression.sh` - `src/commands/artifact-status.ts` --- title: "Self-improvement example" description: "Recipe: three done tasks with outcomes → reflect writes learning + pending skill-proposal → user accepts → skill file diff and proposal Outcome recorded." --- The self-improvement loop in loopany is a skill-driven workflow (`loopany-reflect`) over three artifact kinds: `task` (evidence via `## Outcome`), `learning` (belief), and `skill-proposal` (pending skill edit). The CLI enforces status machines and frontmatter schemas; pattern thresholds, body sections, and skill-file edits are governed by kind playbooks and the reflect skill. Agents never patch `SKILL.md` files directly—acceptance goes through a proposal artifact plus git. ## Prerequisites | Requirement | Detail | |-------------|--------| | Workspace | `loopany init` with `$LOOPANY_HOME` set | | Mission | Active `mission` artifact (resolver bootstrap blocks other work without one) | | Skills | `loopany-reflect` installed and routable from `loopany-resolver` | | Evidence | ≥ 3 `task` artifacts in `done` with distinct, comparable `## Outcome` sections | <Note> `test/skill-regression.sh` scenario 8 asserts reflect must **not** create a `learning` when fewer than three completed tasks exist. Reflect after every single task is an anti-pattern. </Note> ## Scenario Three metric-related tasks closed without a `## Before` baseline. Outcomes report numbers but lack a falsifiable before/after. Reflect should generalize that pattern into a `learning`, then a `skill-proposal` that updates the task kind playbook. A human (or delegated agent) accepts the proposal, applies the skill diff, records `## Outcome` on the proposal, and commits. ```text $LOOPANY_HOME/ artifacts/tasks/<task-slug>.md ×3 (status: done, ## Outcome) artifacts/learnings/<lrn-slug>.md (status: active, evidence: tasks) artifacts/skill-proposals/<spr-slug>.md (status: pending → accepted) references.jsonl (mentions / led-to edges) skills/... (repo checkout — edit target on accept) ``` ## Phase 1 — Close three tasks with outcomes Create and complete three tasks that share the same failure mode (metric work without baseline). Append `## Outcome` **before** flipping to `done` (kind playbook requirement; the CLI does not block `done` without that section). <Steps> <Step title="Create first task"> ```bash loopany artifact create --kind task \ --slug dashboard-lcp-march \ --title "Improve dashboard LCP" \ --status todo \ --content "$(cat <<'EOF' ## Plan Ship lazy-loading for the heaviest chart widgets. EOF )" ``` </Step> <Step title="Work, append Outcome, mark done"> ```bash loopany artifact status dashboard-lcp-march running loopany artifact append dashboard-lcp-march --section Outcome --content "$(cat <<'EOF' LCP improved from ~4.2s to ~2.8s in staging after lazy-loading charts. No ## Before snapshot; cannot verify production baseline or control for traffic mix. EOF )" loopany artifact status dashboard-lcp-march done --reason "shipped lazy-load" ``` </Step> <Step title="Repeat for two more tasks"> Use distinct slugs with the same structural gap (e.g. `api-p99-latency-april`, `checkout-conversion-may`). Each `## Outcome` should state what moved and explicitly note the missing baseline. </Step> <Step title="Verify evidence pool"> ```bash loopany artifact list --kind task --status done ``` Expect ≥ 3 items. Confirm each body contains `## Outcome` via `loopany artifact get <slug>`. </Step> </Steps> Terminal task statuses and required body sections are defined in the bundled `task` kind; `artifact status` only validates the status machine (`todo` → `running` → `done`, etc.). ## Phase 2 — Reflect: learning + pending proposal Trigger reflect when the user asks or when ≥ 3 tasks recently reached `done` (not after every task). <Steps> <Step title="Gather and filter evidence"> ```bash loopany artifact list --kind task --status done loopany artifact list --kind learning --status active loopany artifact list --kind skill-proposal --status rejected ``` Subtract task slugs already listed in `evidence` on active learnings or non-rejected proposals. Default time window ≈ one week (`createdAt`, newest first). </Step> <Step title="Apply pattern threshold"> | Pattern | Threshold | |---------|-----------| | Same class of outcome | ≥ 3 tasks | | Belief refuted | ≥ 2 contradicting outcomes | | Belief needs caveat | ≥ 2 tasks | | Dismissed signal recurs | ≥ 3 dismissals over ≥ 2 weeks | Here: ≥ 3 tasks whose Outcomes lack `## Before` on metric work → valid pattern. </Step> <Step title="Write learning artifact"> ```bash loopany artifact create --kind learning \ --slug metric-outcomes-need-before-2026 \ --title "Metric task outcomes are unfalsifiable without a ## Before baseline" \ --evidence "dashboard-lcp-march,api-p99-latency-april,checkout-conversion-may" \ --mentions "<your-mission-slug>" \ --check-at 2026-09-01 \ --content "$(cat <<'EOF' ## Observation Three recent metric tasks reported deltas without reproducible baselines. ## Evidence - dashboard-lcp-march — "LCP improved… No ## Before snapshot…" - api-p99-latency-april — "p99 down 18%… no baseline captured" - checkout-conversion-may — "conversion +0.4pp… traffic mix uncontrolled" ## Scope Applies to tasks measuring product metrics (latency, conversion, error rates). Does not apply to pure refactors or docs-only work. ## Check-at On 2026-09-01: count done metric tasks; do ≥ 80% include ## Before? EOF )" ``` `evidence` must cite ≥ 2 artifact IDs; use `--slug` on learnings because they are cited often. </Step> <Step title="Write skill-proposal (optional but typical here)"> Only when the learning implies a concrete skill edit: ```bash loopany artifact create --kind skill-proposal \ --slug require-before-on-metric-tasks \ --title "Require ## Before on metric tasks in task playbook" \ --target-skill skills/loopany-core/kinds/task.md \ --change-type modify \ --evidence "metric-outcomes-need-before-2026" \ --mentions "metric-outcomes-need-before-2026" \ --check-at 2026-09-01 \ --content "$(cat <<'EOF' ## Motivation Learning [[metric-outcomes-need-before-2026]]: three metric tasks closed without baselines. ## Proposed change **File:** skills/loopany-core/kinds/task.md **Intent:** Under `## Playbook` / `### Body`, elevate `## Before` from "strongly recommended" to **required** when the task title or Outcome references a measurable metric (LCP, p99, conversion, error rate). **Location:** After the Outcome bullet list, add a short gate: "Metric tasks: append ## Before before status done." ## Expected effect - Short term: agents pause to capture baseline screenshots or query snapshots. - Long term: reflect can falsify beliefs from outcomes; fewer duplicate learnings. ## Check-at 2026-09-01 — sample 10 done metric tasks; measure ## Before presence rate. EOF )" ``` Default status is `pending`. Frontmatter uses camelCase flags on the CLI (`--target-skill`, `--change-type`, `--check-at`). </Step> <Step title="Verify evidence chain"> ```bash loopany trace require-before-on-metric-tasks --direction backward ``` Expect backward reachability to the learning and cited tasks (via `mentions` / `evidence` frontmatter and explicit `refs` if added). </Step> </Steps> ```mermaid stateDiagram-v2 [*] --> todo: task created todo --> running: artifact status running --> done: Outcome appended done --> [*]: evidence for reflect [*] --> active: learning created active --> superseded: new learning supersedes [*] --> pending: skill-proposal created pending --> accepted: user accept + Outcome pending --> rejected: user reject + Outcome ``` ## Phase 3 — Accept proposal and apply skill diff Acceptance is human-gated (`agent proposes, human accepts`). The reflect skill's **Proposal Apply** mode performs the edit. <Steps> <Step title="List pending proposals"> ```bash loopany artifact list --kind skill-proposal --status pending ``` </Step> <Step title="Read proposal, learning, and target file"> ```bash loopany artifact get require-before-on-metric-tasks loopany refs require-before-on-metric-tasks --direction out --relation mentions loopany artifact get metric-outcomes-need-before-2026 ``` Read `targetSkill` from proposal frontmatter (e.g. `skills/loopany-core/kinds/task.md` in the repo checkout). </Step> <Step title="Apply only the described change"> Edit the target file faithfully—scope is bounded by `## Proposed change`. Do not edit skills outside the proposal contract. </Step> <Step title="Record Outcome and flip status"> ```bash loopany artifact append require-before-on-metric-tasks --section Outcome --content "$(cat <<'EOF' Added metric-task gate under task.md Playbook § Body: ## Before required before done when Outcome cites measurable metrics. Interpretation: strengthens capture quality; does not auto-enforce at CLI layer. EOF )" loopany artifact status require-before-on-metric-tasks accepted \ --reason "playbook updated per proposal" ``` `skill-proposal` kind requires `## Outcome` on both `accepted` and `rejected` terminal statuses. </Step> <Step title="Git commit target + proposal"> ```bash git add skills/loopany-core/kinds/task.md git add "$LOOPANY_HOME/artifacts/skill-proposals/require-before-on-metric-tasks.md" git commit -m "accept skill-proposal: require ## Before on metric tasks" ``` </Step> </Steps> ```mermaid sequenceDiagram participant User participant Reflect as loopany-reflect participant CLI as loopany CLI participant WS as $LOOPANY_HOME participant Git as git User->>Reflect: reflect / accept proposal Reflect->>CLI: artifact list --kind task --status done CLI->>WS: read tasks + Outcomes Reflect->>CLI: artifact create --kind learning Reflect->>CLI: artifact create --kind skill-proposal CLI->>WS: pending proposal User->>Reflect: accept require-before-on-metric-tasks Reflect->>Reflect: edit targetSkill file Reflect->>CLI: artifact append Outcome Reflect->>CLI: artifact status accepted Reflect->>Git: commit skill + proposal artifact ``` ### Reject path (contrast) ```bash loopany artifact append <proposal-slug> --section Outcome --content "Rejected: change belongs in capture skill, not task kind." loopany artifact status <proposal-slug> rejected --reason "wrong target" ``` Rejected proposals are listed on the next reflect pass so the same rule is not re-suggested. ## Verification checklist | Check | Command / signal | |-------|------------------| | Three done tasks | `loopany artifact list --kind task --status done` → count ≥ 3 | | Outcomes present | `loopany artifact get <task-slug>` → body contains `## Outcome` | | Learning active | `loopany artifact list --kind learning --status active` | | Proposal terminal | `loopany artifact get require-before-on-metric-tasks` → `status: accepted`, `## Outcome` | | Skill diff landed | `git log -1 -- skills/loopany-core/kinds/task.md` | | No duplicate reflect | `evidence` on new learnings excludes already-processed task slugs | <Warning> Do not edit `SKILL.md` or kind files without a `skill-proposal` artifact. Direct skill edits bypass the evidence chain and break the "agent proposes, human accepts" constraint from project architecture. </Warning> ## Status machines (quick reference) | Kind | Initial | Terminal transitions | |------|---------|------------------------| | `task` | `todo` | `done`, `failed` require `## Outcome` (playbook) | | `learning` | `active` | `superseded`, `archived` require `## Outcome` (playbook) | | `skill-proposal` | `pending` | `accepted`, `rejected` require `## Outcome` (playbook) | CLI `artifact status` enforces **legal edges only** (e.g. `pending` → `accepted`). Body section gates are agent discipline unless a future validator adds them. ## Anti-patterns this example avoids - Reflecting on one task (insufficient pattern). - Creating a `skill-proposal` without a backing `learning`. - Re-proposing a `rejected` change without reading `loopany artifact list --kind skill-proposal --status rejected`. - Accepting without `## Outcome` on the proposal. - Editing beyond `## Proposed change` scope. ## Automated regression `test/skill-regression.sh` installs four skills into a temp workspace, seeds a mission, and runs Claude-driven scenarios—including scenario 8 (reflect declines when &lt; 3 tasks). Run from repo root: ```bash ./test/skill-regression.sh # all scenarios ./test/skill-regression.sh 8 # reflect threshold only ./test/skill-regression.sh --dry-run ``` Requires `claude` CLI and API credentials; see script header for prerequisites. Unit/E2E CLI tests cover `artifact append` / `status` and lifecycle flows in `test/scenario.e2e.test.ts` (signal → task → `## Outcome` → `done`) but do not automate the full reflect → accept → git path—that remains skill-guided. ## Related pages <CardGroup> <Card title="Self-improvement loop" href="/self-improvement-loop"> Concept: Outcome evidence, learnings, proposals, checkAt, and the no-direct-skill-edit rule. </Card> <Card title="Reflect workflow" href="/reflect-workflow"> Operational reflect and proposal-apply steps in depth. </Card> <Card title="Kinds and validation" href="/kinds-and-validation"> Dynamic schemas, status machines, and immutable write semantics. </Card> <Card title="Artifact commands" href="/artifact-commands"> `create`, `append`, `status`, and per-kind `--field` flags used above. </Card> <Card title="Build and test" href="/build-and-test"> `bun test`, E2E workspaces, and `skill-regression.sh` requirements. </Card> </CardGroup> --- ## 21. Build and test > bun install, typecheck, bun test unit/e2e, compiled binary via bun build --compile, and skill-regression.sh requirements (claude CLI, API key). - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/21-build-and-test.md - Generated: 2026-06-05T19:05:11.662Z ### Source Files - `package.json` - `tsconfig.json` - `README.md` - `test/cli.e2e.test.ts` - `test/skill-regression.sh` - `test/helpers/cli.ts` - `CLAUDE.md` --- title: "Build and test" description: "bun install, typecheck, bun test unit/e2e, compiled binary via bun build --compile, and skill-regression.sh requirements (claude CLI, API key)." --- loopany ships as a Bun-first TypeScript ESM project: `package.json` defines `install`, `typecheck`, `test`, `build`, and `dev` scripts; the default verification path is `bun install`, `bun run typecheck`, and `bun test` (235 tests across 15 files). A compiled CLI lands at `bin/loopany` via `bun build --compile`. Agent-skill behavior is validated separately by `test/skill-regression.sh`, which drives Claude Code with live API calls. ## Prerequisites | Requirement | Used for | |-------------|----------| | [Bun](https://bun.sh) | Runtime, test runner, bundler, and `bun build --compile` | | TypeScript 5.x (devDependency) | `bun run typecheck` (`tsc --noEmit`) | | Temp filesystem access | E2E tests create scratch workspaces under the OS temp dir | | `claude` CLI (≥ 2.0) + Anthropic API credentials | `test/skill-regression.sh` only | `tsconfig.json` targets ES2022, `moduleResolution: "bundler"`, `strict: true`, and includes `src/**/*` and `test/**/*`. There is no `engines` field in `package.json`; development and tests assume Bun, not Node, for spawning the CLI and running the suite. ## Install dependencies ```bash bun install ``` This pulls runtime deps (`yaml`, `zod`, `@huggingface/transformers`) and dev deps (`typescript`, `@types/bun`). For a global `loopany` command during local development, also run `bun link` after install (see [Installation](/installation)). ## npm scripts | Script | Command | Purpose | |--------|---------|---------| | `dev` | `bun run src/cli.ts` | Run the CLI from source without compiling | | `build` | `bun build --compile --outfile bin/loopany src/cli.ts` | Produce a single static binary | | `test` | `bun test` | Full unit + E2E suite | | `typecheck` | `tsc --noEmit` | Static type check (no emit) | <Note> `CLAUDE.md` mentions `bun run test:e2e`, but that script is **not** defined in `package.json`. Run E2E files explicitly (see below) or use `bun test` for the full suite. </Note> ## Typecheck ```bash bun run typecheck ``` Runs `tsc --noEmit` against `src/` and `test/` with Bun types. A clean run produces no output and exit code `0`. ## Test suite overview `bun test` discovers all `*.test.ts` files under `test/`. The repo uses filename convention to separate layers: ```text test/ ├── *.test.ts # unit / module tests (11 files) ├── *.e2e.test.ts # CLI subprocess + real workspace (4 files) ├── helpers/cli.ts # spawns `bun src/cli.ts` with LOOPANY_HOME └── skill-regression.sh # optional; Claude Code + API (not bun test) ``` ```mermaid flowchart TB subgraph bun_test["bun test"] U[Unit tests<br/>artifact-store, kind-registry, search-store, …] E[E2E tests<br/>cli.e2e, search.e2e, scenario.e2e, migration e2e] end subgraph harness["E2E harness"] H[test/helpers/cli.ts] CLI[src/cli.ts via Bun.spawn] WS["$LOOPANY_HOME temp dir"] end subgraph skill_reg["skill-regression.sh"] CC[claude -p] SK[skills: core, capture, reflect, review] end U --> bun_test E --> H --> CLI --> WS skill_reg --> CC --> CLI skill_reg --> SK ``` ### Full run ```bash bun test ``` Typical result: **235 pass**, **0 fail**, across **15 files** (~45–50s on a developer machine). The suite uses real directories and files; E2E tests do not mock the filesystem. ### Unit tests only Unit files omit the `.e2e.` segment in the filename. Examples: `test/slug.test.ts`, `test/kind-registry.test.ts`, `test/search-store.test.ts`, `test/artifact-store.test.ts`, `test/migration-framework.test.ts`. Run a subset: ```bash bun test test/slug.test.ts test/kind-registry.test.ts ``` Roughly **136 tests** live in the 11 non-`.e2e.` files (total minus the four E2E files). ### E2E tests only Four files, **99 tests** total: | File | Focus | |------|--------| | `test/cli.e2e.test.ts` | `init`, `artifact/*`, `refs`, `trace`, `domain`, `followups`, `search`, `doctor`, JSON stdout | | `test/search.e2e.test.ts` | `reindex` / `search` with `--no-embed` (no ONNX download in CI-style runs) | | `test/scenario.e2e.test.ts` | Full lifecycle: signal → person → task → refs → followups → done + `## Outcome` | | `test/migration-v0.1-to-v0.2.e2e.test.ts` | v0.1 synthetic workspace → five migration scripts → doctor-clean v0.2 | Run E2E only: ```bash bun test \ test/cli.e2e.test.ts \ test/search.e2e.test.ts \ test/scenario.e2e.test.ts \ test/migration-v0.1-to-v0.2.e2e.test.ts ``` ### E2E harness behavior `test/helpers/cli.ts` resolves `src/cli.ts`, spawns `bun` with `LOOPANY_HOME` set to a fresh temp directory from `newWorkspace()`, and returns `{ stdout, stderr, code }`. Every CLI E2E test exercises the same entrypoint agents use in development (`bun run src/cli.ts`), not the compiled binary. <Info> Search E2E intentionally passes `--no-embed` so runs avoid downloading the Hugging Face ONNX embedding model. Semantic embedding behavior is covered in `test/search-store.test.ts` with `NoopEmbedder` / vector math; production `reindex` without `--no-embed` uses `TransformersEmbedder` in `src/core/embedder.ts`. </Info> ## Compiled binary ```bash bun run build ``` Equivalent to: ```bash bun build --compile --outfile bin/loopany src/cli.ts ``` Output: `bin/loopany` (single executable, on the order of tens of MB). `bin/` is listed in `.gitignore` — build locally; do not expect it in git. Verify: ```bash ./bin/loopany --help ``` Distribution path for agents is still commonly `bun link` + `loopany` from `src/cli.ts` (`package.json` `bin` points at `./src/cli.ts`). The compiled binary is the planned single-artifact ship target described in `CLAUDE.md`. ## Skill regression (`test/skill-regression.sh`) Separate from `bun test`. Exercises **four** bundled skills (`loopany-core`, `loopany-capture`, `loopany-reflect`, `loopany-review`) through **10** Claude Code scenarios with real `claude -p` calls. ### Prerequisites - `claude` in `PATH` (Anthropic Claude Code CLI, documented as ≥ 2.0 in the script header) - Active Anthropic API configuration for `claude` - `bun` and repo checkout (script uses `bun $REPO_ROOT/src/cli.ts`, not requiring `loopany` on `PATH`) `loopany-resolver` is not symlinked into the regression harness; scenarios target the four workflow skills above. ### Usage ```bash ./test/skill-regression.sh # all 10 scenarios ./test/skill-regression.sh 4 # scenario 4 only ./test/skill-regression.sh --dry-run # print plan; skip claude calls ``` ### What the script does <Steps> <Step title="Prepare temp environment"> Creates `/tmp/loopany-skill-regression.*` with a fake `HOME` (skills under `~/.claude/skills/`), sets `LOOPANY_HOME` to a fresh workspace, symlinks the four skills. </Step> <Step title="Seed workspace"> Runs `loopany init`, creates a test `mission` artifact so bootstrap checks pass. </Step> <Step title="Run scenarios"> Each scenario calls `claude -p` with `--bare`, `--permission-mode bypassPermissions`, `--add-dir` for workspace and repo, `--max-turns 15`, and tool allowlist `Bash Read Write Edit Glob Grep`. </Step> <Step title="Assert workspace state"> Checks artifact counts, file patterns, quality-gate behavior (e.g. scenario 4 must not create artifacts), reflect threshold (scenario 8), signal→task upgrade (scenario 9). </Step> </Steps> Expect **~5 minutes** for a full pass (10 API-backed agent turns). README notes isolated runs: each scenario shares one seeded workspace, not 10 separate inits. On failure, the script **disables cleanup** (`trap - EXIT`) and prints log paths (`$TMPBASE/scenario*.log`) so you can inspect Claude output. Exit code `1` when any assertion fails. ### Scenario map | # | Skill / theme | Validates | |---|----------------|-----------| | 1 | loopany-core | Signal created via natural language + CLI | | 2 | loopany-core | Task created and moved to `running` | | 3 | loopany-core | Note (fallback kind) | | 4 | loopany-capture | Quality gate skips trivial typo fix | | 5 | loopany-capture | Shipped PR → task artifact | | 6 | loopany-review | Daily followups on empty workspace | | 7 | loopany-review | Weekly doctor | | 8 | loopany-reflect | Declines learning when &lt;3 task outcomes | | 9 | Cross-skill | Signal upgraded to task, signal `addressed` | | 10 | Kind routing | Competitor intel routed to `signal` | <Warning> Skill regression incurs **API cost** and requires network access. It is not part of `bun test`. Use `--dry-run` in CI planning or local scaffolding without calling Claude. </Warning> ## Recommended verification order <Steps> <Step title="Fast gate"> `bun install && bun run typecheck && bun test` </Step> <Step title="Binary (optional)"> `bun run build && ./bin/loopany --help` </Step> <Step title="Before skill changes"> `./test/skill-regression.sh` (or targeted scenario number) </Step> </Steps> ## Troubleshooting | Symptom | Likely cause | Action | |---------|----------------|--------| | `tsc` errors after pull | Types out of sync | `bun install`, re-run `bun run typecheck` | | E2E timeout / slow run | 88 CLI tests spawn many processes | Run single file: `bun test test/cli.e2e.test.ts` | | Search E2E vs prod divergence | E2E uses `--no-embed` | Manually test `loopany reindex` without `--no-embed` in a scratch workspace | | `skill-regression.sh`: command not found `claude` | Claude Code CLI missing | Install Claude Code; ensure `claude` on `PATH` | | Skill regression auth errors | No API key / expired creds | Configure Anthropic credentials for `claude` | | Scenario 9 fails | Scenario 1 did not create a signal | Run full suite or fix scenario 1 logs | | `bin/loopany` missing | Not built or cleaned | `bun run build` | For workspace health after migrations or failed runs, use `loopany doctor` ([Doctor and troubleshooting](/doctor-and-troubleshooting)). ## Related pages <CardGroup> <Card title="Installation" href="/installation"> Clone, `bun link`, `LOOPANY_HOME`, and agent harness wiring. </Card> <Card title="CLI reference" href="/cli-reference"> Full command surface exercised by `cli.e2e.test.ts`. </Card> <Card title="Artifact lifecycle example" href="/artifact-lifecycle-example"> Mirrors `test/scenario.e2e.test.ts` and skill-regression cross-skill flows. </Card> <Card title="Skills library" href="/skills-library"> Skills installed and tested by `skill-regression.sh`. </Card> <Card title="Schema migration" href="/schema-migration"> v0.1→v0.2 path covered by `migration-v0.1-to-v0.2.e2e.test.ts`. </Card> </CardGroup> --- ## 22. Doctor and troubleshooting > doctor checks (workspace, schema version, kinds, artifacts, references, onboarding), common failures (WorkspaceNotFound, Zod validation, missing search index), and factory/reindex recovery steps. - Page Markdown: https://grok-wiki.com/public/docs/superdesigndev-loopany-97bd9ab97ae8/pages/22-doctor-and-troubleshooting.md - Generated: 2026-06-05T19:03:52.956Z ### Source Files - `src/commands/doctor.ts` - `src/core/engine.ts` - `src/commands/search.ts` - `src/commands/reindex.ts` - `test/cli.e2e.test.ts` - `INSTALL_FOR_AGENTS.md` - `src/commands/migrate.ts` --- title: "Doctor and troubleshooting" description: "doctor checks (workspace, schema version, kinds, artifacts, references, onboarding), common failures (WorkspaceNotFound, Zod validation, missing search index), and factory/reindex recovery steps." --- `loopany doctor` runs deterministic integrity checks over `$LOOPANY_HOME` and exits **0** when every check with status `fail` is clear; **warnings** (`warn`) do not fail the command. The command bootstraps with `skipVersionCheck: true`, so it can report schema mismatches instead of throwing `SchemaVersionMismatchError`. Search health is separate: `loopany search` warns when `search.db` is missing; rebuild with `loopany reindex`. ## Command surface ```bash loopany doctor [--format human|json] ``` | Output | Behavior | |--------|----------| | Human (default) | Columnar check lines with `✓` / `⚠` / `✗` and indented `problems` | | JSON | `{ workspace, checks[], ok }` — each check has `name`, `status`, `detail`, optional `problems[]` | | Exit code | `0` if `ok: true`; `1` if any check has `status: "fail"` | <ParamField body="--format" type="human | json"> Human is the default. JSON is intended for agents and weekly review scripts (`loopany doctor --format json`). </ParamField> <ResponseField name="ok" type="boolean"> `true` only when no check has `status: "fail"`. Warnings still yield `ok: true`. </ResponseField> ## What doctor checks Doctor is **integrity-only**: no LLM passes, no body-content heuristics (TODO scans belong in reflect), and **no `search.db` probe**. ```mermaid flowchart TB subgraph bootstrap["Bootstrap (doctor only)"] B["bootstrap({ skipVersionCheck: true })"] end subgraph checks["Deterministic checks"] W[workspace] SV[schema version] K[kinds] A[artifacts + Zod] R[references / dangling edges] O[onboarding: self + active mission] MC[mission coverage — warn] DC[domain coverage — warn] end B --> W --> SV --> K --> A --> R --> O --> MC --> DC ``` | Check | Status on problem | What it validates | |-------|-------------------|-------------------| | `workspace` | always `ok` if doctor runs | Bootstrap succeeded; reports `engine.root` | | `schema version` | `fail` | `config.yaml` `schemaVersion` vs binary `SCHEMA_VERSION` (`0.2.0`) | | `kinds` | `fail` | Every kind under `kinds/` (and enabled domain packs) parses; lists `registry.issues` | | `artifacts` | `fail` | Every indexed artifact’s frontmatter passes its kind’s dynamic Zod schema | | `references` | `fail` | No dangling edges in `references.jsonl` or implicit graph (missing `from`/`to`) | | `onboarding` | `fail` | Artifact id `self` exists; ≥1 `mission` with `status: active` | | `mission coverage` | `warn` | Each `task` has a `mentions[]` entry pointing at a mission id (skipped if no missions) | | `domain coverage` | `warn` | Artifacts with `domain` set use a value in `enabled_domains` | <Warning> A fresh `loopany init` workspace fails **onboarding** until you create `--slug self` person and an active mission — matching `ONBOARDING.md` Phase 3 and e2e expectations. </Warning> ### Onboarding expectations Doctor requires: - `loopany artifact create --kind person --slug self ...` - At least one `loopany artifact create --kind mission --status active ...` Legacy v0.1 ids like `prs-self` are migrated to `self` by `skills/migrations/v0.1.0-to-v0.2.0/`; doctor keys off the v0.2 slug id `self`. ## What doctor does not check | Gap | Where to handle it | |-----|-------------------| | Missing or stale `search.db` | `loopany reindex`; `loopany search` stderr hint | | Resolver / skill injection registered | Roadmap only (`INSTALL_FOR_AGENTS.md`); not in doctor today | | Semantic body quality | `loopany-reflect` skill | | Auto-reindex after each write | By design — reindex is explicit in v1 | ## Bootstrap gates vs doctor Most commands call `bootstrap()` **with** the schema guard. Doctor and migrate are exceptions. ```text $LOOPANY_HOME/ kinds/ ← must exist or WorkspaceNotFoundError config.yaml ← schemaVersion matched unless skipped artifacts/ references.jsonl search.db ← optional; not part of doctor ``` | Error | When | Message / recovery | |-------|------|-------------------| | `WorkspaceNotFoundError` | No `kinds/` under workspace root | `No loopany workspace at {root}. Run loopany init first.` | | `SchemaVersionMismatchError` | `config.schemaVersion() !== SCHEMA_VERSION` on normal bootstrap | Points at `skills/migrations/v{from}-to-v{to}/SKILL.md` and `loopany migrate v{from}-to-v{to}` | | Doctor still runs on mismatch | `skipVersionCheck: true` | `schema version` check `fail` with `run loopany migrate v…-to-v…` in `problems` | <Info> `LOOPANY_SKIP_VERSION_CHECK=1` also bypasses the guard (for migration scripts). `loopany migrate` always skips the check so it can describe the next migration on stale workspaces. </Info> Absent `schemaVersion` in `config.yaml`, the runtime assumes **`0.1.0`** (`ASSUMED_LEGACY_VERSION`) until `init` or migration writes the current version. ## Common failures and fixes ### WorkspaceNotFound ```bash loopany init # or LOOPANY_HOME=/path/to/brain loopany init ``` Default root: `~/loopany` unless `LOOPANY_HOME` is set. ### Schema version mismatch Symptom on most commands: ```text Error: Workspace schema is v0.1.0 but this binary expects v0.2.0. Read skills/migrations/v0.1.0-to-v0.2.0/SKILL.md and run loopany migrate v0.1.0-to-v0.2.0. ``` <Steps> <Step title="Inspect without crashing"> Run `loopany doctor` or `loopany migrate` — both bypass the version guard and report the gap. </Step> <Step title="Discover migration"> ```bash loopany migrate loopany migrate v0.1.0-to-v0.2.0 ``` `migrate` prints `SKILL.md` and ordered `bun run …/scripts/*.ts [--apply]` paths; the CLI does not auto-run migrations. </Step> <Step title="Run scripts, verify"> Execute migration scripts per the skill (typically dry-run first, then `--apply`). Finish with `loopany doctor` — `schema version` should be `ok`. </Step> </Steps> Pre-migration doctor failures should be fixed **before** migrating (`skills/migrations/v0.1.0-to-v0.2.0/SKILL.md`). ### Zod validation (CLI writes) `artifact create`, `set`, and `status` validate input at write time. Failures surface as: ```text Invalid input: - status: Invalid enum value... ``` Doctor re-validates **all** on-disk artifacts: problems look like `{id}: {first zod message}`. Fix by correcting frontmatter (`artifact set`) or editing the markdown file to match `kinds/<kind>.md`, then re-run doctor. ### Broken kind definitions Kind load errors are **tolerated at boot** but fail the **kinds** check: ```text kinds/some-kind.md: <parse or schema error> ``` Repair the kind markdown under `~/loopany/kinds/` (or domain `domains/<name>/kinds/`), then doctor again. ### Dangling references Sources include: - `loopany refs add` to a missing id - Hand-edited `references.jsonl` - Body `[[wiki-links]]` to non-existent slugs (indexed as implicit edges) Doctor lists lines like: ```text dangling: task-foo → bar-baz (led-to) ``` Create the target artifact, remove the edge, or fix the link text. ### Onboarding incomplete ```text self person artifact missing — run onboarding (Phase 3 step 2) no active mission — run onboarding (Phase 3 step 3) ``` Complete onboarding artifacts or follow `ONBOARDING.md` with your agent host. ### Mission / domain warnings These set `status: "warn"`; exit code stays **0**. - **Mission coverage:** add `--mentions <mission-id>` on tasks, or accept orphan tasks. - **Domain coverage:** `loopany domain enable <name>` or clear/remove invalid `domain` on artifacts. ## Search index: missing or stale `loopany search` does **not** error when `search.db` is absent: ```text loopany: no search index found — run `loopany reindex` first. ``` Stdout is `[]`; exit code **0** (scripts can probe safely). ### Reindex ```bash loopany reindex [--force] [--no-embed] ``` | Flag | Effect | |------|--------| | (none) | Incremental: skip artifacts whose mtime matches `search.db` | | `--force` | Delete `search.db` and rebuild FTS5 + embeddings from scratch | | `--no-embed` | Use `NoopEmbedder` (fast CI; keyword/FTS still work) | <ResponseField name="stdout JSON" type="object"> `{ indexed, skipped, removed, embedder: "transformers" | "noop" }`. Creates `$LOOPANY_HOME/search.db` on first run. Artifact CRUD does not auto-update the index in v1. </ResponseField> <Steps> <Step title="Populate after init or bulk edit"> ```bash loopany reindex --no-embed # quick smoke / CI loopany reindex # full hybrid (downloads embed model on first run) ``` </Step> <Step title="Verify search"> ```bash loopany search "your query" --kind note --limit 10 ``` </Step> <Step title="Recover deleted artifacts in index"> Re-run `reindex` without `--force`; removed on-disk files are dropped from the DB (`removed` count in JSON). </Step> </Steps> ## Factory UI (exploration, not repair) ```bash loopany factory [--port N] [--host H] [--no-open] ``` - Default: `http://127.0.0.1:4242`, opens browser unless `--no-open` - Requires **normal** bootstrap (schema version must match) - Read-only walkable view of nodes/edges from the same index doctor uses for references (not search) Use factory to **see** graph shape after doctor is green; use **reindex** when search is empty. Factory does not fix validation or migration issues. ## Operational playbooks ### After `loopany init` or agent install ```bash loopany doctor --format json # fix onboarding → kinds → artifacts → references loopany reindex --no-embed loopany search "smoke" --limit 3 ``` ### Weekly review (agent) Per `skills/loopany-review/references/weekly.md`: run `loopany doctor --format json`, summarize failures, propose fixes — **do not auto-fix** without user confirmation. ### Stale workspace on new binary 1. `loopany doctor` → read `schema version` problems 2. `loopany migrate` → run scripted migration with git snapshot per skill README 3. `loopany doctor` → all `fail` checks clear 4. `loopany reindex [--force]` if you rely on search ### Machine-readable health ```bash loopany doctor --format json | jq '.ok, .checks[] | select(.status=="fail")' ``` Exit **1** when `ok` is false — suitable for cron or CI **after** onboarding is complete. ## JSON report shape (reference) ```json { "workspace": "/Users/you/loopany", "ok": false, "checks": [ { "name": "schema version", "status": "fail", "detail": "workspace v0.1.0, binary expects v0.2.0", "problems": ["run `loopany migrate v0.1.0-to-v0.2.0`"] } ] } ``` Human output capitalizes check names and pads columns; semantics match JSON. <Tip> `VERSION` (package semver) and `SCHEMA_VERSION` (workspace format) drift independently — bumping the CLI does not always require migration; bumping `SCHEMA_VERSION` always ships a `skills/migrations/v*-to-v*/` directory. </Tip> ## Related pages <CardGroup> <Card title="Workspace setup" href="/workspace-setup"> Init, bundled kinds, onboarding, and first doctor pass. </Card> <Card title="Schema migration" href="/schema-migration"> `SchemaVersionMismatchError`, migrate discovery, and v0.1.0→v0.2.0 scripts. </Card> <Card title="Graph, search, and scheduling" href="/graph-search-commands"> `reindex`, `search`, `factory`, refs/trace, and followups. </Card> <Card title="Kinds and validation" href="/kinds-and-validation"> Kind markdown, dynamic Zod schemas, and immutable write rules. </Card> <Card title="Configuration reference" href="/configuration-reference"> `LOOPANY_HOME`, `LOOPANY_SKIP_VERSION_CHECK`, and `config.yaml` keys. </Card> <Card title="CLI reference" href="/cli-reference"> Full command surface and JSON stdout conventions. </Card> </CardGroup> ---