Pin — Session Decision Board
Persist decisions, questions, constraints, and corrections to a JSON file that survives context compaction. A companion hook injects the board into every tool call so the model never forgets.
Auto-Invoke Rules
After responding to any user message where a decision was made, a question was asked about a proposal, or a constraint was stated, IMMEDIATELY invoke /pin with the appropriate category. Do not ask permission — just pin it.
Examples of auto-invoke triggers:
- User: "yes go with bun" → respond normally, then
/pin ✅ use bun
- User: "what about the latency impact?" → respond normally, then
/pin ❓ split services — latency impact?
- User: "no skip auth for now" → respond normally, then
/pin ❌ auth layer — skip for MVP
- User: "MVP only, max 3 files" → respond normally, then
/pin 📌 MVP only, max 3 files
- User: "no I meant artisans not developers" → respond normally, then
/pin 🔧 target = artisans, not developers
Do NOT pin:
- Casual conversation, greetings
- Implementation details (code changes, file edits)
- Things already pinned (check board first)
Commands
| Command |
Action |
/pin ✅ <text> |
Pin approved item |
/pin ❓ <text> |
Pin pending question |
/pin ❌ <text> |
Pin killed/rejected item |
/pin 📌 <text> |
Pin scope constraint |
/pin 🔧 <text> |
Pin correction |
/pin show or /pin |
Display current board |
/pin rm <n> |
Remove pin by number |
/pin clear |
Clear all pins |
/pin clear triage |
Clear ✅/❓ only, keep 📌/❌/🔧 |
State File
Path: a session-scoped .session-logs/<slug>/pins.json file under a configurable base directory.
Derive slug from CWD:
GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
REL_PATH="${PWD#$GIT_ROOT/}"
SLUG=$(echo "$REL_PATH" | tr '/' '-')
PINS_DIR="$PRAXIS_DIR/.session-logs/$SLUG"
PINS_FILE="$PINS_DIR/pins.json"
Schema:
{
"items": [
{
"id": 1,
"type": "approved",
"emoji": "✅",
"content": "use bun everywhere",
"detail": "",
"ts": "2026-04-01T14:30:00Z"
}
],
"next_id": 2
}
Type mapping: ✅=approved, ❓=pending, ❌=killed, 📌=scope, 🔧=correction
Pin (/pin <emoji> <text>)
Parse the emoji prefix to determine type. Text after emoji is content. If text contains —, split into content and detail.
- Derive
PINS_FILE path (see State File above)
mkdir -p the directory
- Read existing file or init
{"items":[],"next_id":1}
- Check if content already pinned (exact match on content field) → if so, respond
⚠️ Already pinned. and stop
- Check limits: 5 items per type, 20 total. If category full, drop oldest item of that type.
- Append new item with
id=next_id, increment next_id
- Write file
- Respond:
📌 Pinned #N: <emoji> <content> — one line only, then resume prior work
Show (/pin show or /pin)
- Read
PINS_FILE
- If file missing or items empty:
📋 Pin board is empty.
- Display:
📋 Pin Board (5 items)
1. ✅ use bun everywhere (minor: keep fallback for CI)
2. ✅ split the PR into 2
3. ❓ split services — latency impact?
4. ❌ auth rewrite — out of MVP scope
5. 📌 MVP only, max 3 files
Remove (/pin rm <n>)
If no number: ⚠️ Usage: /pin rm <number>
- Read
PINS_FILE, find item with id === n
- If not found:
⚠️ Pin #N not found.
- Remove item, write file
- Respond:
🗑️ Pin #N removed.
Clear (/pin clear)
- Reset file to
{"items":[],"next_id":<keep current next_id>}
- Respond:
🗑️ Pin board cleared.
Clear Triage (/pin clear triage)
- Remove items where type is
approved or pending
- Keep items where type is
killed, scope, or correction
- Write file
- Respond:
🗑️ Triage cleared. <N> pins remaining.
Limits
- 5 items per type, 20 total
- When a category is full, drop the oldest item of that type (lowest id)
Philosophy
The pin board is a capture tool, not a management tool. Its only job is to hold decisions and constraints in a persistent, queryable form so that context compaction does not erase them. ALWAYS treat the board as a write-once, append-only record during a session — edits and removals are operations of last resort, not routine cleanup. A well-run session generates fewer than 20 pins; if you hit the limit frequently, the signals are too granular.
Key Behaviors
- One-line responses only. Never add commentary about pin content.
- Resume immediately. After any pin command, pick up the prior conversation exactly where it left off.
- Store verbatim. No cleanup, no categorization, no reformulation of user's words.
- Stable numbering. Gaps stay after deletion.
next_id always increments, never reuses.
- Gotcha — emoji parsing: Always split on the first emoji character, not the first space. Content like
📌 MVP only, max 3 files has the emoji as a 2-byte or 4-byte sequence; naive cut -d' ' will misparse on some shells.
When to Use
- The user explicitly approves or rejects a recommendation during a multi-step session ("yes, go with that approach", "no, skip auth for now").
- The user states a scope constraint or boundary that must survive context compaction ("MVP only", "max 3 files", "no external dependencies").
- The user corrects a misunderstanding or reframes a key concept ("I meant artisans, not developers").
- The user asks a clarifying question about a proposal that remains unresolved and will affect future decisions.
- A session involves multiple decision points and you need a shared reference that both parties can query mid-conversation.
When Not to Use
- The conversation is casual or exploratory with no binding decisions made.
- The item being tracked is an implementation detail (a code change, a file edit) rather than a decision or constraint.
- The content is already pinned — always check the board before adding a duplicate.
- The session is short and single-purpose; context compaction is not a risk.
- The user has explicitly asked to stop auto-pinning for the current session.
Anti-Patterns
- NEVER reformulate or paraphrase the user's words when pinning — store the exact wording as given. Why: Rewording introduces your interpretation and erases the original mental model; future agents will read the pin as authoritative, not as a paraphrase.
- NEVER pin implementation details like code edits or file changes — pins are for decisions, constraints, and corrections only. Why: Flooding the board with low-signal items makes it harder to spot the constraints that actually govern future choices.
- NEVER add commentary or explanation after a pin response — the response is one line only, then resume prior work. Why: This skill is a capture tool; any extra output derails the primary conversation and signals that the board matters more than the user's task.
- NEVER reuse or reassign an id after deletion —
next_id always increments forward. Why: Stable numbering prevents confusion when users reference pins by number across a session; id recycling silently corrupts that contract.
- NEVER skip the duplicate check before writing — always compare incoming content against existing items. Why: Duplicate pins create a false picture of the decision count and waste the 20-item budget on noise.
Usage Examples
Pinning a scope constraint mid-session:
# User says: "Keep this MVP only, no more than 3 new files"
/pin 📌 MVP only, max 3 files
# Response: 📌 Pinned #1: 📌 MVP only, max 3 files
# Session resumes immediately from where it left off
Pinning a decision reversal and viewing the board:
# User says: "Actually use PostgreSQL, not SQLite"
/pin 🔧 target database = PostgreSQL, not SQLite
# Response: 📌 Pinned #2: 🔧 target database = PostgreSQL, not SQLite
# Later: review all active pins
/pin show
# Response:
# 📋 Pin Board (2 items)
# 1. 📌 MVP only, max 3 files
# 2. 🔧 target database = PostgreSQL, not SQLite
Pinning an unresolved question and clearing triage items after resolution:
# Unresolved question surfaces during design
/pin ❓ split services — will latency exceed 200ms SLA?
# Response: 📌 Pinned #3: ❓ split services — will latency exceed 200ms SLA?
# After the question is answered, clear only approved/pending pins
/pin clear triage
# Response: 🗑️ Triage cleared. 1 pin remaining.
References
1---2name: pin3description: Pin session decisions, questions, objections, scope constraints, and corrections to a persistent board that survives context compaction. Use PROACTIVELY when: (1) user approves/rejects a recommendation, (2) user asks a clarifying question about a proposal, (3) user states a scope constraint, (4) user corrects a misunderstanding. Also use when user says pin, track this, mark as approved, board, show pins. Auto-invoke without user asking whenever a decision, question, or constraint is detected.4---56# Pin — Session Decision Board78Persist decisions, questions, constraints, and corrections to a JSON file that survives context compaction. A companion hook injects the board into every tool call so the model never forgets.910## Auto-Invoke Rules1112After responding to any user message where a decision was made, a question was asked about a proposal, or a constraint was stated, IMMEDIATELY invoke /pin with the appropriate category. Do not ask permission — just pin it.1314Examples of auto-invoke triggers:15- User: "yes go with bun" → respond normally, then `/pin ✅ use bun`16- User: "what about the latency impact?" → respond normally, then `/pin ❓ split services — latency impact?`17- User: "no skip auth for now" → respond normally, then `/pin ❌ auth layer — skip for MVP`18- User: "MVP only, max 3 files" → respond normally, then `/pin 📌 MVP only, max 3 files`19- User: "no I meant artisans not developers" → respond normally, then `/pin 🔧 target = artisans, not developers`2021Do NOT pin:22- Casual conversation, greetings23- Implementation details (code changes, file edits)24- Things already pinned (check board first)2526## Commands2728| Command | Action |29|---|---|30| `/pin ✅ <text>` | Pin approved item |31| `/pin ❓ <text>` | Pin pending question |32| `/pin ❌ <text>` | Pin killed/rejected item |33| `/pin 📌 <text>` | Pin scope constraint |34| `/pin 🔧 <text>` | Pin correction |35| `/pin show` or `/pin` | Display current board |36| `/pin rm <n>` | Remove pin by number |37| `/pin clear` | Clear all pins |38| `/pin clear triage` | Clear ✅/❓ only, keep 📌/❌/🔧 |3940## State File4142Path: a session-scoped `.session-logs/<slug>/pins.json` file under a configurable base directory.4344Derive slug from CWD:4546```bash47GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)48REL_PATH="${PWD#$GIT_ROOT/}"49SLUG=$(echo "$REL_PATH" | tr '/' '-')50PINS_DIR="$PRAXIS_DIR/.session-logs/$SLUG"51PINS_FILE="$PINS_DIR/pins.json"52```5354Schema:5556```json57{58 "items": [59 {60 "id": 1,61 "type": "approved",62 "emoji": "✅",63 "content": "use bun everywhere",64 "detail": "",65 "ts": "2026-04-01T14:30:00Z"66 }67 ],68 "next_id": 269}70```7172Type mapping: ✅=approved, ❓=pending, ❌=killed, 📌=scope, 🔧=correction7374## Pin (`/pin <emoji> <text>`)7576Parse the emoji prefix to determine type. Text after emoji is content. If text contains ` — `, split into content and detail.77781. Derive `PINS_FILE` path (see State File above)792. `mkdir -p` the directory803. Read existing file or init `{"items":[],"next_id":1}`814. Check if content already pinned (exact match on content field) → if so, respond `⚠️ Already pinned.` and stop825. Check limits: 5 items per type, 20 total. If category full, drop oldest item of that type.836. Append new item with `id=next_id`, increment `next_id`847. Write file858. Respond: `📌 Pinned #N: <emoji> <content>` — one line only, then resume prior work8687## Show (`/pin show` or `/pin`)88891. Read `PINS_FILE`902. If file missing or items empty: `📋 Pin board is empty.`913. Display:9293```94📋 Pin Board (5 items)95 1. ✅ use bun everywhere (minor: keep fallback for CI)96 2. ✅ split the PR into 297 3. ❓ split services — latency impact?98 4. ❌ auth rewrite — out of MVP scope99 5. 📌 MVP only, max 3 files100```101102## Remove (`/pin rm <n>`)103104If no number: `⚠️ Usage: /pin rm <number>`1051061. Read `PINS_FILE`, find item with `id === n`1072. If not found: `⚠️ Pin #N not found.`1083. Remove item, write file1094. Respond: `🗑️ Pin #N removed.`110111## Clear (`/pin clear`)1121131. Reset file to `{"items":[],"next_id":<keep current next_id>}`1142. Respond: `🗑️ Pin board cleared.`115116## Clear Triage (`/pin clear triage`)1171181. Remove items where type is `approved` or `pending`1192. Keep items where type is `killed`, `scope`, or `correction`1203. Write file1214. Respond: `🗑️ Triage cleared. <N> pins remaining.`122123## Limits124125- 5 items per type, 20 total126- When a category is full, drop the oldest item of that type (lowest id)127128## Philosophy129130The pin board is a **capture tool, not a management tool**. Its only job is to hold decisions and constraints in a persistent, queryable form so that context compaction does not erase them. ALWAYS treat the board as a write-once, append-only record during a session — edits and removals are operations of last resort, not routine cleanup. A well-run session generates fewer than 20 pins; if you hit the limit frequently, the signals are too granular.131132## Key Behaviors133134- **One-line responses only.** Never add commentary about pin content.135- **Resume immediately.** After any pin command, pick up the prior conversation exactly where it left off.136- **Store verbatim.** No cleanup, no categorization, no reformulation of user's words.137- **Stable numbering.** Gaps stay after deletion. `next_id` always increments, never reuses.138- **Gotcha — emoji parsing**: Always split on the first emoji character, not the first space. Content like `📌 MVP only, max 3 files` has the emoji as a 2-byte or 4-byte sequence; naive `cut -d' '` will misparse on some shells.139140## When to Use141142- The user explicitly approves or rejects a recommendation during a multi-step session ("yes, go with that approach", "no, skip auth for now").143- The user states a scope constraint or boundary that must survive context compaction ("MVP only", "max 3 files", "no external dependencies").144- The user corrects a misunderstanding or reframes a key concept ("I meant artisans, not developers").145- The user asks a clarifying question about a proposal that remains unresolved and will affect future decisions.146- A session involves multiple decision points and you need a shared reference that both parties can query mid-conversation.147148## When Not to Use149150- The conversation is casual or exploratory with no binding decisions made.151- The item being tracked is an implementation detail (a code change, a file edit) rather than a decision or constraint.152- The content is already pinned — always check the board before adding a duplicate.153- The session is short and single-purpose; context compaction is not a risk.154- The user has explicitly asked to stop auto-pinning for the current session.155156## Anti-Patterns157158- **NEVER reformulate or paraphrase the user's words when pinning** — store the exact wording as given. **Why:** Rewording introduces your interpretation and erases the original mental model; future agents will read the pin as authoritative, not as a paraphrase.159- **NEVER pin implementation details like code edits or file changes** — pins are for decisions, constraints, and corrections only. **Why:** Flooding the board with low-signal items makes it harder to spot the constraints that actually govern future choices.160- **NEVER add commentary or explanation after a pin response** — the response is one line only, then resume prior work. **Why:** This skill is a capture tool; any extra output derails the primary conversation and signals that the board matters more than the user's task.161- **NEVER reuse or reassign an id after deletion** — `next_id` always increments forward. **Why:** Stable numbering prevents confusion when users reference pins by number across a session; id recycling silently corrupts that contract.162- **NEVER skip the duplicate check before writing** — always compare incoming content against existing items. **Why:** Duplicate pins create a false picture of the decision count and waste the 20-item budget on noise.163164## Usage Examples165166**Pinning a scope constraint mid-session:**167168```bash169# User says: "Keep this MVP only, no more than 3 new files"170/pin 📌 MVP only, max 3 files171# Response: 📌 Pinned #1: 📌 MVP only, max 3 files172# Session resumes immediately from where it left off173```174175**Pinning a decision reversal and viewing the board:**176177```bash178# User says: "Actually use PostgreSQL, not SQLite"179/pin 🔧 target database = PostgreSQL, not SQLite180# Response: 📌 Pinned #2: 🔧 target database = PostgreSQL, not SQLite181182# Later: review all active pins183/pin show184# Response:185# 📋 Pin Board (2 items)186# 1. 📌 MVP only, max 3 files187# 2. 🔧 target database = PostgreSQL, not SQLite188```189190**Pinning an unresolved question and clearing triage items after resolution:**191192```bash193# Unresolved question surfaces during design194/pin ❓ split services — will latency exceed 200ms SLA?195# Response: 📌 Pinned #3: ❓ split services — will latency exceed 200ms SLA?196197# After the question is answered, clear only approved/pending pins198/pin clear triage199# Response: 🗑️ Triage cleared. 1 pin remaining.200```201202## References203204- [Architectural Decision Records (ADR)](https://adr.github.io/) — lightweight format for capturing and tracking architectural decisions; pin is the session-scoped, in-conversation equivalent205- [Cognitive Offloading and External Memory](https://en.wikipedia.org/wiki/Cognitive_offloading) — theoretical basis for persisting decisions to an external store so working memory is freed for active reasoning