ARC Labs Studio - Notes System (Obsidian-Backed)
Overview
The arc-notes-system defines how each ARC project mounts an Obsidian vault as
a gitignored notes/ symlink for operational, ephemeral, and per-feature notes.
It complements /arc-memory, which covers the committed memory/ tree of
evergreen documentation.
Memory vs Notes — The Four-Layer Model
| Layer |
Location |
Committed? |
Lifetime |
Purpose |
| Project memory |
memory/ (in repo) |
✅ Yes |
Long (evergreen) |
Architecture, decisions, patterns, dependencies — read by every contributor |
| Operational notes |
notes/ (symlink) |
❌ Gitignored |
Per-feature / per-session |
Working notes, troubleshooting, drafts — owned by the individual |
| Auto-memory |
~/.claude/projects/.../memory/ |
❌ Local |
Cross-session per machine |
Claude's own scratch context (MEMORY.md) |
| Plans (auto-synced) |
~/.claude/plans/ → notes/plans/ |
❌ Gitignored |
Snapshot at session end |
Claude Code plans archived into vault by Stop hook |
The split keeps the repo lean, lets each developer keep personal scratch
without polluting git history, and still surfaces cross-cutting decisions in
the committed memory/ tree.
Vault Folder Structure
The Obsidian vault is the source of truth. Each project owns one folder under
01 - Projects/:
$HOME/Documents/ObsidianVault/
└── 01 - Projects/
└── <ProjectName>/ # e.g. FavRes, PizzeriaLaFamiglia
├── features/ # Active per-feature notes (FVRS-XXX, PIZ-XXX, etc.)
├── archive/ # Archived feature notes (post-merge)
├── decisions/ # Cross-cutting decision records (mirrors memory/DECISIONS.md scope)
├── plans/ # Auto-synced Claude Code plans (Stop hook target)
└── troubleshooting/ # Debugging session notes written during active work
Each project's repo gets a notes/ symlink pointing at its vault folder:
ln -s "$HOME/Documents/ObsidianVault/01 - Projects/<ProjectName>" notes
Setup Steps (New ARC Repo)
Apply this checklist to any ARC project (app, package, or web) that needs the
notes integration. Templates live in this skill's templates/ directory.
Create the vault folder. In Obsidian, create
01 - Projects/<ProjectName>/ with the five subfolders above. Skip
subfolders the project doesn't need (see Applicability).
Add the SessionStart hook. Copy
templates/setup-notes.sh.template to .claude/hooks/setup-notes.sh,
replace <PROJECT_NAME> with the project's vault folder name, and
chmod +x it. The hook:
- Is idempotent (re-running is a no-op).
- Uses
$HOME — works across machines and worktrees.
- Skips silently if the vault is absent (cross-machine safe).
Add the Stop hook for plan auto-sync. Copy
templates/sync-plans.sh.template to .claude/hooks/sync-plans.sh and
chmod +x it. No placeholders to replace. The hook:
- Finds plans from
~/.claude/plans/ modified in the last 8 hours.
- Copies to
notes/plans/YYYY-MM-DD-<branch-slug>-<plan-stem>.md.
- Strips branch type prefix:
feature/FVRS-222-foo → FVRS-222-foo.
- Skips silently when
notes/plans/ is absent.
Gitignore the symlink. Add notes (no trailing slash — it's a symlink,
not a directory) to .gitignore. Never commit the symlink or its contents.
Wire the hooks into .claude/settings.local.json. Merge the contents
of templates/settings.local.json.fragment into the project's
settings.local.json. Both hooks.SessionStart and hooks.Stop must be
registered, and Write(./notes/**) must be in permissions.allow so
Claude can write troubleshooting and feature notes without prompting.
Document it in the project's CLAUDE.md. Append the contents of
templates/CLAUDE-md-section.md (with <ProjectName> replaced) to the
bottom of the project's CLAUDE.md so every Claude session knows the
convention.
Bootstrap the symlink locally. Trigger the SessionStart hook once
(start a Claude session, or run .claude/hooks/setup-notes.sh manually) to
create the notes symlink in the working tree.
Cross-Machine Support
- No hardcoded user paths. Every script and template uses
$HOME. Never
hardcode /Users/<name>/... — it breaks on other machines, on CI, and on
worktrees mounted under different paths.
- Vault path convention:
$HOME/Documents/ObsidianVault/01 - Projects/<ProjectName>.
- Fallback when the vault is absent. If the vault directory does not
exist on a given machine (CI, fresh clone, contributor without Obsidian),
setup-notes.sh exits 0 silently — notes/ is simply not created and the
Stop hook becomes a no-op. The project remains fully usable; the notes
integration is opt-in per machine.
- Worktrees. The SessionStart hook re-runs in each worktree, so each
worktree gets its own
notes/ symlink pointing at the same vault folder.
Naming Conventions
| Type |
Path |
Example |
| Active feature |
notes/features/<TICKET>-<short-description>.md |
notes/features/FVRS-138-expenses-graph.md |
| Archived feature |
notes/archive/<TICKET>-<short-description>-YYYYMMDD.md |
notes/archive/FVRS-138-expenses-graph-20260515.md |
| Decision |
notes/decisions/YYYY-MM-DD-<decision-title>.md |
notes/decisions/2026-05-15-tabrouter-isolation.md |
| Troubleshooting |
notes/troubleshooting/YYYY-MM-DD-<TICKET>-<short>.md |
notes/troubleshooting/2026-05-14-FVRS-150-spm-checksum.md |
| Plan (auto) |
notes/plans/YYYY-MM-DD-<branch-slug>-<plan-stem>.md |
notes/plans/2026-05-15-FVRS-222-onboarding-implementation.md |
Tickets use the project's Linear team key (FVRS, PIZ, etc.). Branch slug
strips the type prefix (feature/FVRS-222-foo → FVRS-222-foo).
When to Write Where
| Content |
Goes in |
Rationale |
| Architecture overview, layer rules |
memory/ARCHITECTURE.md (committed) |
Every contributor must read |
| Cross-cutting decision with long-term impact |
memory/DECISIONS.md (committed) |
Auditable record for the team |
| Project-wide pattern or gotcha |
memory/PATTERNS.md (committed) |
Prevents repeated mistakes |
| Active feature progress, scratch, TODOs |
notes/features/<TICKET>-...md |
Personal/working — not for the team |
| Debug session, root-cause walkthrough |
notes/troubleshooting/... |
Useful later, not part of the codebase |
| Local decision before promoting to ADR |
notes/decisions/... |
Draft; promote to memory/DECISIONS.md if it sticks |
| Claude Code plans |
notes/plans/... (auto via Stop hook) |
Snapshots, not authored by hand |
| Cross-session Claude scratch |
~/.claude/projects/.../memory/MEMORY.md (auto-memory) |
Claude-managed, not human-edited |
Rule of thumb: if the entire team needs it, commit it under memory/. If
only you (or the next debugging session) needs it, write it under notes/.
Applicability
| Project type |
Convention |
Notes |
| Apps (FavRes-iOS, etc.) |
Full convention |
features/, archive/, decisions/, plans/, troubleshooting/ |
| Packages (ARCNavigation, ARCDesignSystem, etc.) |
Reduced — memory/ + notes/decisions/ only |
Packages don't have per-feature tickets the way apps do; no features/ or archive/. Keep plans/ and troubleshooting/ if useful |
| Web (ARCKnowledge-Web, etc.) |
Full convention adapted |
Same five folders; ticket prefix follows the project's tracker |
Bootstrap Script (ARCDevTools)
A one-shot bootstrap script lives in ARCDevTools at
scripts/arc-setup-notes-system.sh. It performs all six setup steps
(creates the hook scripts from these templates, wires the JSON, appends the
CLAUDE.md section, and adds notes to .gitignore) for any ARC repo. Use it
when onboarding a new project; use this skill's templates when bootstrapping
manually or when scripting a custom variant.
Examples
Setting up a new app
User says: "Set up the notes system for the new ChessTrainer app."
- Create
$HOME/Documents/ObsidianVault/01 - Projects/ChessTrainer/ with
the five subfolders.
- Copy
templates/setup-notes.sh.template → .claude/hooks/setup-notes.sh,
replace <PROJECT_NAME> with ChessTrainer, chmod +x.
- Copy
templates/sync-plans.sh.template → .claude/hooks/sync-plans.sh,
chmod +x.
- Add
notes to .gitignore.
- Merge
templates/settings.local.json.fragment into
.claude/settings.local.json.
- Append
templates/CLAUDE-md-section.md (with ChessTrainer substituted)
to CLAUDE.md.
- Run
.claude/hooks/setup-notes.sh once — the symlink is created.
Capturing a debug session mid-flow
User says: "We just spent two hours debugging the SwiftData migration —
write it up."
- Resolve current ticket (e.g.
FVRS-205) and today's date.
- Create
notes/troubleshooting/2026-05-15-FVRS-205-swiftdata-version-mismatch.md.
- Capture symptoms, root cause, fix, and links to the relevant commit/PR.
- No commit —
notes/ is gitignored. Obsidian sync handles distribution.
Promoting a draft decision to an ADR
User says: "We've been using TabRouter isolation for two weeks and it's
sticking — promote it."
- Read
notes/decisions/2026-05-01-tabrouter-isolation.md.
- Append a new ADR entry to committed
memory/DECISIONS.md with the
final rationale.
- Optionally archive the draft by moving it under
notes/archive/.
- Commit only the
memory/DECISIONS.md change.
Related Skills
| If you need... |
Use |
| Committed evergreen docs |
/arc-memory |
| Worktrees setup (re-runs SessionStart per worktree) |
/arc-worktrees-workflow |
| Git workflow (branch slug origin) |
/arc-workflow |
| New project bootstrap |
/arc-project-setup |
1---2name: arc-notes-system3description: Operational notes system backed by an Obsidian vault, mounted into each ARC project as a gitignored `notes/` symlink. Standardizes how features, decisions, plans, troubleshooting sessions, and weekly reviews are captured outside of the committed `memory/` tree. Complements `/arc-memory` (committed evergreen docs). Use when "setting up notes", "configuring Obsidian vault for project", "second brain", "notes symlink", "sync plans to Obsidian", "feature notes folder", "troubleshooting notes", "decision records", "weekly review note", or "where should I write this note".4license: MIT5---67# ARC Labs Studio - Notes System (Obsidian-Backed)89## Overview1011The `arc-notes-system` defines how each ARC project mounts an Obsidian vault as12a gitignored `notes/` symlink for operational, ephemeral, and per-feature notes.13It complements `/arc-memory`, which covers the committed `memory/` tree of14evergreen documentation.1516### Memory vs Notes — The Four-Layer Model1718| Layer | Location | Committed? | Lifetime | Purpose |19|-------|----------|-----------|----------|---------|20| **Project memory** | `memory/` (in repo) | ✅ Yes | Long (evergreen) | Architecture, decisions, patterns, dependencies — read by every contributor |21| **Operational notes** | `notes/` (symlink) | ❌ Gitignored | Per-feature / per-session | Working notes, troubleshooting, drafts — owned by the individual |22| **Auto-memory** | `~/.claude/projects/.../memory/` | ❌ Local | Cross-session per machine | Claude's own scratch context (`MEMORY.md`) |23| **Plans (auto-synced)** | `~/.claude/plans/` → `notes/plans/` | ❌ Gitignored | Snapshot at session end | Claude Code plans archived into vault by Stop hook |2425The split keeps the repo lean, lets each developer keep personal scratch26without polluting git history, and still surfaces cross-cutting decisions in27the committed `memory/` tree.2829## Vault Folder Structure3031The Obsidian vault is the source of truth. Each project owns one folder under32`01 - Projects/`:3334```35$HOME/Documents/ObsidianVault/36└── 01 - Projects/37 └── <ProjectName>/ # e.g. FavRes, PizzeriaLaFamiglia38 ├── features/ # Active per-feature notes (FVRS-XXX, PIZ-XXX, etc.)39 ├── archive/ # Archived feature notes (post-merge)40 ├── decisions/ # Cross-cutting decision records (mirrors memory/DECISIONS.md scope)41 ├── plans/ # Auto-synced Claude Code plans (Stop hook target)42 └── troubleshooting/ # Debugging session notes written during active work43```4445Each project's repo gets a `notes/` symlink pointing at its vault folder:4647```bash48ln -s "$HOME/Documents/ObsidianVault/01 - Projects/<ProjectName>" notes49```5051## Setup Steps (New ARC Repo)5253Apply this checklist to any ARC project (app, package, or web) that needs the54notes integration. Templates live in this skill's `templates/` directory.55561. **Create the vault folder.** In Obsidian, create57 `01 - Projects/<ProjectName>/` with the five subfolders above. Skip58 subfolders the project doesn't need (see Applicability).59602. **Add the SessionStart hook.** Copy61 `templates/setup-notes.sh.template` to `.claude/hooks/setup-notes.sh`,62 replace `<PROJECT_NAME>` with the project's vault folder name, and63 `chmod +x` it. The hook:64 - Is idempotent (re-running is a no-op).65 - Uses `$HOME` — works across machines and worktrees.66 - Skips silently if the vault is absent (cross-machine safe).67683. **Add the Stop hook for plan auto-sync.** Copy69 `templates/sync-plans.sh.template` to `.claude/hooks/sync-plans.sh` and70 `chmod +x` it. No placeholders to replace. The hook:71 - Finds plans from `~/.claude/plans/` modified in the last 8 hours.72 - Copies to `notes/plans/YYYY-MM-DD-<branch-slug>-<plan-stem>.md`.73 - Strips branch type prefix: `feature/FVRS-222-foo` → `FVRS-222-foo`.74 - Skips silently when `notes/plans/` is absent.75764. **Gitignore the symlink.** Add `notes` (no trailing slash — it's a symlink,77 not a directory) to `.gitignore`. Never commit the symlink or its contents.78795. **Wire the hooks into `.claude/settings.local.json`.** Merge the contents80 of `templates/settings.local.json.fragment` into the project's81 `settings.local.json`. Both `hooks.SessionStart` and `hooks.Stop` must be82 registered, and `Write(./notes/**)` must be in `permissions.allow` so83 Claude can write troubleshooting and feature notes without prompting.84856. **Document it in the project's `CLAUDE.md`.** Append the contents of86 `templates/CLAUDE-md-section.md` (with `<ProjectName>` replaced) to the87 bottom of the project's `CLAUDE.md` so every Claude session knows the88 convention.89907. **Bootstrap the symlink locally.** Trigger the SessionStart hook once91 (start a Claude session, or run `.claude/hooks/setup-notes.sh` manually) to92 create the `notes` symlink in the working tree.9394## Cross-Machine Support9596- **No hardcoded user paths.** Every script and template uses `$HOME`. Never97 hardcode `/Users/<name>/...` — it breaks on other machines, on CI, and on98 worktrees mounted under different paths.99- **Vault path convention:** `$HOME/Documents/ObsidianVault/01 - Projects/<ProjectName>`.100- **Fallback when the vault is absent.** If the vault directory does not101 exist on a given machine (CI, fresh clone, contributor without Obsidian),102 `setup-notes.sh` exits 0 silently — `notes/` is simply not created and the103 Stop hook becomes a no-op. The project remains fully usable; the notes104 integration is opt-in per machine.105- **Worktrees.** The SessionStart hook re-runs in each worktree, so each106 worktree gets its own `notes/` symlink pointing at the same vault folder.107108## Naming Conventions109110| Type | Path | Example |111|------|------|---------|112| Active feature | `notes/features/<TICKET>-<short-description>.md` | `notes/features/FVRS-138-expenses-graph.md` |113| Archived feature | `notes/archive/<TICKET>-<short-description>-YYYYMMDD.md` | `notes/archive/FVRS-138-expenses-graph-20260515.md` |114| Decision | `notes/decisions/YYYY-MM-DD-<decision-title>.md` | `notes/decisions/2026-05-15-tabrouter-isolation.md` |115| Troubleshooting | `notes/troubleshooting/YYYY-MM-DD-<TICKET>-<short>.md` | `notes/troubleshooting/2026-05-14-FVRS-150-spm-checksum.md` |116| Plan (auto) | `notes/plans/YYYY-MM-DD-<branch-slug>-<plan-stem>.md` | `notes/plans/2026-05-15-FVRS-222-onboarding-implementation.md` |117118Tickets use the project's Linear team key (`FVRS`, `PIZ`, etc.). Branch slug119strips the type prefix (`feature/FVRS-222-foo` → `FVRS-222-foo`).120121## When to Write Where122123| Content | Goes in | Rationale |124|---------|---------|-----------|125| Architecture overview, layer rules | `memory/ARCHITECTURE.md` (committed) | Every contributor must read |126| Cross-cutting decision with long-term impact | `memory/DECISIONS.md` (committed) | Auditable record for the team |127| Project-wide pattern or gotcha | `memory/PATTERNS.md` (committed) | Prevents repeated mistakes |128| Active feature progress, scratch, TODOs | `notes/features/<TICKET>-...md` | Personal/working — not for the team |129| Debug session, root-cause walkthrough | `notes/troubleshooting/...` | Useful later, not part of the codebase |130| Local decision before promoting to ADR | `notes/decisions/...` | Draft; promote to `memory/DECISIONS.md` if it sticks |131| Claude Code plans | `notes/plans/...` (auto via Stop hook) | Snapshots, not authored by hand |132| Cross-session Claude scratch | `~/.claude/projects/.../memory/MEMORY.md` (auto-memory) | Claude-managed, not human-edited |133134**Rule of thumb:** if the entire team needs it, commit it under `memory/`. If135only you (or the next debugging session) needs it, write it under `notes/`.136137## Applicability138139| Project type | Convention | Notes |140|--------------|-----------|-------|141| **Apps** (FavRes-iOS, etc.) | Full convention | `features/`, `archive/`, `decisions/`, `plans/`, `troubleshooting/` |142| **Packages** (ARCNavigation, ARCDesignSystem, etc.) | Reduced — `memory/` + `notes/decisions/` only | Packages don't have per-feature tickets the way apps do; no `features/` or `archive/`. Keep `plans/` and `troubleshooting/` if useful |143| **Web** (ARCKnowledge-Web, etc.) | Full convention adapted | Same five folders; ticket prefix follows the project's tracker |144145## Bootstrap Script (ARCDevTools)146147A one-shot bootstrap script lives in ARCDevTools at148`scripts/arc-setup-notes-system.sh`. It performs all six setup steps149(creates the hook scripts from these templates, wires the JSON, appends the150CLAUDE.md section, and adds `notes` to `.gitignore`) for any ARC repo. Use it151when onboarding a new project; use this skill's templates when bootstrapping152manually or when scripting a custom variant.153154## Examples155156### Setting up a new app157158User says: "Set up the notes system for the new ChessTrainer app."1591601. Create `$HOME/Documents/ObsidianVault/01 - Projects/ChessTrainer/` with161 the five subfolders.1622. Copy `templates/setup-notes.sh.template` → `.claude/hooks/setup-notes.sh`,163 replace `<PROJECT_NAME>` with `ChessTrainer`, `chmod +x`.1643. Copy `templates/sync-plans.sh.template` → `.claude/hooks/sync-plans.sh`,165 `chmod +x`.1664. Add `notes` to `.gitignore`.1675. Merge `templates/settings.local.json.fragment` into168 `.claude/settings.local.json`.1696. Append `templates/CLAUDE-md-section.md` (with `ChessTrainer` substituted)170 to `CLAUDE.md`.1717. Run `.claude/hooks/setup-notes.sh` once — the symlink is created.172173### Capturing a debug session mid-flow174175User says: "We just spent two hours debugging the SwiftData migration —176write it up."1771781. Resolve current ticket (e.g. `FVRS-205`) and today's date.1792. Create180 `notes/troubleshooting/2026-05-15-FVRS-205-swiftdata-version-mismatch.md`.1813. Capture symptoms, root cause, fix, and links to the relevant commit/PR.1824. No commit — `notes/` is gitignored. Obsidian sync handles distribution.183184### Promoting a draft decision to an ADR185186User says: "We've been using TabRouter isolation for two weeks and it's187sticking — promote it."1881891. Read `notes/decisions/2026-05-01-tabrouter-isolation.md`.1902. Append a new ADR entry to committed `memory/DECISIONS.md` with the191 final rationale.1923. Optionally archive the draft by moving it under `notes/archive/`.1934. Commit only the `memory/DECISIONS.md` change.194195## Related Skills196197| If you need... | Use |198|----------------|-----|199| Committed evergreen docs | `/arc-memory` |200| Worktrees setup (re-runs SessionStart per worktree) | `/arc-worktrees-workflow` |201| Git workflow (branch slug origin) | `/arc-workflow` |202| New project bootstrap | `/arc-project-setup` |