From conport
Use when managing project context - task planning, progress tracking, documentation, searching project information. Must run init at session start.
How this skill is triggered — by the user, by Claude, or both
Slash command
/conport:conportThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **ConPort stores everything about the project:** tasks, documents, decisions, infrastructure.
ConPort stores everything about the project: tasks, documents, decisions, infrastructure. Without init, context is unavailable. Without search, you are answering blindly.
This skill carries the always-on discipline — what you must do every session regardless of topic. Deep, situational reference (recipe semantics, the gap system, the semantic pass, the full per-tool parameter tables, the block model, the documentation-graph callout reference, the spec append-only rationale) lives in the live docs — see the Live docs section below. Fetch the relevant page before acting on one of those topics; don't act from memory.
| Environment | Prefix |
|---|---|
| Claude Code CLI | mcp__conport__ |
| Claude.ai Chat | mcp__claude_ai_conport__ |
Claude Code CLI — the env var CONPORT_PROJECT_NAME is already available (from .claude/settings.local.json).
If it's not set, fall back in this priority order:
.git)Claude.ai Chat — ask the user (the file system is not available).
mcp__conport__init({
name: "<detected_name>",
skill_id: "conport",
skill_version: "14.11.2", // value of metadata.version in this SKILL.md frontmatter
client_type: "claude-code" // or claude-ai / cursor / codex
})
skill_id / skill_version / client_type are optional but strongly recommended — they let the server tell you when SKILL.md has been updated upstream so manual installs (Claude.ai project files, hand-copied skills) don't silently drift. Pick client_type from the list above; fall back to omitting it if running somewhere else.
If auto-detection of the project name did not work, ask the user.
Print summary: [CONPORT] {summary from response}
Execute instructions from the response (read files, apply rules)
Report backlog: N tasks in TODO, M in progress. Top 5: — use backlog.top from the response. Line format: Pk · #id title (n subtasks), skipping (n subtasks) when zero. Pk is effective_priority. Skip the whole block if backlog.total_todo == 0 and backlog.total_in_progress == 0.
If the project is empty (no decisions, no patterns, empty product_context) — offer the bootstrap flow from references/bootstrap.md.
If skill_update_available is present in the response — emit ONE short notice at the very start of your first reply (after the [CONPORT] line). Format:
[SKILL UPDATE] {skill_id} {current} → {latest} ({severity}). Changelog: {changelog_url} · Install: {install_guide}
current == "unknown" — phrase as cannot determine version, see {install_guide}.severity == "security" — emit a stronger line ([SECURITY UPDATE]) and recommend updating before proceeding.Never decide "is there an update" by hand-comparing version numbers.
The conport plugin (this skill, conport-agent, plugin.json, marketplace) is
versioned in lockstep — one number per release. A skill version is NOT
comparable to a plugin release number, and independent installable units
carry their own unrelated numbers. The ONLY correct signal is skill_update_available above:
present → update; absent → you're current. If you catch yourself reasoning
"my 14.x looks higher than the 12.x release, so I'm ahead" — stop, that's the
exact mistake this signal exists to prevent.
Save-first. Save decisions (sync_decision) and progress (log_progress)
as they happen, not in a batch at the end of the session. The moment a choice is
made or a step is finished, persist it — saving at the end loses rationale and
an interrupted session leaves nothing behind. (Live docs → core/save-first.)
Without init you cannot: answer questions about the project or work with tasks. Ignoring instructions is FORBIDDEN.
The at-a-glance trigger → tool map. Each table is navigational; for the deep semantics of a surface, fetch the live-docs page named in the table.
| Trigger | Tool |
|---|---|
| "We need to do X" | add_task with priority and estimated_seconds (priority 1-5 where 1=critical and 5=idle, default 3; epic rows leave effort NULL, sum-from-children at read time) |
| "X depends on Y" | add_task_dep |
| "Create an epic" / multi-step body of work | add_task with kind='epic' |
| "Break it into subtasks" | add_task with parent_task_id (parent must be kind='epic') |
| "Move task X under epic Y" / re-parent | update_task with parent_task_id (target must be kind='epic'; 0 detaches to root) |
| "Promote this task to an epic" | update_task with kind='epic' (task must be root — combine with parent_task_id=0 to detach + promote atomically) |
| "Demote this epic to a task" | update_task with kind='task' (epic must have no children) |
| Need a task in another project I own (no context switch) | add_linked_task with target_project name |
| Trigger | Tool |
|---|---|
| Starting work | update_task → IN_PROGRESS (before the first write on a task) |
| Done / Finished | update_task → DONE with resolution=... (see below) |
| Cancelled | update_task → CANCELLED with resolution=... (why dropped) |
| Blocked | update_task → BLOCKED |
IN_PROGRESS gate. Before your first ConPort write against a task, move it to
IN_PROGRESS. This keeps current_focus accurate and the backlog honest.
Closing tasks — always pass resolution:
On status=DONE or CANCELLED, pass a resolution argument with the verdict
(what was done / why cancelled). The server:
## Resolution section to the task's description (preserves the
original spec verbatim).progress_entry so the close shows up in
recent_activity, list_progress, and search.Do NOT call log_progress separately for task closes — that would
duplicate the entry. log_progress is for progress events that don't belong
to a single closing task (e.g. mid-implementation notes, infra changes).
| Trigger | Tool |
|---|---|
| "Show me the patterns" | list_patterns |
| "Record a pattern" | log_pattern with name, description, tags |
| "Update / rename / re-tag a pattern" | update_pattern |
| "What approaches do we use?" | list_patterns or search by topic |
| Trigger | Tool |
|---|---|
| Question about the project | search BEFORE answering |
| "What was decided about Y?" | search by topic |
| "Which projects do I own?" / bootstrapping without a known project_id | list_projects |
| Trigger | Tool |
|---|---|
| Technology choice | sync_decision |
| Trade-off with rationale | sync_decision |
| Amend / re-tag an existing decision | update_decision |
| Trigger | Tool |
|---|---|
| Standalone progress note (not a task close) | log_progress |
| Closing a task | update_task with resolution=... (auto-creates progress; do not also call log_progress) |
| Context has changed | update_active_context |
| Trigger | Tool |
|---|---|
| Spec / API docs | add_document |
| Wholesale body / metadata update | update_document(content=<full markdown>, ...) |
| List a doc's blocks (pick ulids before a surgical edit) | list_blocks(document_id) |
| Read / edit / insert / delete one block | get_block / update_block / insert_block / delete_block |
| Read a doc (with rendered Wave 5 stubs) | get_document (raw=true for unmodified markdown) |
| "Who references this doc/block?" | get_block_backlinks (omit block_ulid for whole doc) |
| "What's similar to this block but not yet linked?" | get_semantically_related_blocks |
| Overlapping content / linking two docs | author one callout — [!supersedes] / [!resolves] / [!extends] / [!relates-to] |
Block-level editing is the default for any narrow change — including specs.
Use update_block / insert_block / delete_block for surgical edits; only one
block re-embeds and the spec append-only invariant doesn't engage.
update_document(content=...) is the wholesale-rewrite channel.
Spec append-only invariant. update_document on a doc_type='spec' body
requires change_kind: amend (clarification/typo — allowed, logged with a
mandatory reason) or substantive (a meaningful claim change — rejected;
author a new spec and link the old one with link_items(relationship='supersedes')).
Don't chain block edits to rewrite a spec's claims either.
Don't create a doc when an edit will do. Never add_document whose purpose
is to describe / amend / react to an existing doc — that accumulates synthesis
drift. Edit the original, or author an addendum with an explicit callout
(> [!extends] [[doc-N]] / > [!supersedes] [[doc-N]]); default to
> [!relates-to] when unsure.
→ Deep detail: live docs projects/block-model, projects/spec-append-only,
core/documentation-callouts; full agent reference in
references/documentation_graph.md.
| Trigger | Tool |
|---|---|
| "Open task #N and brief me" | assemble_context with recipe='task_briefing', start_id='task-N' |
| "Implementation status of spec doc-N?" | assemble_context with recipe='spec_implementation_status', start_id='doc-N' |
| "What recipes are available?" | list_context_recipes |
| "Current architecture of subsystem X?" | render_current_architecture with scope=[...] |
| "Is this architecture doc safe to archive?" | audit_doc_l1_coverage(doc_id) |
| Init response shows gaps / "show all gaps" | review gaps.fresh; gap_list, gap_ack, gap_dismiss (reason), gap_dismiss_bulk, gap_undismiss, gap_stats |
| "Clean up the graph" | semantic_cleanup (one-click) |
| Manual semantic flow | semantic_pass_run(dry_run=true) → semantic_proposals_list → approve/reject/defer → semantic_proposals_apply; semantic_pass_stats |
start_id convention. Prefer the prefix form '<type>-<id>' ('task-271',
'doc-76'); type vocabulary task / doc / decision / pattern /
progress. The wikilink form '[[task-271]]' is also accepted verbatim. Plain
integers work as a legacy fallback but the prefix form gives a clean 400 on type
mismatch.
→ Deep detail: live docs projects/context-recipes, projects/gaps,
projects/semantic-pass.
The task tree is two levels and the database enforces it:
kind='task' — leaf node. May have parent_task_id pointing to an epic. Cannot have children.kind='epic' — root container. Always parent_task_id=NULL. Other tasks attach under it.No third level. Trying to attach a task under another task (kind=task with kind=task parent) is rejected at the DB level. The MCP/REST layer maps the rejection to a structured parent_not_epic payload with two recovery options.
parent_not_epic errorWhen add_task(parent_task_id=X) or update_task(parent_task_id=X) returns:
{
"error": "parent_not_epic",
"message": "Cannot attach a task under task-X — a task can only have an epic as parent.",
"context": {
"intended_parent": {"id": X, "kind": "task", "title": "..."},
"resolved_epic": {"id": Y, "kind": "epic", "title": "..."}
},
"suggestions": [
{"action": "promote_parent", "call": "update_task(task_id=X, kind='epic')"},
{"action": "attach_to_epic", "call": "add_task(..., parent_task_id=Y)"}
]
}
Decide by local context:
If resolved_epic is null, only promote_parent is offered — there's no ancestor epic in the chain.
task → epic: task must be root (no parent). Combine kind='epic' with parent_task_id=0 in one update_task call to atomically detach + promote.epic → task: epic must have no children. Close or reparent subtasks first.Cross-references stay task-N for both kinds — epic is a subtype, not a separate namespace.
Every reference to another ConPort item — in summary, rationale,
description, document body, commit messages — uses the canonical form
<type>-<number>.
Type vocabulary (lowercase): decision, task, doc, pattern,
progress. No aliases (no document, no tasks, no dec).
Forms accepted by the parser:
decision-321, task-271, doc-76.[[decision-321]], [[task-271]], [[doc-76]] — preferred inside
document bodies; the autolinker reifies them as item_links rows.[[doc-89#<block_ulid>]] — link to a
specific block (Wave 6).Anti-patterns (silently break tag/graph navigation):
| ❌ Don't write | ✅ Write |
|---|---|
decision #321 (typed legacy with #) | decision-321 |
Task #123, #124, #125 (untyped continuation) | task-123, task-124, task-125 |
#634 (untyped, ambiguous) | decision-634 (or the correct type) |
decision #1155 (pre-migration global id) | drop — autolinker can't resolve; cite the new per-project id |
Microcheck (extends POST-WRITE VERIFICATION):
Before the write call, scan your summary / rationale / description
payload for #\d+. If you find one:
<type>-<number> if you know the type.The parser currently accepts both legacy #N and canonical <type>-N so
older corpus stays linked; emit canonical-only in new writes.
MCP tools return JSON with a summary field. Use it to inform the user.
| After | Format |
|---|---|
init | [CONPORT] {summary} |
search | [ConPort: N results found for "query"] ... |
update_task | ✅ {summary} |
| Task DONE/CANCELLED | ✅ {summary} (progress entry was auto-logged from resolution) + suggest updating active_context |
MCP create / update tools return a slim payload (not the full entity body) to
save agent context — id, tags (echoed even as []), summary, and
context-specific fields (version, status, kind, …). Need the full body? Use
the matching read tool (get_task, get_document, list_decisions, …). The
slim tags / kind echo is your POST-WRITE verification channel.
(Live docs → core/post-write-verification.)
Some MCP clients silently drop an optional parameter when the surrounding
tool-call XML is malformed — the dropped value folds into the previous string
field and the call returns 200 OK with a truncated payload. A real incident lost
a sync_decision's tags array (they ended up inside rationale); the decision
landed untagged and broke graph navigation.
The server now rejects any write whose string field contains literal
tool-call fragments (<parameter …>, <invoke …>, </invoke>,
<function_calls>, antml:*) with a structured mcp_payload_contaminated
error — the bad write does not land; re-issue with the field cleaned. That guard
catches the worst class; the echo check below still catches the subtler cases.
After every create/update call that took optional fields, verify the response echo against intent:
| You passed | Check on the response |
|---|---|
tags=[...] | Response tags is non-empty and matches intent (count + values) |
description=... | description length ≈ what you sent (not visibly truncated) |
priority=N | priority equals N |
parent_task_id, tag_kinds, links | Field is present and equal to intent |
kind='epic' / kind='task' | Response kind matches the promote/demote you asked for |
On mismatch. Re-issue the call with the field re-stated (often one retry fixes the XML glitch). If a second attempt still loses it, flag the mismatch to the user verbatim ("graph integrity: tags lost on decision N, please re-run") rather than silently moving on — the damage is mute graph drift, easy to miss.
Applies to: sync_decision, add_task, update_task, log_progress,
log_pattern, add_document, update_document, update_active_context,
update_product_context, block ops (update_block, insert_block,
delete_block).
On an Invalid arguments for tool error:
path — the name of the broken parameterexpected — the required typepath | Fix |
|---|---|
project_id | "4" → 4 |
tags | "tag" → ["tag"] |
priority | "3" → 3 |
init been run?search been done?resolution?log_progress separately (it's auto-logged)?sync_decision?<type>-<number> form (no #N, no decision #321)?The deep, situational reference lives at https://conport.app and is the
single source of truth. Before acting on a deep topic, fetch the relevant
page. Public index: https://conport.app/llms.txt. (No web fetch? Use the
conport docs <topic> CLI.)
| Topic | Page |
|---|---|
| Save-first discipline | core/save-first |
| Knowledge-graph model (item links + GraphRAG) | core/graph-model |
| Cross-reference grammar | core/cross-references |
| Documentation-graph callouts (full reference) | core/documentation-callouts |
| Post-write verification / slim responses | core/post-write-verification |
assemble_context recipes / render_current_architecture | projects/context-recipes |
| Knowledge-base gaps | projects/gaps |
| Semantic pass | projects/semantic-pass |
| Spec append-only invariant | projects/spec-append-only |
| Block-level document model | projects/block-model |
| Task hierarchy | projects/task-hierarchy |
| Full per-tool parameter reference | projects/tool-reference |
Local references (shipped with the skill): references/command_list.md
(full MCP tool API), references/documentation_graph.md (long-form callout
reference), references/bootstrap.md (fresh-project onboarding).
v15.21.0 | Thinned skill — always-on discipline here, deep reference routed to live docs at conport.app | 83 MCP tools | Auto-detection | GraphRAG | Gap detection | Semantic pass | Cross-project linked tasks | Block-level document model | Recipe-pattern context assembly | Prefix-id convention | Skill version notification | Post-write payload verification | Slim MCP write responses | Task reparenting + 2-level hierarchy schema invariant | Canonical cross-reference grammar | Spec append-only enforcement | Documentation graph callouts + backlinks | current_architecture recipe + L1 capture-gap audit | Per-task time tracking | Server-side reject of MCP tool-call XML leakage
npx claudepluginhub shaurgon/conport-plugin --plugin conportCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.