Agent skill

NLP

by ericrisco in ericrisco/rsc-harness

A skill your agent uses when choosing how to tokenize text or which transformer type fits an NLP task, when a tokenizer over-fragments non-English text or inflates token cost, when picking a…

MITAuto-check passedAI & LLM Engineering

Install NLP

skills CLI
$ npx skills add ericrisco/rsc-harness --skill nlp -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install ericrisco/rsc-harness nlp --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/nlp .claude/skills/nlp && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
nlp
GitHub stars
156
Token cost
~3.5k tokens
SKILL.md length
1,486 words
Files
5 (incl. references)
Skills in repo
229
Repo updated
First seen
Licence
MIT

At a glance

A skill your agent uses when choosing how to tokenize text or which transformer type fits an NLP task, when a tokenizer over-fragments non-English text or inflates token cost, when picking a…

  • Works in 4 steps: Tokenization → Tasks → Evaluation — pick the metric that… → …
  • Choosing how to tokenize text
  • SKILL.md covers Route out first (loud — do not…, Decision: model type per task…, 1. Tokenization and 2. Tasks, plus 6 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

NLP is an agent skill from ericrisco/rsc-harness. Use when choosing how to tokenize text or which transformer type fits an NLP task, when a tokenizer over-fragments non-English text or inflates token cost, when picking a language metric, or when classification, NER or summarization output looks wrong and it is unclear whether the tokenizer, the architecture or the metric is at fault. Covers subword tokenizers, encoder versus decoder versus encoder-decoder choice, sentence embeddings, and the metric families. NOT retrieval or vector search (that is…

Its SKILL.md is about 3.5k tokens, which your agent loads only when the skill is triggered. The skill folder holds 6 other files, including reference files (for example `evals/README.md`, `evals/cases.yaml` and `references/evaluation.md`).

It sits in AI & LLM Engineering, covering Natural language processing, Embeddings and Summarization. The repository describes itself as: Your agent invents things because it has no memory, and can't touch your database because it has no arms. rsc is the meta-harness that gives it both, plus the trade to know the… The licence is MIT.

When your agent uses it

  • Choosing how to tokenize text
  • Which transformer type fits an NLP task
  • A tokenizer over-fragments non-English text
  • Inflates token cost

Example prompts

  • “/nlp”

Requirements

  • Python 3

Workflow steps

4 steps, taken from the step headings in SKILL.md.

  1. Tokenization
  2. Tasks
  3. Evaluation — pick the metric that matches the task
  4. Multilingual pitfalls

What it can do on your machine

Read from SKILL.md and the folder at commit 92fde8f. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are python).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

NLP loads about 3.5k tokens when it runs, and up to ~6k if it reads all its reference files. Until then it costs about 166 tokens; SKILL.md has 1,486 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~166
When it runs · the whole SKILL.md, loaded when a task matches
~3.5k
With references · SKILL.md plus every file in references/, read only if the agent opens them
~6k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from ericrisco/rsc-harness at commit 92fde8f, republished under its MIT licence (© ericrisco). 1,486 words, ~3,541 tokens.

Download SKILL.mdSave it as .claude/skills/nlp/SKILL.md (or your agent's skills folder). This skill also uses 4 other files; get the full folder from GitHub.
name
nlp
description
Use when choosing how to tokenize text or which transformer type fits an NLP task, when a tokenizer over-fragments non-English text or inflates token cost, when picking a language metric, or when classification, NER or summarization output looks wrong and it is unclear whether the tokenizer, the architecture or the metric is at fault. Covers subword tokenizers, encoder versus decoder versus encoder-decoder choice, sentence embeddings, and the metric families. NOT retrieval or vector search (that is `embeddings-search`), NOT the RAG loop (that is `rag`), NOT prompt wording (that is `prompt-engineering`), NOT training the network (that is `finetuning`).
tags
nlp, tokenization, bpe, wordpiece, ner, text-classification, bleu, rouge, perplexity, transformers
recommends
deep-learning, embeddings-search, rag, training-data
origin
risco

nlp — tokenize the text, pick the model type, pick the metric

You own the language-modeling discipline: how raw text becomes tokens, which transformer architecture fits a task, and which metric actually tells you whether it worked. When the question is "which tokenizer," "BERT or GPT or T5 for this," "why does my Catalan text cost 3× the tokens," or "is this BLEU score meaningful," this is the skill. You stop at retrieval, the RAG loop, prompt wording, and the training step itself — those route out (below).

Route out first (loud — do not duplicate these)

Decision: model type per task (get this right before anything else)

Pick the architecture from the task's shape, not from what is trendy. A decoder LLM can technically classify, but a fine-tuned encoder is smaller, faster, cheaper, and usually more accurate on a fixed-label task.

Task shapeArchitectureWhyExample families*
Understand / label a whole input (classification, NER, extractive QA, similarity)Encoder (bidirectional)Attends to the full sentence both directions; cheap to fine-tune and to serveBERT, RoBERTa, DistilBERT, ModernBERT
Free-form generation, chat, few-shotDecoder (autoregressive)Attends only to prior tokens; predicts the next tokenGPT-style, Llama, Gemma, Qwen
Transform input → new text (summarize, translate, generative QA)Encoder-decoder / seq2seqEncoder reads all of the source, decoder writes conditioned on itT5 / FLAN-T5, BART, mT5

* Architecture families are stable; specific checkpoints and their licenses are not — check the HF model card before you commit (licenses change; see open-weights). ModernBERT (2024) is a current long-context encoder; verify the latest at author time.

The two most common own-goals: reaching for a 7B decoder to do sentiment on 5 classes (an encoder does it for a fraction of the cost), and forcing an encoder to generate (it cannot — it has no decoder).

1. Tokenization

Every downstream number depends on this step, and its failures are silent. The single load-bearing rule:

Load the tokenizer that shipped with the checkpoint, and use the same one at train and inference. AutoTokenizer.from_pretrained(same_checkpoint). A train/inference tokenizer mismatch — different vocab, different special tokens, different casing/normalization — maps text to token ids the model never saw and corrupts everything downstream with no error.

python
from transformers import AutoTokenizer   # transformers current major ~v5 (verify at author time)

tok = AutoTokenizer.from_pretrained("bert-base-cased")
enc = tok("Tokenizers matter.", return_offsets_mapping=True)
tok.convert_ids_to_tokens(enc["input_ids"])
# ['[CLS]', 'Token', '##izers', 'matter', '.', '[SEP]']  — note WordPiece '##' continuation + added specials

The four algorithms (full mechanics in references/tokenization.md):

AlgorithmBuilds vocab by…Applies by…Used by
BPEmerging the most frequent adjacent pair, repeatedlysplit to chars, replay learned mergesGPT-2 (byte-level), many
WordPiecemerging pairs that maximize a likelihood scorelongest-match subword from the front (## continuations)BERT family
Unigram (SentencePiece)start large, remove tokens that least hurt corpus likelihoodmost-probable segmentationT5, ALBERT, mT5
Byte-level BPEBPE over the 256 raw bytes, not Unicode charssame as BPE on bytesGPT-2, RoBERTa
  • Byte-level BPE has no [UNK]. Base vocab is exactly 256 (all byte values), so every emoji, accent, and script maps to some byte sequence — nothing falls out as unknown (verified: HF NLP course ch.6). WordPiece/word-level tokenizers do have [UNK] and lose OOV content.
  • SentencePiece is reversible — it treats space as a normal symbol (the ▁ meta-symbol), so decode(encode(x)) == x without language-specific detokenization rules. That is why it dominates multilingual models.

Special tokens are not decoration. [CLS]/<s> carries the pooled sentence representation for classification; [SEP]/</s> marks segment/end; [PAD] fills a batch (and must be masked out via attention_mask); [MASK] is the MLM target; [UNK] is the fallback. Names differ by model ([CLS] in BERT vs <s> in RoBERTa) — another reason to never hand-roll the tokenizer.

Why it matters — three concrete costs:

  • $ cost & context. Token count is the bill and the context budget. Fewer tokens per sentence = cheaper calls and more room in the window.
  • OOV / information loss. A tokenizer that emits [UNK] throws away content it can't represent; byte-level/SentencePiece degrade gracefully instead.
  • Fairness. Vocab trained mostly on English fragments other scripts far harder — the same meaning costs more tokens, more money, and more latency (section 5).

2. Tasks

Text classification (encoder + classification head)
python
from transformers import pipeline
clf = pipeline("text-classification", model="distilbert-base-uncased-finetuned-sst-2-english")
clf("The service was slow but the food was incredible.")
# [{'label': 'POSITIVE', 'score': 0.99...}]

Metric: accuracy on balanced data; macro-F1 the moment classes are imbalanced (accuracy lies when 95% of rows are one class).

Token classification / NER (encoder, per-token labels)

Labels are B-/I-/O spans aligned to subword tokens: the first subword of a word gets the label, continuation subwords and special tokens get -100 (ignored by the loss). Evaluate with seqeval at the entity level, never per-token accuracy (per-token accuracy is inflated by the flood of O tokens).

python
from transformers import pipeline
ner = pipeline("token-classification", aggregation_strategy="simple")
ner("Ada Lovelace worked in London.")
# groups subwords back into entities: PER 'Ada Lovelace', LOC 'London'
Seq2seq — summarization / translation (encoder-decoder)
python
summ = pipeline("summarization", model="facebook/bart-large-cnn")
summ(long_article, max_length=130, min_length=30)

Metric: ROUGE for summarization, BLEU/chrF for translation — with the heavy caveat in section 4.

Sentence embeddings (SBERT — the task, not retrieval)
python
from sentence_transformers import SentenceTransformer   # sentence-transformers ~v5 (verify)
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
emb = model.encode(["The weather is lovely today.", "It's so sunny outside!"])
model.similarity(emb, emb)   # semantic textual similarity / clustering / paraphrase mining

Producing/judging embeddings for retrieval (model choice, chunking, recall@k, rerankers) is embeddings-search, not here.

Show full SKILL.md (679 more words)Show less

3. Evaluation — pick the metric that matches the task

TaskPrimary metricCatchesTrap
Classificationaccuracy + macro-F1wrong labelsaccuracy hides minority-class failure
NER / tokenentity-level F1 (seqeval)missed/partial spansper-token accuracy is inflated by O
TranslationBLEU / chrFn-gram overlap w/ referenceweak on meaning; chrF better for morphology
SummarizationROUGE (1/2/L)recall of reference n-gramsrewards copying; blind to faithfulness
Generation (LM)perplexityhow well the model predicts held-out texttokenizer-dependent — not comparable across tokenizers
Open-ended / chatLLM-as-judge + humanquality overlap metrics missjudge bias (position, verbosity, self-preference)

The caveat that governs this whole section: BLEU, ROUGE, and chrF are n-gram/character overlap metrics and correlate weakly with human judgment on open-ended and creative generation — they reward matching the reference's exact phrasing, so a correct paraphrase scores low and a fluent-but-wrong copy scores high (well documented; e.g. the summarization and MT-evaluation literature). Use them for regression tracking on a fixed reference set, never as the final verdict on quality. For open-ended output, use an LLM-as-judge rubric plus a human spot-check — and know the judge has its own biases (position, verbosity, self-preference), so pin the rubric and randomize order.

Perplexity = exp(mean token NLL): lower means the model predicts held-out text better. It is tokenizer-dependent, so two models with different tokenizers have non-comparable perplexities — only compare within the same tokenizer/vocab. Runnable snippets for seqeval, sacrebleu, ROUGE, perplexity, and an LLM-judge harness are in references/evaluation.md.

4. Multilingual pitfalls

The English-centric trap: a tokenizer whose vocab was learned mostly on English over-fragments other scripts. The same sentence in Ukrainian, Arabic, Hindi, or even accented Catalan can take 2–15× more tokens than its English equivalent (Petrov et al., Language Model Tokenizers Introduce Unfairness Between Languages, NeurIPS 2023). That "fertility" (tokens per word) inflation is a triple tax:

  • Money — more tokens per identical meaning = a proportionally larger bill for the same work.
  • Context — over-fragmented text eats the window faster, so fewer few-shot examples fit and long documents truncate sooner.
  • Quality — sequences fragmented into byte-shards are harder to model, degrading accuracy for exactly the users the tool already serves worst.

Mitigations: prefer a multilingual tokenizer/model (mT5, XLM-R, a SentencePiece-based model) whose vocab actually covers your languages; measure fertility on your own corpus (tokens per word, per language) before you commit; and don't benchmark cost or latency only on English.

Guardrails / gotchas

  • Tokenizer must match the checkpoint, at train and inference. Mismatch corrupts silently, no error. The most expensive bug in this skill.
  • Encoders can't generate; don't classify with a giant decoder by default. Match architecture to task shape.
  • BLEU/ROUGE/chrF ≠ quality on open-ended text. Overlap metrics; weak human correlation. Track regressions with them; judge quality with an LLM-judge + human.
  • Entity-F1 (seqeval), not token accuracy, for NER. O tokens inflate accuracy toward 1.0.
  • macro-F1, not accuracy, on imbalanced classes.
  • Perplexity is tokenizer-relative — never compare it across different tokenizers.
  • Don't benchmark tokenization/cost only in English — fertility varies 2–15× across scripts.
  • Never assert a model's license from memory — check the current model card; license classes shift (Llama = Meta Community license, not OSI-open; Gemma = custom terms; etc.).
  • embeddings-search — retrieval embeddings, chunking, recall@k, reranking. NLP owns making/judging sentence embeddings as a task; using them to search is theirs.
  • rag — the full retrieve→generate answer loop and groundedness.
  • prompt-engineering — the wording of the prompt.
  • finetuning + deep-learning — actually training/adapting the network (this skill picks the type and metric; those move the weights).
  • training-data — building the labeled corpus you train on.

Checklist

  • Architecture picked from task shape (encoder / decoder / enc-dec), not habit.
  • Tokenizer loaded from the same checkpoint, used identically at train and inference.
  • Tokenizer choice justified vs OOV, cost, and — if multilingual — measured fertility.
  • Special tokens and attention_mask handled (padding masked, -100 on ignored labels).
  • Metric matches the task: macro-F1 (imbalanced), entity-F1/seqeval (NER), ROUGE/BLEU/chrF only as a regression signal, LLM-judge + human for open-ended.
  • Perplexity compared only within one tokenizer.
  • Retrieval / RAG / prompt / training concerns routed to the sibling skill, not re-solved here.

References

  • references/tokenization.md — BPE/WordPiece/Unigram/byte-level training mechanics, special tokens per family, offset mapping, and a fertility-measuring snippet.
  • references/evaluation.md — runnable seqeval, sacrebleu (BLEU/chrF), ROUGE, perplexity, and an LLM-as-judge rubric, with when each lies.

© ericrisco, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 4 other files (references) in skills/nlp of ericrisco/rsc-harness.

  • SKILL.md
  • evals/README.md
  • evals/cases.yaml
  • references/evaluation.md
  • references/tokenization.md

Open the folder on GitHubat commit 92fde8f

Compare with similar skills

NLP next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

NLP compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
NLP this skillericrisco/rsc-harness156—~3.5kAutomated safety check: PassMIT
Comparetaishi-i/awesome-japanese-nlp-resources1k—~4.1kAutomated safety check: NotesCC0-1.0
Sentence Transformers EmbeddingsOrchestra-Research/AI-Research-SKILLs13k3 repos~1.6kAutomated safety check: PassMIT
Researchtaishi-i/awesome-japanese-nlp-resources1k—~3.5kAutomated safety check: NotesCC0-1.0
Searchtaishi-i/awesome-japanese-nlp-resources1k—~4.3kAutomated safety check: NotesCC0-1.0
Transformersynulihao/AgentSkillOS617—~2.9kAutomated safety check: PassNone

Similar skills

  • Compare

    taishi-i/awesome-japanese-nlp-resources

    Compare several Japanese NLP libraries, models, or datasets for a keyword (a specific tool name, or a function/task like '形態素解析') across a handful of criteria chosen for that comparison, rendered as…

    1k GitHub stars~4.1k tokensUpdated yesterday
    AI & LLM EngineeringAuto-check: notes
  • Sentence Transformers Embeddings

    Orchestra-Research/AI-Research-SKILLs

    Generates text embeddings locally with the sentence-transformers library for RAG, semantic search, clustering and similarity, with model picks for general, multilingual and legal text.

    13k GitHub starsUsed in 3 repos~1.6k tokens
    AI & LLM EngineeringAuto-check passed
  • Research

    taishi-i/awesome-japanese-nlp-resources

    Analyze current trends and challenges in Japanese NLP for a topic.

    1k GitHub stars~3.5k tokensUpdated yesterday
    AI & LLM EngineeringAuto-check: notes
  • Search

    taishi-i/awesome-japanese-nlp-resources

    Search all Japanese NLP resources (libraries, models, datasets, tutorials, dictionaries, Hugging Face).

    1k GitHub stars~4.3k tokensUpdated yesterday
    AI & LLM EngineeringAuto-check: notes
  • Transformers

    ynulihao/AgentSkillOS

    Work with state-of-the-art machine learning models for NLP, computer vision, audio, and multimodal tasks using HuggingFace Transformers.

    617 GitHub stars~2.9k tokensUpdated 7 mo ago
    AI & LLM EngineeringAuto-check passed
  • Scholar Compute

    joshzyj/open-scholar-skill

    Design and execute computational social science analyses across 11 modules: text-as-data/NLP (STM, BERTopic, Wordfish, BERT, conText embedding regression, LLM annotation + DSL bias correction…

    167 GitHub stars~15k tokensUpdated 19 days ago
    AI & LLM EngineeringAuto-check passed

More from ericrisco/rsc-harness

All 229 skills in this repo
  • Ab Testing

    ericrisco/rsc-harness

    A skill your agent uses when designing or analyzing a controlled experiment — falsifiable hypothesis, sample size from an MDE, reading significance/CI/power, CUPED, or rescuing tests that won't go…

    156 GitHub stars~2.4k tokensUpdated yesterday
    Auto-check passed
  • Accessibility

    ericrisco/rsc-harness

    A skill your agent uses when making a web UI conform to WCAG 2.2 Level AA — axe-core or Lighthouse a11y violations, keyboard operability, focus management, ARIA roles/names/live regions, contrast…

    156 GitHub stars~3.4k tokensUpdated yesterday
    Auto-check passed
  • Ads

    ericrisco/rsc-harness

    A skill your agent uses when running or fixing paid acquisition on Google or Meta — campaign structure (Performance Max, Demand Gen, Search, Advantage+), platform-fit creative, budget/scaling rules…

    156 GitHub stars~2.2k tokensUpdated yesterday
    Auto-check passed
  • Agent Eval

    ericrisco/rsc-harness

    A skill your agent uses when measuring whether an LLM or agent system actually got better and gating merges on it: golden sets, fixing an inflated LLM-as-judge, scoring RAG (faithfulness, contextual…

    156 GitHub stars~3.2k tokensUpdated yesterday
    Auto-check passed
  • AI Media

    ericrisco/rsc-harness

    A skill your agent uses when a creative goal must become a finished media file: pick and order generative-media models per modality — AI voiceover, image-to-video clips, score — then glue them with…

    156 GitHub stars~3.3k tokensUpdated yesterday
    Auto-check passed
  • Analytics

    ericrisco/rsc-harness

    A skill your agent uses when instrumenting product or web analytics — GA4/PostHog SDK wiring, event taxonomy, funnels, double-counted events, consent gating, PII scrubbing.

    156 GitHub stars~2.8k tokensUpdated yesterday
    Auto-check passed

Questions about NLP

What does NLP do?

A skill your agent uses when choosing how to tokenize text or which transformer type fits an NLP task, when a tokenizer over-fragments non-English text or inflates token cost, when picking a…. NLP is an agent skill from ericrisco/rsc-harness. Use when choosing how to tokenize text or which transformer type fits an NLP task, when a tokenizer over-fragments non-English text or inflates token cost, when picking a language metric, or when classification, NER or summarization output looks wrong and it is unclear whether the tokenizer, the architecture or the metric is at fault.

When should I use NLP?

NLP fits situations like: choosing how to tokenize text; which transformer type fits an NLP task; A tokenizer over-fragments non-English text; inflates token cost.

How do I install NLP in Claude Code?

Run `npx skills add ericrisco/rsc-harness --skill nlp -a claude-code`. Or copy the skill folder (skills/nlp in ericrisco/rsc-harness) into .claude/skills/nlp in your project. Claude Code loads it when a task matches its description.

How do I install NLP in Codex?

Run `npx skills add ericrisco/rsc-harness --skill nlp -a codex`. Or copy the skill folder (skills/nlp in ericrisco/rsc-harness) into .agents/skills/nlp in your project. Codex loads it when a task matches its description.

Can I use NLP in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add ericrisco/rsc-harness --skill nlp -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/nlp, .gemini/skills/nlp, .github/skills/nlp and .opencode/skills/nlp in your project.

What does NLP need to run?

SKILL.md names no scripts, command-line tools or credentials: NLP is instructions for the agent only. Our summary lists: Python 3.

Does NLP access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is NLP safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does NLP use?

NLP is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does NLP use?

About 3.5k tokens (SKILL.md is roughly 14k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 2.5k tokens, read only when the agent opens those files.

What are the alternatives to NLP?

Skills that share tags, products or a category with NLP: Compare (taishi-i/awesome-japanese-nlp-resources, 1k stars), Sentence Transformers Embeddings (Orchestra-Research/AI-Research-SKILLs, 13k stars), Research (taishi-i/awesome-japanese-nlp-resources, 1k stars) and Search (taishi-i/awesome-japanese-nlp-resources, 1k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains NLP?

ericrisco (a GitHub user) maintains it in ericrisco/rsc-harness, which has 156 GitHub stars. The repository holds 229 skills in this directory. The repository was last updated on October 6, 2026.

Source: ericrisco/rsc-harness on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.