The Craftsman standard for LLM-powered features — prompt-injection surface, key and spend protection, PII reaching model APIs, and reliability/eval discipline. Use whenever work touches an LLM integration: a chatbot or agent, a RAG pipeline, an LLM call from a route or job, tool/function-calling, or reviewing prompts and completions. Trigger even on "add an AI feature", "is my chatbot secure", "my OpenAI bill exploded", or "why did the model call the wrong tool" without naming a provider or framework. Handoffs: see "Scope boundaries" in the body.
This skill encodes one engineer's standard for shipping LLM-powered features safely, applied the
same way across every repo. The method and opinions live here; the project specifics
(which provider, which SDK, which framework) live in the target repo's code and config — always
discover them, never assume or hardcode.
Operating principle — discover before you build
Different repos already have different pieces in place. Before changing anything, spend a few
minutes mapping the current posture so you extend rather than conflict:
package.json / lockfile / requirements → which LLM SDK is present (OpenAI, Anthropic, Vercel
AI SDK, LangChain, LlamaIndex)? Which is the default model tier?
grep for the API key — is it read through a validated server-side env schema, or does it leak
into a NEXT_PUBLIC_* / client-bundled / mobile-app constant?
Find every call site that sends a prompt — what user-controlled or retrieved content reaches
the system prompt or context window, and is there any structural separation between
instructions and untrusted content?
Check whether the integration uses tool-use / function-calling — what can the model actually
invoke, and is there a confirmation step before a consequential action fires?
Look for logging/observability around LLM calls — are raw prompts and completions logged, and
where do those logs live?
Check for rate limits, max_tokens caps, and timeouts on LLM-calling routes; check for retry
logic around tool actions.
Look for any eval harness, golden test set, or regression check tied to prompt changes.
State what you found, then propose the smallest set of changes that closes the gaps.
The AI layers (work in this order)
Prompt-injection surface — anything that reaches the model's context that isn't the
developer's own instructions (user input, retrieved documents, scraped pages, tool output) is
a potential injection vector. Structural separation between instructions and untrusted content,
allow-listed tools, and human confirmation for consequential actions are the mitigations — none
of them fully close the gap. See references/prompt-injection.md.
Keys & spend — provider keys never ship to a client bundle or mobile app; every LLM-calling
route has a rate limit, a max_tokens cap, and a bound on loop/agent iterations; streaming
requests abort when the client disconnects. See references/keys-and-spend.md.
Data privacy — inventory what PII leaves the building in a prompt to a third-party API,
check the provider's retention/training posture, and scrub prompts/completions before they hit
observability logs. See references/data-privacy.md.
Reliability & evals — provider outages are routine, so every call has a timeout and a
fallback; non-idempotent tool actions are never blindly retried; a minimal golden-case eval
harness catches prompt regressions before they ship. See references/reliability-evals.md.
Standing opinions (the non-negotiables)
These are the judgments that make output consistent across repos — apply them unless the user
overrides:
Untrusted content is never structurally indistinguishable from developer instructions.
User input, retrieved documents, and tool output are content, not commands — the integration is
designed so an instruction embedded in that content can't silently expand what the model is
willing to do.
Provider keys live server-side only. No LLM API key in a client bundle, mobile binary, or
anything a browser or device can read; every model call is proxied through your own backend.
Every LLM-calling endpoint has a rate limit, a token cap, and a timeout. An unbounded loop,
an unbounded max_tokens, or a hung request against a stalled provider is a spend bomb and an
availability bug waiting to happen.
Tool-calling that performs a consequential action gets a confirmation step or an allow-list.
A model that can call a tool is a model that can take real-world action on injected instructions
— treat that boundary with the same suspicion as remote code execution.
PII in a prompt to a third-party API is inventoried, not assumed away. If personal data
reaches a model provider, know what's sent, what the provider's retention/training terms say,
and whether logging pipelines are scrubbing it before storage.
Prompts that ship non-trivial agent/prompt behavior are versioned and covered by a golden-case
eval. A thin single-route MVP wrapper can flag a missing harness as 🟡 recommend, not automatic
🔴 — see reliability-evals.md applicability.
Workflow
Discover — map the current posture (provider/SDK, key handling, injection surface, tool
access, logging, rate limits, evals) and report the gaps.
Propose — ordered by the four layers above, highest-risk gap first, smallest viable changes.
Implement — against the repo's existing patterns (its SDK, its middleware chain, its env
schema, its observability pipeline).
Verify — confirm keys aren't reachable from the client, rate limits/token caps actually
fire, an injected instruction in test content doesn't expand tool access, and the eval suite
passes before a prompt change ships.
Scope boundaries
This skill owns the LLM-specific layer: what reaches the prompt, what the model can do, what leaves
the building, and whether it's reliable. Hand off at these lines:
Generic route authentication → craft-backend. This skill does not re-audit the auth boundary.
Secret storage mechanics (where keys live, how they rotate) → craft-security. This skill
covers key exposure through the model path — client-reachable keys, keys in prompts or logs.
Provider billing alerts as infra config → craft-infra. This skill owns application-level
spend-safety design: rate limits, token caps, and loop bounds on model-calling routes.
Rate-limit ownership, so the same gap isn't emitted four times: BE supplies the route
middleware mechanism; SEC owns login-abuse policy; INFRA owns edge capacity; AI owns LLM
cost/token limits.
Whole-project readiness → craft-audit, which routes here for the AI slice.
Reference index
Read the one matching the current task — they hold the concrete setup, not this overview:
references/prompt-injection.md — injection via user content and retrieved/scraped content
(indirect injection), tool-use/function-calling boundaries, output handling (XSS, downstream
injection), mitigation framing
references/keys-and-spend.md — client-bundle key leaks, per-route rate limits and token caps,
unbounded loops as spend bombs, streaming abort on disconnect, billing alerts, model tiering
references/data-privacy.md — PII inventory in prompts, provider retention/training flags,
scrubbing logged prompts/completions, user consent/disclosure, regional/enterprise endpoints
references/reliability-evals.md — timeouts and fallbacks, retry vs. non-idempotent tool
actions, minimal eval harness, structured-output validation, prompt versioning
Audit checklist (for craft-audit)
When craft-audit plans an AI pass for a scope, it turns this checklist into the plan.md
todo list — the checklist is owned by this skill, not improvised by the orchestrator. Tailor to
what discovery found: skip a step that genuinely doesn't apply with a one-line reason; never
silently drop one. Emit findings using craft-audit workspace.md → "Canonical findings.md emission format"
(authority). Heading grammar (variables required — do not hardcode NNN/severity/status):
## <scopeLabel>-AI-<NNN> · severity <🔴|🟡|🟢> · status <open|fixed|wontfix (reason)|regressed|fixed (merged into <ID>)>
Example only: ## <scopeLabel>-AI-001 · severity 🔴 · status open
Required fields under each heading, in order, with these exact labels:
**What breaks (plain language):** · **Technical:** · **Fix:** · **Fingerprint:** ·
**Last-checked:** (optional **Confidence:** — verified | inferred | unverified-from-repo, absent
means verified — then optional **Fix-attempt:** only from craft-fix).
Assign sequential NNN per (scope, domain); judge severity with craft-audit prioritization.md.
Forbidden: ### headings; ## ID · 🔴 · open shorthand; severity/status as body bullets.
Map the current posture — provider/SDK, key handling, injection surface, tool access,
logging, rate limits, evals — flagging client-exposed keys and unbounded loops →
SKILL.md "Operating principle — discover before you build"
Verify user-controlled and retrieved/scraped content is structurally separated from
developer instructions in the prompt; hunt for indirect-injection vectors (RAG documents,
web-scraped context, uploaded files) → references/prompt-injection.md
Check tool-use/function-calling boundaries — is every callable tool allow-listed, and does
a consequential action require confirmation rather than firing on an injected instruction?
→ references/prompt-injection.md
Confirm LLM output rendered as HTML/markdown is encoded, and output used to build a query
or command is treated as untrusted → references/prompt-injection.md
Confirm no provider API key is reachable from a client bundle, mobile app, or public env var;
verify calls are proxied through the backend → references/keys-and-spend.md
Verify every LLM-calling route has a rate limit, a max_tokens cap, and a bound on
agent/loop iterations; check streaming requests abort on client disconnect. Ownership: AI
owns LLM spend/cost limits; BE may still own the middleware mechanism — emit once, don't
triple-count with SEC/INFRA → references/keys-and-spend.md
Check for provider billing alerts/hard caps and model tiering (cheap model for cheap tasks)
→ references/keys-and-spend.md (cross-ref craft-infra for the alerting config itself)
Inventory PII flowing into prompts to third-party APIs; check provider retention/training
settings and note zero-retention options where offered → references/data-privacy.md
Confirm prompts/completions logged for observability are scrubbed of PII, and users are
disclosed that AI processes their data → references/data-privacy.md
For regulated or policy-constrained generation (medical, financial, legal, real-estate,
insurance), identify where the authoritative restrictions live and confirm they are enforced
either by a deterministic, fail-closed control (constrained templates, output validation
against the restriction list) or by an enforced human-review publication gate — the review
gate blocks publication reliably even though human judgment itself is not deterministic.
Restrictions injected as prompt text or per-tenant context fields are an input the model can
disregard, not enforcement; a doc the model never sees is neither
→ references/data-privacy.md
For factual or citation-bearing output published on a user's behalf, check there is an approved
evidence source and a claim-attribution, source-validation, or human-review path rather than
free-generated assertions. Require tenant-level prohibited-claim rules only where the product's
own policy or regulatory scope calls for them → references/reliability-evals.md
Check the model is selected and evaluated against the task's factuality and citation
requirements, not cost alone, and that observed fabricated citations are treated as
disqualifying regardless of tier. Model choice alone does not make citation-bearing output safe
— keep the evaluation and its mitigation rationale with the call site or its config
→ references/reliability-evals.md
Verify LLM calls have timeouts and a graceful-degradation fallback for provider outages, and
that retries never re-fire a non-idempotent tool action → references/reliability-evals.md
Check for schema validation (repair-or-reject) on structured LLM output before it's consumed
downstream → references/reliability-evals.md
Confirm eval discipline matches surface risk: golden-case harness is required (🔴-class
when missing) when shipping non-trivial prompt/agent behavior to production; for a thin
single-route MVP wrapper, flag missing eval as 🟡 opportunity / recommend, not automatic 🔴.
Prompts that do ship should still be versioned like code → references/reliability-evals.md
1---2name: craft-ai3description: The Craftsman standard for LLM-powered features — prompt-injection surface, key and spend protection, PII reaching model APIs, and reliability/eval discipline. Use whenever work touches an LLM integration: a chatbot or agent, a RAG pipeline, an LLM call from a route or job, tool/function-calling, or reviewing prompts and completions. Trigger even on "add an AI feature", "is my chatbot secure", "my OpenAI bill exploded", or "why did the model call the wrong tool" without naming a provider or framework. Handoffs: see "Scope boundaries" in the body.4---56# AI Craft78This skill encodes one engineer's standard for shipping LLM-powered features safely, applied the9same way across every repo. The **method and opinions** live here; the **project specifics**10(which provider, which SDK, which framework) live in the target repo's code and config — always11discover them, never assume or hardcode.1213## Operating principle — discover before you build1415Different repos already have different pieces in place. Before changing anything, spend a few16minutes mapping the current posture so you extend rather than conflict:1718- `package.json` / lockfile / requirements → which LLM SDK is present (OpenAI, Anthropic, Vercel19 AI SDK, LangChain, LlamaIndex)? Which is the default model tier?20- `grep` for the API key — is it read through a validated server-side env schema, or does it leak21 into a `NEXT_PUBLIC_*` / client-bundled / mobile-app constant?22- Find every call site that sends a prompt — what user-controlled or retrieved content reaches23 the system prompt or context window, and is there any structural separation between24 instructions and untrusted content?25- Check whether the integration uses tool-use / function-calling — what can the model actually26 invoke, and is there a confirmation step before a consequential action fires?27- Look for logging/observability around LLM calls — are raw prompts and completions logged, and28 where do those logs live?29- Check for rate limits, `max_tokens` caps, and timeouts on LLM-calling routes; check for retry30 logic around tool actions.31- Look for any eval harness, golden test set, or regression check tied to prompt changes.3233State what you found, then propose the smallest set of changes that closes the gaps.3435## The AI layers (work in this order)36371. **Prompt-injection surface** — anything that reaches the model's context that isn't the38 developer's own instructions (user input, retrieved documents, scraped pages, tool output) is39 a potential injection vector. Structural separation between instructions and untrusted content,40 allow-listed tools, and human confirmation for consequential actions are the mitigations — none41 of them fully close the gap. See `references/prompt-injection.md`.422. **Keys & spend** — provider keys never ship to a client bundle or mobile app; every LLM-calling43 route has a rate limit, a `max_tokens` cap, and a bound on loop/agent iterations; streaming44 requests abort when the client disconnects. See `references/keys-and-spend.md`.453. **Data privacy** — inventory what PII leaves the building in a prompt to a third-party API,46 check the provider's retention/training posture, and scrub prompts/completions before they hit47 observability logs. See `references/data-privacy.md`.484. **Reliability & evals** — provider outages are routine, so every call has a timeout and a49 fallback; non-idempotent tool actions are never blindly retried; a minimal golden-case eval50 harness catches prompt regressions before they ship. See `references/reliability-evals.md`.5152## Standing opinions (the non-negotiables)5354These are the judgments that make output consistent across repos — apply them unless the user55overrides:5657- **Untrusted content is never structurally indistinguishable from developer instructions.**58 User input, retrieved documents, and tool output are content, not commands — the integration is59 designed so an instruction embedded in that content can't silently expand what the model is60 willing to do.61- **Provider keys live server-side only.** No LLM API key in a client bundle, mobile binary, or62 anything a browser or device can read; every model call is proxied through your own backend.63- **Every LLM-calling endpoint has a rate limit, a token cap, and a timeout.** An unbounded loop,64 an unbounded `max_tokens`, or a hung request against a stalled provider is a spend bomb and an65 availability bug waiting to happen.66- **Tool-calling that performs a consequential action gets a confirmation step or an allow-list.**67 A model that can call a tool is a model that can take real-world action on injected instructions68 — treat that boundary with the same suspicion as remote code execution.69- **PII in a prompt to a third-party API is inventoried, not assumed away.** If personal data70 reaches a model provider, know what's sent, what the provider's retention/training terms say,71 and whether logging pipelines are scrubbing it before storage.72- **Prompts that ship non-trivial agent/prompt behavior are versioned and covered by a golden-case73 eval.** A thin single-route MVP wrapper can flag a missing harness as 🟡 recommend, not automatic74 🔴 — see `reliability-evals.md` applicability.7576## Workflow77781. **Discover** — map the current posture (provider/SDK, key handling, injection surface, tool79 access, logging, rate limits, evals) and report the gaps.802. **Propose** — ordered by the four layers above, highest-risk gap first, smallest viable changes.813. **Implement** — against the repo's existing patterns (its SDK, its middleware chain, its env82 schema, its observability pipeline).834. **Verify** — confirm keys aren't reachable from the client, rate limits/token caps actually84 fire, an injected instruction in test content doesn't expand tool access, and the eval suite85 passes before a prompt change ships.8687## Scope boundaries8889This skill owns the LLM-specific layer: what reaches the prompt, what the model can do, what leaves90the building, and whether it's reliable. Hand off at these lines:9192- **Generic route authentication** → `craft-backend`. This skill does not re-audit the auth boundary.93- **Secret *storage* mechanics** (where keys live, how they rotate) → `craft-security`. This skill94 covers key *exposure through the model path* — client-reachable keys, keys in prompts or logs.95- **Provider billing alerts as infra config** → `craft-infra`. This skill owns application-level96 spend-safety *design*: rate limits, token caps, and loop bounds on model-calling routes.97- **Rate-limit ownership, so the same gap isn't emitted four times:** BE supplies the route98 middleware mechanism; SEC owns login-abuse policy; INFRA owns edge capacity; AI owns LLM99 cost/token limits.100- **Whole-project readiness** → `craft-audit`, which routes here for the AI slice.101102## Reference index103104Read the one matching the current task — they hold the concrete setup, not this overview:105106- `references/prompt-injection.md` — injection via user content and retrieved/scraped content107 (indirect injection), tool-use/function-calling boundaries, output handling (XSS, downstream108 injection), mitigation framing109- `references/keys-and-spend.md` — client-bundle key leaks, per-route rate limits and token caps,110 unbounded loops as spend bombs, streaming abort on disconnect, billing alerts, model tiering111- `references/data-privacy.md` — PII inventory in prompts, provider retention/training flags,112 scrubbing logged prompts/completions, user consent/disclosure, regional/enterprise endpoints113- `references/reliability-evals.md` — timeouts and fallbacks, retry vs. non-idempotent tool114 actions, minimal eval harness, structured-output validation, prompt versioning115116## Audit checklist (for craft-audit)117118When `craft-audit` plans an AI pass for a scope, it turns this checklist into the `plan.md`119todo list — the checklist is owned by this skill, not improvised by the orchestrator. Tailor to120what discovery found: skip a step that genuinely doesn't apply with a one-line reason; never121silently drop one. Emit findings using craft-audit `workspace.md` → "Canonical findings.md emission format"122(authority). Heading grammar (variables required — do not hardcode NNN/severity/status):123124`## <scopeLabel>-AI-<NNN> · severity <🔴|🟡|🟢> · status <open|fixed|wontfix (reason)|regressed|fixed (merged into <ID>)>`125126Example only: `## <scopeLabel>-AI-001 · severity 🔴 · status open`127128Required fields under each heading, in order, with these exact labels:129`**What breaks (plain language):**` · `**Technical:**` · `**Fix:**` · `**Fingerprint:**` ·130`**Last-checked:**` (optional `**Confidence:**` — `verified | inferred | unverified-from-repo`, absent131means `verified` — then optional `**Fix-attempt:**` only from craft-fix).132Assign sequential NNN per (scope, domain); judge severity with craft-audit `prioritization.md`.133Forbidden: `###` headings; `## ID · 🔴 · open` shorthand; severity/status as body bullets.134135- [ ] Map the current posture — provider/SDK, key handling, injection surface, tool access,136 logging, rate limits, evals — flagging client-exposed keys and unbounded loops →137 SKILL.md "Operating principle — discover before you build"138- [ ] Verify user-controlled and retrieved/scraped content is structurally separated from139 developer instructions in the prompt; hunt for indirect-injection vectors (RAG documents,140 web-scraped context, uploaded files) → `references/prompt-injection.md`141- [ ] Check tool-use/function-calling boundaries — is every callable tool allow-listed, and does142 a consequential action require confirmation rather than firing on an injected instruction?143 → `references/prompt-injection.md`144- [ ] Confirm LLM output rendered as HTML/markdown is encoded, and output used to build a query145 or command is treated as untrusted → `references/prompt-injection.md`146- [ ] Confirm no provider API key is reachable from a client bundle, mobile app, or public env var;147 verify calls are proxied through the backend → `references/keys-and-spend.md`148- [ ] Verify every LLM-calling route has a rate limit, a `max_tokens` cap, and a bound on149 agent/loop iterations; check streaming requests abort on client disconnect. **Ownership:** AI150 owns LLM spend/cost limits; BE may still own the middleware mechanism — emit once, don't151 triple-count with SEC/INFRA → `references/keys-and-spend.md`152- [ ] Check for provider billing alerts/hard caps and model tiering (cheap model for cheap tasks)153 → `references/keys-and-spend.md` (cross-ref `craft-infra` for the alerting config itself)154- [ ] Inventory PII flowing into prompts to third-party APIs; check provider retention/training155 settings and note zero-retention options where offered → `references/data-privacy.md`156- [ ] Confirm prompts/completions logged for observability are scrubbed of PII, and users are157 disclosed that AI processes their data → `references/data-privacy.md`158- [ ] For regulated or policy-constrained generation (medical, financial, legal, real-estate,159 insurance), identify where the authoritative restrictions live and confirm they are enforced160 either by a **deterministic, fail-closed control** (constrained templates, output validation161 against the restriction list) **or by an enforced human-review publication gate** — the review162 gate blocks publication reliably even though human judgment itself is not deterministic.163 Restrictions injected as prompt text or per-tenant context fields are an input the model can164 disregard, **not** enforcement; a doc the model never sees is neither165 → `references/data-privacy.md`166- [ ] For factual or citation-bearing output published on a user's behalf, check there is an approved167 evidence source and a claim-attribution, source-validation, or human-review path rather than168 free-generated assertions. Require tenant-level prohibited-claim rules only where the product's169 own policy or regulatory scope calls for them → `references/reliability-evals.md`170- [ ] Check the model is selected and evaluated against the task's **factuality and citation171 requirements**, not cost alone, and that observed fabricated citations are treated as172 disqualifying regardless of tier. Model choice alone does not make citation-bearing output safe173 — keep the evaluation and its mitigation rationale with the call site or its config174 → `references/reliability-evals.md`175- [ ] Verify LLM calls have timeouts and a graceful-degradation fallback for provider outages, and176 that retries never re-fire a non-idempotent tool action → `references/reliability-evals.md`177- [ ] Check for schema validation (repair-or-reject) on structured LLM output before it's consumed178 downstream → `references/reliability-evals.md`179- [ ] Confirm eval discipline matches surface risk: golden-case harness is **required** (🔴-class180 when missing) when shipping non-trivial prompt/agent behavior to production; for a thin181 single-route MVP wrapper, flag missing eval as 🟡 opportunity / recommend, not automatic 🔴.182 Prompts that do ship should still be versioned like code → `references/reliability-evals.md`183
Run npx skillmds@latest add gul-labs/craft-ai in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
The Craftsman standard for LLM-powered features — prompt-injection surface, key and spend protection, PII reaching model APIs, and reliability/eval discipline. Use whenever work touches an LLM integration: a chatbot or agent, a RAG pipeline, an LLM call from a route or job, tool/function-calling, or reviewing prompts and completions. Trigger even on "add an AI feature", "is my chatbot secure", "my OpenAI bill exploded", or "why did the model call the wrong tool" without naming a provider or framework. Handoffs: see "Scope boundaries" in the body. It is listed under DevOps & Infra on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
gul-labs (@gul-labs) published this skill. Their other Agent Skills are listed on their SkillMD profile.