Skill Cards as Context Artifacts
The Weaver projects need a clear boundary between reusable guidance produced by one component and context selected by another component. A reviewed skill card is guidance authored and approved elsewhere; contextweaver's job is only to decide whether — and how — that guidance enters a given phase's prompt.
This page documents the mapping. It is documentation, not a new adapter: everything below uses existing contextweaver APIs and adds no dependency.
What a skill card is
A skill card is a small, reviewed unit of guidance — "when editing
sensitivity.py, treat changes as security-grade", or "prefer match
statements for protocol dispatch". Some other component (a reviewer, a curation
pipeline) decides the card is worth keeping. contextweaver receives the card as
data and scores it against the current query under the same budget pressure as
every other event.
The mapping
A skill card maps onto a single ContextItem.
There is no bespoke type — the existing fields carry everything a card needs:
| Skill-card concept | ContextItem field |
Notes |
|---|---|---|
| artifact id | id |
Stable, unique. Reuse the card's own id so provenance round-trips. |
| artifact text | text |
The guidance itself — what the model should read. |
| scope metadata | metadata["scope"] |
Where the card applies (module, task type, phase hint). Free-form. |
| sensitivity metadata | sensitivity |
One of Sensitivity.public / internal / confidential / restricted. Items whose sensitivity meets or exceeds the active ContextPolicy.sensitivity_floor are dropped or redacted (per sensitivity_action). |
| provenance metadata | metadata["provenance"] |
Who reviewed it, when, source commit/URL. Audit trail, not scored. |
| task-matching notes | text (+ metadata) |
Matching is lexical against text; see below. |
kind should be ItemKind.doc_snippet for reference guidance (or
ItemKind.policy for high-priority guidance — policy carries a higher kind
priority in scoring, but inclusion is still subject to per-kind limits
(max_items_per_kind) and the phase token budget, so it is not guaranteed to
appear in any given prompt). If a rule must always be present, pass it as the
header or footer keyword argument to build_sync() (its token cost is
reserved up front — see BuildStats.header_footer_tokens) or size the phase
budget to accommodate it. Both kinds
flow through the standard phase-filter → sensitivity → firewall → scoring →
dedup → budget pipeline with no special-casing.
Constructing a card
from contextweaver.types import ContextItem, ItemKind, Sensitivity
card = ContextItem(
id="skill:sensitivity-is-security-grade",
kind=ItemKind.doc_snippet,
text=(
"When editing context/sensitivity.py, treat changes as security-grade: "
"never weaken the default sensitivity floor or default drop action."
),
sensitivity=Sensitivity.internal,
metadata={
"scope": {"module": "context/sensitivity.py", "task": "edit"},
"provenance": {
"reviewed_by": "maintainers",
"source": "AGENTS.md#things-that-must-not-be-simplified",
},
},
)
Ingest it like any other event:
from contextweaver.context.manager import ContextManager
mgr = ContextManager()
mgr.ingest_sync(card)
Task matching
contextweaver does not run a separate skill-card matcher. The card competes
in the normal scoring pass: when the query carries tags, the similarity term
is tokenized Jaccard between the card's metadata["tags"] and the query
tags; otherwise it falls back to tokenized Jaccard between the card's text
and the query text. That similarity term is then combined with recency,
item-kind priority, and a token-cost penalty (see
context/scoring.py).
This is not TF-IDF or BM25 — those are routing backends, a separate
subsystem. The highest-scoring items that fit the phase budget are kept. Two
consequences worth designing for:
- Matching quality is a function of the card's
text. Put the words a query would use into the guidance itself; do not hide the trigger only in free-formmetadatasuch asscope, which is not scored. (The one scored metadata field ismetadata["tags"], which contributes tag overlap.) metadata["scope"]is yours to enforce. contextweaver preserves it but does not filter on it. If you want a card to apply only to one module, readmetadata["scope"]in your own pre-ingest filter and skip cards that do not apply before callingingest_sync.
Matching example
Query: "I'm editing context/sensitivity.py — anything I should know?"
The card above scores well: its text shares the editing, context,
sensitivity.py, sensitivity, and context/sensitivity.py tokens with the
query (contextweaver's tokenize() helper lowercases input and splits
compounds on ., /, and -, but does not stem — so write cards and
queries with overlapping word forms). With little competition from other items
on this query, the card survives scoring and lands in the compiled context.
The model sees the security-grade warning before it proposes a change.
Non-matching example
Query: "How do I add a new CrewAI adapter?"
The same card scores near zero — no lexical overlap with adapter/CrewAI terms — so it is dropped under budget pressure in favour of adapter-relevant items. The card stays in the event log (nothing is deleted); it simply does not enter this prompt. That is the intended behaviour: guidance is available everywhere but only surfaces where it is relevant.
See also
- How contextweaver Fits — the overall policy-vs-execution boundary
- Concepts —
ContextItem, phases, and the scoring pipeline - External Memory Backends — the related pattern for cross-session facts and episodes