From workflow-skills
Use when building analyses where numbers feed into decisions, reports, or documents — financial projections, cost comparisons, capacity planning, scenario analysis — with more than a couple of variable inputs.
How this skill is triggered — by the user, by Claude, or both
Slash command
/workflow-skills:analysis-pipelineThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Note**: Project-level preferences (CLAUDE.md, AGENTS.md) override these.
Note: Project-level preferences (CLAUDE.md, AGENTS.md) override these.
Structured analysis where numbers feed into decisions, reports, or ongoing models. Examples: financial projections, cost comparisons, capacity planning, scenario analysis.
Does NOT apply to quick calculations or exploratory conversation. If the user asks "what's 20% of $500k?" just answer.
Rule of thumb: if the analysis has more than ~3 variable inputs or produces a document, use these conventions.
example/ contains the reference implementation. Copy its structure when building new pipelines. Specifically:
fill_templates.py and adapt it (the placeholder resolution logic is reusable as-is)model.py's pattern: inputs with source comments at top, derived values in the middle, structured JSON output at the bottommemo.template.md's pattern: executive summary first, data tables with {{dotted.key}} placeholders, {{narrative:*}} for prose sectionsmodel_output.json's structure: top-level inputs and derived sections, pre-formatted display valuesDo NOT search the repo for other implementations to use as reference. This example and the spec below are the complete specification. If the example doesn't cover a case, ask.
The core principle: every number in a final document must trace to either an input or a computation in the model — never to a one-time Claude response. Re-running the pipeline should regenerate everything from current inputs.
For simple analyses, this can be as lightweight as a single script that prints results. For larger projects with narrative reports, a multi-stage pipeline works well:
model_report.json).template.md) contain {{key}} placeholders for dataScale the pipeline to the complexity of the analysis. A one-page cost comparison doesn't need four stages.
Every pipeline lives in a single directory. Use this structure:
analysis-name/
model.py # computation — inputs, derived values, JSON output
model_output.json # committed output of model.py
memo.template.md # document with {{key}} and {{narrative:*}} placeholders
fill_templates.py # deterministic placeholder replacement (copy from example/)
memo.filled.md # committed output of fill_templates.py
inputs/ # (optional) source data files the model reads
Naming: use model.py and model_output.json unless the pipeline has multiple models, in which case name them descriptively (e.g., cost_model.py, cost_model_output.json).
.py script (default for pipelines): deterministic pipeline steps, one-shot computations, anything that writes JSON output for the template fill step.When building a pipeline that produces a document, always use a plain script for the model step.
The model's JSON output should follow this structure:
{
"inputs": {
"section_name": {
"field": "value",
"source": "where this came from (URL, date checked)"
}
},
"derived": {
"section_name": {
"field": "$1,234.56"
}
}
}
derived.vendor_a.monthly_cost maps to {{derived.vendor_a.monthly_cost}}).recommendation, sensitivity) — the fill script handles arbitrary nesting.Every value in an analysis is either an input or derived. Keep this distinction explicit.
If an input is an assumption rather than a measured/sourced fact, say so — e.g., "assumed 5% annual rate increase (no source, revisit)."
External parameters MUST include a source link or note (vendor URL, spec sheet, date checked) in code comments. Inputs without sources should be flagged as assumptions.
MUST NOT derive numbers in markdown files. Markdown is for narrative, decisions, qualitative analysis, system descriptions, assumptions, and open questions.
MUST keep all derived numbers (calculations, savings, sensitivity) in a notebook or script. Markdown may show summary tables with max/realistic ranges but MUST reference the model for derivation.
When an analysis produces a narrative document (memo, recommendation, report):
Bad (numbers derived in markdown):
The system produces 817 kWh/month, saving $142/month.
Good (reference to model):
See [model.py] for production estimates. Summary: 750-820 kWh/month, $128-$145/month savings (sensitivity table in model).
Bad (structure buries the conclusion):
We analyzed three vendors across 12 dimensions... [3 pages] ...Vendor B is recommended.
Good (lead with the decision):
Recommendation: Vendor B. Best cost/reliability tradeoff at $X/mo. See comparison model for full breakdown across 12 dimensions.
When an analysis produces a narrative document, use templates to keep numbers tied to the model.
.template.md files contain the document structure with two kinds of placeholders:
{{key}} — data placeholders, filled deterministically from the model's JSON output. These are reproducible: re-run the model, re-fill, get the same numbers.{{narrative:section_name}} — prose placeholders, filled by piping the enriched template through Claude CLI. These are regenerable but not deterministic — the data they reference is pinned, but the wording may vary.Fill step: a script (e.g., fill_templates.py, a jq one-liner, whatever fits) reads model_report.json and replaces {{key}} placeholders. This step has no LLM involvement and should be trivially verifiable.
Narrate step (optional): passes the data-filled template to Claude CLI to expand {{narrative:*}} blocks into prose. The LLM sees the real numbers in context, so the narrative stays consistent with the model.
This separation matters because data fill is deterministic (same inputs = same output) while narrative fill is not. Keeping them as distinct steps means you can re-run just the narrative without re-running the model, or update the model and verify the numbers changed before regenerating prose.
A model that computes correct numbers from inputs can still describe an infeasible system. Examples: a financial projection assuming two contradictory tax treatments, a capacity plan where ingest rate exceeds storage throughput, a staffing model that assumes 10 hours/day per person.
When the model depends on components working together, make compatibility visible in the model (assertions, checks, comments) — not just in narrative. If an incompatibility exists, the model should surface it rather than silently producing numbers as if everything works.
Inputs, specs, rates, costs, and parameters belong in the model as named variables (or in JSON files the model reads) — not as prose assertions in markdown. This applies to:
This keeps data auditable, updatable in one place, and prevents drift between model and narrative.
# Step 1: compute the model
uv run model.py
# Step 2: fill data placeholders into the template
uv run fill_templates.py
# Step 3 (optional): fill narrative sections via Claude CLI
cat memo.filled.md | claude -p "Fill in the {{narrative:*}} sections. Return the full document." > memo.final.md
Always use uv run, never bare python. Commit model_output.json and memo.filled.md so the outputs are auditable without re-running.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub bestdan/workflow-skills --plugin workflow-skills