Pin — Session Decision Board
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.
npx skills add github:pantheon-org/tekhne --skill agentic-harness--pinPersist 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_FILEpath (see State File above) mkdir -pthe 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, incrementnext_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 withid === 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
approvedorpending - Keep items where type is
killed,scope, orcorrection - 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_idalways increments, never reuses. - Gotcha — emoji parsing: Always split on the first emoji character, not the first space. Content like
📌 MVP only, max 3 fileshas the emoji as a 2-byte or 4-byte sequence; naivecut -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
WHY: rewording introduces your interpretation and erases the original mental model; future agents will read the pin as authoritative, not as a paraphrase.
BAD: user says “no I meant artisans not developers” and you pin “refine target persona”.
GOOD: pin 🔧 target = artisans, not developers using the user’s own wording.
NEVER pin implementation details like code edits or file changes
WHY: flooding the board with low-signal items makes it harder to spot the constraints that actually govern future choices.
BAD: /pin ✅ renamed helper in utils.ts.
GOOD: pin only decisions, constraints, questions, and corrections.
NEVER add commentary or explanation after a pin response
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.
BAD: 📌 Pinned #3 ... followed by a paragraph on why the pin matters.
GOOD: one line, then resume prior work.
NEVER reuse or reassign an id after deletion
WHY: stable numbering prevents confusion when users reference pins by number across a session; id recycling silently corrupts that contract.
BAD: renumber the remaining pins after /pin rm 2, or reset next_id.
GOOD: leave gaps and let next_id only ever increment.
NEVER skip the duplicate check before writing
WHY: duplicate pins create a false picture of the decision count and waste the 20-item budget on noise.
BAD: append the same content a second time.
GOOD: compare incoming content against existing items and respond ⚠️ Already pinned. on a match.
NEVER ask permission before pinning a detected decision
WHY: the skill is auto-invoked; asking turns a silent capture into an interruption and the decision may be lost to compaction.
BAD: “Should I pin this?” after the user approves a recommendation.
GOOD: respond normally, then pin immediately with the right category.
NEVER pin casual conversation or already-pinned items
WHY: greetings and repeats add noise and consume the per-type and total limits.
BAD: pin “thanks, sounds good” as an approval.
GOOD: check the board first and pin only genuine decisions and constraints.
NEVER use the wrong emoji or type for the user’s intent
WHY: /pin clear triage removes only approved and pending items, so a mistyped constraint can be wiped while a decision is kept.
BAD: pin a scope constraint with ✅.
GOOD: map ✅ approved, ❓ pending, ❌ killed, 📌 scope, 🔧 correction.
NEVER stop the conversation after pinning
WHY: the pin is a side effect; the user’s actual task is still waiting.
BAD: end the turn after 📌 Pinned #N.
GOOD: resume the prior work exactly where it left off.
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
| Topic | Reference | When to Use |
|---|---|---|
Read-only checks on pins.json |
references/inspecting-the-board.md | Verifying limits, duplicates, and numbering before or after a write |
| Edge-case responses | references/edge-cases.md | Handling missing ids, full categories, and empty boards |
External reading
- Architectural Decision Records (ADR): lightweight format for capturing and tracking architectural decisions; pin is the session-scoped, in-conversation equivalent
- Cognitive Offloading and External Memory: theoretical basis for persisting decisions to an external store so working memory is freed for active reasoning
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
No eval scenarios available.
Pin Edge Cases
Responses and behaviours for situations the main skill file only summarises.
| Situation | Behaviour |
|---|---|
Content already pinned (exact match on content) |
Respond ⚠️ Already pinned. and stop. Do not write the file. |
| Category already holds 5 items | Drop the oldest item of that type (lowest id), then append the new one. |
| Board already holds 20 items | Apply the per-type drop first; treat frequent hits on the limit as a sign the pins are too granular. |
/pin rm with no number |
Respond ⚠️ Usage: /pin rm <number>. |
/pin rm <n> where no item has that id |
Respond ⚠️ Pin #N not found. and leave the file unchanged. |
/pin show on a missing or empty file |
Respond 📋 Pin board is empty. Do not return an error. |
/pin clear |
Reset items to empty but keep the current next_id. |
Text contains — |
Split into content (before) and detail (after). |
| Emoji prefix parsing | Split on the first emoji character, not the first space. |
| After any pin command | Resume the prior conversation exactly where it left off. |
Inspecting and Validating pins.json
Read-only checks for the pin state file. These never modify the board, which matches the rule that /pin show must not rewrite the file.
Derive the file path
GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
REL_PATH="${PWD#$GIT_ROOT/}"
SLUG=$(echo "$REL_PATH" | tr '/' '-')
PINS_FILE="$PRAXIS_DIR/.session-logs/$SLUG/pins.json"
Count items against the limits
The limits are 5 items per type and 20 in total.
jq '.items | length' "$PINS_FILE"
jq '.items | group_by(.type) | map({type: .[0].type, count: length})' "$PINS_FILE"
Check for an existing pin before appending
The duplicate check is an exact match on the content field.
jq --arg c "use bun everywhere" '[.items[] | select(.content == $c)] | length' "$PINS_FILE"
A result above zero means respond ⚠️ Already pinned. and stop.
Confirm numbering stays stable
next_id must be greater than every id in the file, including after removals.
jq '{max_id: ([.items[].id] | max), next_id: .next_id}' "$PINS_FILE"
Type and emoji mapping
| Emoji | Type | Cleared by /pin clear triage |
|---|---|---|
| ✅ | approved | Yes |
| ❓ | pending | Yes |
| ❌ | killed | No |
| 📌 | scope | No |
| 🔧 | correction | No |