This skill turns an article URL or pasted content into a properly formatted raw/ file in the correct subdirectory. It covers one task only: getting source material into raw/. Wiki page generation from raw/ is a separate step — see docs/WIKI-SKILL.md.
Tools required: Cursor or Claude Code with file-write access to the repo. Scripts used: tools/check_duplicate.py, tools/derive_slug.py, tools/check_series.py — see tools/README.md for interface reference.
These apply before anything else. No exceptions.
- Never translate the article body. The raw/ file preserves the original author's exact words. If the article is in Russian, the file body is in Russian. If in English, English. Translation is out of scope for this skill.
- Never fabricate the
url:field. Use only the canonical URL provided by the contributor. If no URL is available, leaveurl:empty and warn. - Never modify existing
raw/files. If a duplicate is found, stop and report — do not overwrite or merge. - Never summarize or paraphrase the article body. Clean it, preserve it.
- Never ingest the same URL twice. Run
check_duplicate.pybefore writing. - Never add wiki-layer content here.
raw/files are source material only — no[[wikilinks]], no## Related pages, no Quartz-specific formatting. - Never fetch or reconstruct article body content independently. The body must come from the contributor's paste only. If no paste has been provided, ask for it — do not proceed.
The contributor provides:
| Field | Required | Notes |
|---|---|---|
| Content (pasted article text, preferably markdown) | Yes | Paste the full article text from the title to the last line, including tables, footnotes, and closing notes. Markdown format preferred (e.g. copied from reader mode or a markdown-rendered view). |
| URL | Yes | Always required, even when pasting content — cannot be inferred from pasted text reliably. |
| Author | No | Agent infers from article if not provided. If the contributor provided an author name in their prompt, use it as-is — do not override with what the agent infers from the article. |
| Series context | No | Agent detects automatically via check_series.py. If the contributor specified series name and part number, use it as-is — do not override. |
| Published | No | Publication date as YYYY-MM-DD. If the contributor supplied it, use it as-is. If not provided and not visible in the article, omit the date: field entirely — do not guess. |
If the contributor has not pasted content, ask for it before proceeding. Do not attempt to fetch or reconstruct the article body independently.
Before writing anything, read the pasted content and produce a structural inventory:
Pre-heading content: [YES/NO] — if YES, first sentence: "..."
Headings (in order):
## Heading one
### Sub-heading
## Heading two
...
Post-heading content: [YES/NO] — if YES, first sentence: "..."
Tables: [count] — list first-row content of each
Blockquotes: [count] — list first 8 words of each
Approximate word count: [N]
This inventory is the ground truth. Every item must appear in the written raw/ file. Any item missing from the file is a hard stop — do not proceed to wiki ingest.
python3 tools/check_duplicate.py --url "<URL>"CLEAR→ proceedDUPLICATE: raw/path/to/file.md→ stop, report the existing file to the contributor, do not proceedSLUG_CONFLICT→ proceed but note the conflict; the filename will need manual adjustment in Step 5
Read the article content and determine the primary subdirectory using this lookup table. When content spans two categories, pick the primary one and record the secondary in tags.
| Content type | Subdirectory |
|---|---|
| Bitcoin monetary theory, economics, Austrian school, inflation, deflation, monetary history | raw/Theory/economics/ |
| Protocol mechanics: UTXO, opcodes, addresses, upgrades, SegWit, Taproot, mining, difficulty | raw/Theory/protocol/ |
| Philosophy, culture, manifestos, cypherpunk thought, sovereignty | raw/Theory/philosophy/ |
| History: events, legal cases, exchange histories, regulatory milestones | raw/Theory/history/ |
| Biographical / entity-focused articles (people, organizations) | raw/Theory/entities/ |
| Privacy tools, techniques, chain analysis, CoinJoin, KYC | raw/Theory/privacy/ |
| Security: key management, custody, hardware wallets, seed phrases | raw/Theory/security/ |
| Lightning Network — theory, how it works, channel mechanics | raw/Theory/lightning/ |
| Long-horizon scenarios, hyperbitcoinization, future speculation | raw/Theory/future/ |
| Book chapters | raw/Books/<book-name>/ |
| Practical guides — self-custody, cold storage, hardware setup | raw/Practice/hodl/ |
| Practical guides — Lightning wallets, nodes, liquidity | raw/Practice/lightning/ |
| Practical guides — privacy tools in practice, Tor, CoinJoin workflows | raw/Practice/privacy/ |
| Practical guides — buying Bitcoin, P2P exchanges | raw/Practice/buy/ |
| Practical guides — operational security, passphrase, multisig setup | raw/Practice/security/ |
| Practical guides — interacting with Bitcoin ecosystem, explorers, nodes | raw/Practice/interact/ |
| Introductory content, onboarding guides, glossary source material | raw/Start/ |
Ambiguous cases: If genuinely unclear between two categories, prefer Theory/ over Practice/ for conceptual articles, and protocol/ over economics/ for articles that explain a mechanism (even if they discuss economic implications).
python3 tools/derive_slug.py "<URL>"Use the output as the filename base. Append .md.
Exceptions — use a manual filename when:
- The article is a book chapter → use
chapter-N-book-name.mdwith a shortened book name if applicable - The script returns an empty slug (non-ASCII path segment) → derive from the article title manually: lowercase, hyphenated, ASCII only
- A
SLUG_CONFLICTwas reported in Step 2 → append-2or a disambiguating word
Run the duplicate check again with the slug and subdir to confirm no path conflict before writing:
python3 tools/check_duplicate.py --url "<URL>" --slug "<slug>" --subdir "<Theory/protocol>"Strip all of the following from the pasted or fetched content:
- Site header, navigation bar, breadcrumbs
- Footer (links, copyright, social icons)
- "Related articles" / "You might also like" blocks
- Subscription prompts, newsletter signup forms
- Cookie consent banners
- Comment sections
- Social sharing buttons
- Any repeated site chrome that is not part of the article itself
Preserve exactly:
- Article title (keep as
# Titleat the top of the body — raw/ files are not Quartz pages, so a#heading is fine here) - All body sections, headings, and subheadings
- Blockquotes, code blocks, tables, footnotes
- Images references (keep alt text, drop src if it's a CDN path that won't resolve; note
[image: description]instead) - The author's original formatting and paragraph structure
- All original hyperlinks in the body (these are source material)
VERBATIM RULE: The body of the raw/ file must be a character-level faithful copy of the pasted content after chrome removal. Do not rewrite sentences. Do not compress or merge paragraphs. Do not omit sections that seem redundant or off-topic — curation happens at the wiki layer, not during raw/ ingest. The only permitted changes are: stripping site chrome (as listed above), converting HTML entities to Unicode, and normalizing heading levels if inconsistent.
Normalization:
- Convert HTML entities to proper Unicode characters (
&→&, etc.) - Normalize heading levels if the article uses inconsistent jumps (e.g.
##directly to####) — but do not change the author's intended hierarchy - Preserve original language throughout — do not translate any word, phrase, heading, or caption
---
title: "<Article title — exact, from the article's h1>"
author: "<Original author name>"
url: "<Canonical URL as provided by contributor — verbatim>"
source_domain: "<domain only, e.g. 21ideas.org>"
category: "<e.g. theory/protocol>"
language: "<en, ru, fr, etc. — the article's original language>"
tags: [descriptive, free-form, tags]
date: "<YYYY-MM-DD publication date if known, else omit>"
---Field notes:
title— copy from the article's<h1>or page title exactly. Do not rewrite or translate.author— original author of the content, not a translator or republisher. For 21ideas articles that are adaptations, use the original author's name if credited; otherwise use"21ideas".url— use the URL exactly as the contributor provided it. Do not reconstruct, normalize, or guess. If the contributor did not provide a URL and fetch failed, seturl: ""and add a comment line# url unknown — verify before wiki ingest.source_domain— the bare domain, no protocol or path. Used for provenance classification downstream.category— the subdirectory path relative toraw/, lowercase, forward slashes. E.g.theory/protocol,practice/hodl,books/fiat-standard.language— the language the article is written in, not the language the wiki page will be written in.enorrufor the vast majority of cases.tags— free-form descriptive tags for this raw file. These are NOT the wiki allowlist tags. Use whatever is useful for search and classification. Examples:[taproot, schnorr, protocol-upgrade, bip340]date— publication date if visible on the article. Omit the field entirely if unknown rather than guessing.seriesandpart— add only if Step 7 confirms a series. See below.
Write the file first (Step 8), then run:
python3 tools/check_series.py raw/<subdir>/<filename>.mdNO_SERIES→ nothing to doSERIES_DETECTED→ include the full output block in your verification report to the contributor (see Step 9). Do not block or branch — just report. If confirmed as a series, addseries:andpart:to the frontmatter as a follow-up edit.
Write the cleaned content with the frontmatter to the target path:
raw/<Subdirectory>/<slug>.md
The file structure is:
---
<frontmatter>
---
# Article Title
<cleaned article body — verbatim, original language>
Do not add anything after the body. No wiki navigation, no wikilinks, no ## Sources section — those belong in wiki pages, not raw/ files.
Output this block after writing the file. Do not skip it.
✓ Created: raw/<subdir>/<slug>.md
Title: <article title>
Author: <author>
URL: <url>
Language: <en|ru>
Category: <category>
Subdir: raw/<subdir>/
Slug: <slug>
Duplicate check: CLEAR
Slug conflict: <CLEAR or note>
Section & content inventory:
[PRE-HEADING] in source: YES/NO → in file: [x]/[ ]
[## Heading] blocks in source: N → in file: N [x]/[ ]
... (one row per heading)
[POST-HEADING] in source: YES/NO → in file: [x]/[ ]
Tables: source N / file N [x]/[ ]
Blockquotes: source N / file N [x]/[ ]
Any [ ] item is a blocking issue — rewrite the file before proceeding.
<If SERIES_DETECTED — paste full check_series.py output here>
Next step:
To generate wiki pages from this file, run the wiki ingest workflow:
→ See CLAUDE.md "Ingest" section, or docs/WIKI-SKILL.md
→ File path to use: raw/<subdir>/<slug>.md
If url: is empty or unverified, add a warning line:
⚠ url: field is empty — verify the canonical URL before running wiki ingest.
Without it, wiki pages will have sources: [] and no reader-facing citation.
These come up often enough to be worth stating explicitly:
- "What is X?" explanatory articles →
Theory/in the relevant subcategory - "How to do X" step-by-step guides →
Practice/in the relevant subcategory - Person profiles, obituaries, interviews →
Theory/entities/ - BIP specifications →
Theory/protocol/ - Legal cases, regulatory events →
Theory/history/ - Academic papers on Bitcoin cryptography →
Theory/protocol/orTheory/security/ - "Bitcoin vs X" comparison articles → classify by the primary Bitcoin concept being explained (usually
Theory/economics/orTheory/protocol/) - Newsletter issues (Bitcoin Optech, etc.) — ingest individual topic sections as separate files, not the full newsletter
- Does not generate
wiki-en/orwiki-ru/pages — that is the wiki ingest workflow inCLAUDE.md - Does not enforce source acceptance policy — that is
CONTRIBUTING.mdand PR review - Does not translate content — ever
- Does not summarize or condense the article
- Does not update
docs/log.md— the maintainer logs raw/ additions manually or as part of the subsequent wiki ingest step