Agent skill

Narwhals

by anam-org in anam-org/metaxy

Effectively use Narwhals to write dataframe-agnostic code that works seamlessly across multiple Python dataframe libraries.

Apache-2.0Auto-check passedData & Analytics

Install Narwhals

skills CLI
$ npx skills add anam-org/metaxy --skill narwhals -a claude-code

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

GitHub CLI
$ gh skill install anam-org/metaxy narwhals --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/anam-org/metaxy.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/narwhals .claude/skills/narwhals && 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
narwhals
GitHub stars
124
Token cost
~3.3k tokens
SKILL.md length
1,074 words
Files
1
Skills in repo
8
Repo updated
First seen
Licence
Apache-2.0

At a glance

Effectively use Narwhals to write dataframe-agnostic code that works seamlessly across multiple Python dataframe libraries.

  • Works in 6 steps: Backend Agnostic: Write once, run on any… → Polars-Like API: Uses a subset of the… → Lazy & Eager Execution: Separate APIs… → …
  • Tasks that involve DataFrames
  • SKILL.md covers What is Narwhals?, Core Philosophy, Key Features and Basic Usage Pattern, plus 4 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Narwhals is an agent skill from anam-org/metaxy. Effectively use Narwhals to write dataframe-agnostic code that works seamlessly across multiple Python dataframe libraries. Write correct type annotations for code using Narwhals.

Its SKILL.md is about 3.3k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Data & Analytics, covering DataFrames. It works with Python and Polars. The repository describes itself as: Pluggable metadata management framework for versioned incremental multimodal data/ML pipelines. The licence is Apache-2.0.

When your agent uses it

  • Tasks that involve DataFrames

Example prompts

  • “/narwhals”

Requirements

  • Python 3

Workflow steps

6 steps, taken from the first numbered list in SKILL.md.

  1. Backend Agnostic: Write once, run on any supported dataframe library
  2. Polars-Like API: Uses a subset of the Polars API for consistency
  3. Lazy & Eager Execution: Separate APIs for both execution modes
  4. Expression Support: Full expression API for complex operations
  5. Type Safety: Perfect static typing support
  6. 100% Branch Coverage: Thoroughly tested

What it can do on your machine

Read from SKILL.md and the folder at commit 8337842. 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

    Links to these hosts (documentation or services it may open):

    • narwhals-dev.github.io

    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

Narwhals loads about 3.3k tokens when it runs. Until then it costs about 47 tokens; SKILL.md has 1,074 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~47
When it runs · the whole SKILL.md, loaded when a task matches
~3.3k

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 anam-org/metaxy at commit 8337842, republished under its Apache-2.0 licence (© anam-org). 1,074 words, ~3,328 tokens.

Download SKILL.mdSave it as .claude/skills/narwhals/SKILL.md (or your agent's skills folder).
name
narwhals
description
Effectively use Narwhals to write dataframe-agnostic code that works seamlessly across multiple Python dataframe libraries. Write correct type annotations for code using Narwhals.

Narwhals - DataFrame Agnostic API

Narwhals is a lightweight, zero-dependency compatibility layer for dataframe libraries in Python that provides a unified interface across different backends.

Docs: https://narwhals-dev.github.io/narwhals/

What is Narwhals?

Narwhals enables writing dataframe-agnostic code that works seamlessly across multiple Python dataframe libraries:

Full API Support:

  • cuDF
  • Modin
  • pandas
  • Polars
  • PyArrow

Lazy-Only Support:

  • Dask
  • DuckDB
  • Ibis
  • PySpark
  • SQLFrame

Core Philosophy

Why Narwhals?

  • Resolves subtle differences between libraries (e.g., pandas checking index vs Polars checking values)
  • Provides unified, simple, and predictable API
  • Handles backwards compatibility internally
  • Tests against nightly builds of supported libraries
  • Maintains negligible performance overhead
  • Full static typing support
  • Zero dependencies

Target Use Case: Anyone building libraries, applications, or services that consume dataframes and need complete backend independence.

Key Features

  1. Backend Agnostic: Write once, run on any supported dataframe library
  2. Polars-Like API: Uses a subset of the Polars API for consistency
  3. Lazy & Eager Execution: Separate APIs for both execution modes
  4. Expression Support: Full expression API for complex operations
  5. Type Safety: Perfect static typing support
  6. 100% Branch Coverage: Thoroughly tested

Basic Usage Pattern

Three-Step Workflow
python
import narwhals as nw

# 1. Convert to Narwhals
df_nw = nw.from_native(df)  # Works with pandas, Polars, PyArrow, etc.

# 2. Perform operations using Polars-like API
result = df_nw.select(a_sum=nw.col("a").sum(), a_mean=nw.col("a").mean(), b_std=nw.col("b").std())

# 3. Convert back to original library
result_native = result.to_native()
Using the @narwhalify Decorator

Simplifies function definitions for automatic conversion:

python
@nw.narwhalify
def my_func(df: IntoDataFrameT):
    return df.select(nw.col("a").sum(), nw.col("b").mean()).filter(nw.col("a") > 0)


# Automatically handles conversion to/from Narwhals
result = my_func(pandas_df)  # Works!
result = my_func(polars_df)  # Also works!

Top-Level Functions

Conversion Functions
  • from_native(df, ...): Convert native DataFrame/Series to Narwhals object
    • Parameters: pass_through, backend, eager_only, allow_series
  • to_native(nw_obj): Convert Narwhals object back to native library type
  • narwhalify(): Decorator for automatic dataframe-agnostic functions
Data Creation
  • new_series(name, values, dtype): Create a new Series
  • from_dict(data): Create DataFrame from dictionary
  • from_dicts(data): Create DataFrame from sequence of dictionaries
File I/O

Eager Loading:

  • read_csv(source, **kwargs): Read CSV file into DataFrame
  • read_parquet(source, **kwargs): Read Parquet file into DataFrame

Lazy Loading:

  • scan_csv(source, **kwargs): Lazily scan CSV file
  • scan_parquet(source, **kwargs): Lazily scan Parquet file
Aggregation Functions
  • sum(), mean(), min(), max(), median()
  • sum_horizontal(), mean_horizontal(), etc.
Expression Creation
  • col(name): Reference column by name
  • lit(value): Create literal expression
  • when(condition): Create conditional expression
  • format(template, *args): Format expression as string
Utilities
  • generate_temporary_column_name(): Generate unique column names
  • get_native_namespace(obj): Get the native library of an object
  • show_versions(): Print debugging information

DataFrame Methods

Properties
  • columns: List of column names
  • schema: Ordered mapping of column names to dtypes
  • shape: Tuple of (rows, columns)
  • implementation: Name of native implementation
Column Operations
  • select(*exprs): Select columns using expressions
  • with_columns(*exprs): Add or modify columns
  • drop(*columns): Remove specified columns
  • rename(mapping): Rename columns
Row Operations
  • filter(predicate): Filter rows based on conditions
  • head(n): Get first n rows
  • tail(n): Get last n rows
  • sample(n): Randomly sample n rows
  • drop_nulls(): Drop rows with null values
  • unique(): Remove duplicate rows
Inspection
  • is_empty(): Check if DataFrame has no rows
  • is_duplicated(): Identify duplicated rows
  • is_unique(): Identify unique rows
  • null_count(): Count null values per column
  • estimated_size(): Estimate memory usage
Transformations
  • sort(*by): Sort by one or more columns
  • group_by(*by): Group by columns for aggregation
  • join(other, on, how): Perform SQL-style joins
  • pivot(on, index, values): Create pivot table
  • explode(*columns): Expand list columns to long format
  • lazy(): Convert to LazyFrame
Export
  • to_native(): Convert to original library type
  • to_numpy(): Convert to NumPy array
  • to_pandas(): Convert to pandas DataFrame
  • to_polars(): Convert to Polars DataFrame
  • clone(): Create a copy

LazyFrame Methods

LazyFrame provides the same API as DataFrame but with lazy evaluation:

Key Differences
  • Operations build an execution plan without computing
  • collect(): Materialize the LazyFrame into a DataFrame
  • collect_schema(): Get schema without collecting data
  • sink_parquet(path): Write results directly to Parquet
Common Methods

All DataFrame methods are available on LazyFrame:

  • select(), filter(), with_columns(), drop()
  • group_by(), join(), sort(), unique()
  • head(), tail(), top_k()
  • gather_every(): Select rows at regular intervals
  • unpivot(): Convert from wide to long format
  • with_row_index(): Add row index column
  • pipe(): Apply function to LazyFrame

Expression (Expr) API

Expressions are the building blocks for column operations.

Creation
python
nw.col("column_name")  # Reference column
nw.lit(42)  # Literal value
Filtering
  • filter(predicate): Filter elements
  • is_in(values): Check membership
  • is_between(lower, upper): Check range
  • drop_nulls(): Remove nulls
Aggregations
  • count(): Count non-null elements
  • null_count(): Count null values
  • n_unique(): Count unique values
  • sum(), mean(), median(): Statistical aggregations
  • min(), max(): Extremes
  • std(), var(): Spread measures
  • quantile(q): Quantile values
Show full SKILL.md (460 more words)Show less
Transformations

Mathematical:

  • abs(): Absolute value
  • round(), floor(), ceil(): Rounding
  • sqrt(), log(), exp(): Mathematical functions

Type/Value Operations:

  • cast(dtype): Change data type
  • fill_null(value): Replace null values
  • replace_strict(old, new): Replace specific values

Window Operations:

  • rolling_mean(window_size): Moving average
  • rolling_sum(window_size): Moving sum
  • rolling_std(window_size): Moving standard deviation
  • shift(n): Shift values by n positions
  • over(*by): Compute expression over groups

Ranking/Uniqueness:

  • rank(): Assign ranks
  • unique(): Get unique values
  • is_duplicated(): Identify duplicates
  • is_first_distinct(): Mark first distinct occurrences
Namespace Methods

Expressions have specialized namespaces for specific data types:

String Operations (Expr.str)

  • String manipulation methods

DateTime Operations (Expr.dt)

  • Date/time manipulation methods

List Operations (Expr.list)

  • List column operations

Categorical Operations (Expr.cat)

  • Categorical data methods

Struct Operations (Expr.struct)

  • Struct/nested data methods

Name Operations (Expr.name)

  • Column name operations

Series API

Series represents a single column:

Properties
  • Same as DataFrame: shape, dtype, name
Methods
  • Similar to DataFrame but for single column operations
  • Has specialized namespaces: str, dt, list, cat, struct

Type Hints

Full docs: narwhals.typing

TLDR:

DataFrameT module-attribute

DataFrameT = TypeVar('DataFrameT', bound='DataFrame[Any]') TypeVar bound to Narwhals DataFrame.

Use this if your function can accept a Narwhals DataFrame and returns a Narwhals DataFrame backed by the same backend.

Examples:

py
>>> import narwhals as nw
>>> from narwhals.typing import DataFrameT
>>> @nw.narwhalify
>>> def func(df: DataFrameT) -> DataFrameT:
...     return df.with_columns(c=df["a"] + 1)
Frame module-attribute

Frame: TypeAlias = Union["DataFrame[Any]", "LazyFrame[Any]"] Narwhals DataFrame or Narwhals LazyFrame.

Use this if your function can work with either and your function doesn't care about its backend.

Examples:

py
>>> import narwhals as nw
>>> from narwhals.typing import Frame
>>> @nw.narwhalify
... def agnostic_columns(df: Frame) -> list[str]:
...     return df.columns
FrameT module-attribute

FrameT = TypeVar( "FrameT", "DataFrame[Any]", "LazyFrame[Any]" ) TypeVar bound to Narwhals DataFrame or Narwhals LazyFrame.

Use this if your function accepts either nw.DataFrame or nw.LazyFrame and returns an object of the same kind.

Examples:

py
>>> import narwhals as nw
>>> from narwhals.typing import FrameT
>>> @nw.narwhalify
... def agnostic_func(df: FrameT) -> FrameT:
...     return df.with_columns(c=nw.col("a") + 1)

IntoDataFrame module-attribute

IntoDataFrame: TypeAlias = NativeDataFrame Anything which can be converted to a Narwhals DataFrame.

Use this if your function accepts a narwhalifiable object but doesn't care about its backend.

Examples:

py
>>> import narwhals as nw
>>> from narwhals.typing import IntoDataFrame
>>> def agnostic_shape(df_native: IntoDataFrame) -> tuple[int, int]:
...     df = nw.from_native(df_native, eager_only=True)
...     return df.shape

IntoDataFrameT module-attribute

IntoDataFrameT = TypeVar( "IntoDataFrameT", bound=IntoDataFrame ) TypeVar bound to object convertible to Narwhals DataFrame.

Use this if your function accepts an object which can be converted to nw.DataFrame and returns an object of the same class.

Examples:

py
>>> import narwhals as nw
>>> from narwhals.typing import IntoDataFrameT
>>> def agnostic_func(df_native: IntoDataFrameT) -> IntoDataFrameT:
...     df = nw.from_native(df_native, eager_only=True)
...     return df.with_columns(c=df["a"] + 1).to_native()

Common Patterns

Group By and Aggregate
python
result = df.group_by("category").agg(
    count=nw.col("id").count(),
    total=nw.col("amount").sum(),
    average=nw.col("amount").mean(),
)
Conditional Operations
python
result = df.with_columns(category=nw.when(nw.col("value") > 100).then(nw.lit("high")).otherwise(nw.lit("low")))
Joins
python
result = df1.join(
    df2,
    on="key_column",
    how="left",  # inner, left, outer, cross
)
Chain Operations
python
result = (
    df.filter(nw.col("status") == "active")
    .select("user_id", "amount")
    .group_by("user_id")
    .agg(total=nw.col("amount").sum())
    .sort("total", descending=True)
    .head(10)
)

Best Practices

  1. Use @nw.narwhalify for library functions: Simplifies API and handles conversion automatically

  2. Prefer expressions over method chaining: More flexible and composable

    python
    # Good
    df.select(nw.col("a").sum(), nw.col("b").mean())
    
    # Also fine, but less composable
    df.select("a", "b")
  3. Use lazy evaluation when possible: Better performance for complex pipelines

    python
    result = df.lazy().select(...).filter(...).collect()
  4. Always convert back to native: Remember to call .to_native() when returning from library functions (unless using @narwhalify)

  5. Type hint your functions: Use IntoDataFrame and FrameT for better IDE support

  6. Check supported backends: Some operations may not be available on all backends

Important Constraints

  1. Zero Dependencies: Narwhals has no dependencies, keeping it lightweight

  2. Polars API Subset: Uses Polars-style API but may not support all Polars features

  3. Backend Limitations: Some backends (lazy-only) have restricted functionality

Tips

Checking whether a Narwhals frame is a Polars frame:

py
import polars as pl
import narwhals as nw

df_native = pl.DataFrame({"a": [1, 2, 3]})
df = nw.from_native(df_native)
df.implementation.is_polars()

Asserting series equality:

py
import pandas as pd
import narwhals as nw
from narwhals.testing import assert_series_equal
s1 = nw.from_native(pd.Series([1, 2, 3]), series_only=True)
s2 = nw.from_native(pd.Series([1, 5, 3]), series_only=True)
assert_series_equal(s1, s2)
Traceback (most recent call last):
...
AssertionError: Series are different (exact value mismatch)
[left]:
┌───────────────┐
|Narwhals Series|
|---------------|
| 0    1        |
| 1    2        |
| 2    3        |
| dtype: int64  |
└───────────────┘
[right]:
┌───────────────┐
|Narwhals Series|
|---------------|
| 0    1        |
| 1    5        |
| 2    3        |
| dtype: int64  |
└───────────────┘

© anam-org, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/narwhals of anam-org/metaxy.

Open the folder on GitHubat commit 8337842

Compare with similar skills

Narwhals 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.

Narwhals compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Narwhals this skillanam-org/metaxy124—~3.3kAutomated safety check: PassApache-2.0
Hybrid-Engine Data Analysiscode-yeongyu/oh-my-openagent70k—~1.4kAutomated safety check: PassCustom licence
Optimuskgmims-harvard/OptimusKG147—~1.9kAutomated safety check: PassMIT
PolarsK-Dense-AI/scientific-agent-skills48k1 repos~3.3kAutomated safety check: PassMIT
Ingesting Dataancoleman/ai-design-components525—~1.9kAutomated safety check: PassMIT
Transforming Dataancoleman/ai-design-components525—~3kAutomated safety check: PassMIT

Similar skills

  • Hybrid-Engine Data Analysis

    code-yeongyu/oh-my-openagent

    Analyzes CSV, Parquet and JSON data with DuckDB, Polars, numpy and matplotlib, preferring a persistent kernel over repeated one-shot processes.

    70k GitHub stars~1.4k tokensUpdated today
    Data & AnalyticsAuto-check passed
  • Optimuskg

    mims-harvard/OptimusKG

    Guide for using OptimusKG, the biomedical knowledge graph, through the optimuskg Python client.

    147 GitHub stars~1.9k tokensUpdated 18 days ago
    Data & AnalyticsAuto-check passed
  • Polars

    K-Dense-AI/scientific-agent-skills

    High-performance DataFrame library for Python ETL, analytics, and pandas migration.

    48k GitHub starsUsed in 1 repo~3.3k tokens
    Data & AnalyticsAuto-check passed
  • Ingesting Data

    ancoleman/ai-design-components

    Data ingestion patterns for loading data from cloud storage, APIs, files, and streaming sources into databases.

    525 GitHub stars~1.9k tokensUpdated 10 mo ago
    Data & AnalyticsAuto-check passed
  • Transforming Data

    ancoleman/ai-design-components

    Transform raw data into analytical assets using ETL/ELT patterns, SQL (dbt), Python (pandas/polars/PySpark), and orchestration (Airflow).

    525 GitHub stars~3k tokensUpdated 10 mo ago
    Data & AnalyticsAuto-check passed
  • Flowfile Frame And Codegen

    Edwardvaneechoud/Flowfile

    Deep dive into flowfileframe — the Polars-LazyFrame-shaped Python API that builds an in-process flowfilecore FlowGraph as a side effect of every method call — covering the FlowFrame/Expr internals…

    385 GitHub stars~12k tokensUpdated today
    Data & AnalyticsAuto-check passed

More from anam-org/metaxy

All 8 skills in this repo
  • Metaxy

    anam-org/metaxy

    This skill should be used when the user asks to "define a feature", "create a BaseFeature class", "track feature versions", "set up metadata store", "field-level lineage", "FieldSpec", "FeatureDep"…

    124 GitHub stars~1.5k tokensUpdated 9 days ago
    Auto-check passed
  • Tach

    anam-org/metaxy

    This skill should be used when the user asks to "add a tach module", "configure tach layers", "define module boundaries", "set up interfaces", "run tach check", "check module boundaries", "tach…

    124 GitHub stars~1.2k tokensUpdated 9 days ago
    Auto-check passed
  • Claude Improve Config

    anam-org/metaxy

    Self-reflect on the current session to identify mistakes and propose improvements to .claude configuration (CLAUDE.md, hooks, skills).

    124 GitHub stars~1k tokensUpdated 9 days ago
    Auto-check passed
  • Docs Page Frontmatter

    anam-org/metaxy

    Write YAML front matter for documentation pages with appropriate titles and descriptions for social cards.

    124 GitHub stars~948 tokensUpdated 9 days ago
    Auto-check passed
  • Hypothesis

    anam-org/metaxy

    Use Hypothesis for property-based testing to automatically generate comprehensive test cases, find edge cases, and write more robust tests with minimal example shrinking.

    124 GitHub stars~1.7k tokensUpdated 9 days ago
    Auto-check passed
  • Sybil

    anam-org/metaxy

    Use Sybil for testing code examples in documentation and docstrings.

    124 GitHub stars~1.6k tokensUpdated 9 days ago
    Auto-check passed

Works with

Questions about Narwhals

What does Narwhals do?

Effectively use Narwhals to write dataframe-agnostic code that works seamlessly across multiple Python dataframe libraries. Narwhals is an agent skill from anam-org/metaxy. Effectively use Narwhals to write dataframe-agnostic code that works seamlessly across multiple Python dataframe libraries.

When should I use Narwhals?

Narwhals fits situations like: tasks that involve DataFrames.

How do I install Narwhals in Claude Code?

Run `npx skills add anam-org/metaxy --skill narwhals -a claude-code`. Or copy the skill folder (.claude/skills/narwhals in anam-org/metaxy) into .claude/skills/narwhals in your project. Claude Code loads it when a task matches its description.

How do I install Narwhals in Codex?

Run `npx skills add anam-org/metaxy --skill narwhals -a codex`. Or copy the skill folder (.claude/skills/narwhals in anam-org/metaxy) into .agents/skills/narwhals in your project. Codex loads it when a task matches its description.

Can I use Narwhals 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 anam-org/metaxy --skill narwhals -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/narwhals, .gemini/skills/narwhals, .github/skills/narwhals and .opencode/skills/narwhals in your project.

What does Narwhals need to run?

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

Does Narwhals access the network?

SKILL.md names 1 domain. As links in the text: narwhals-dev.github.io. This is read from the text; nothing was executed.

Is Narwhals 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 Narwhals use?

Narwhals is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Narwhals use?

About 3.3k tokens (SKILL.md is roughly 13k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Narwhals?

Skills that share tags, products or a category with Narwhals: Hybrid-Engine Data Analysis (code-yeongyu/oh-my-openagent, 70k stars), Optimuskg (mims-harvard/OptimusKG, 147 stars), Polars (K-Dense-AI/scientific-agent-skills, 48k stars) and Ingesting Data (ancoleman/ai-design-components, 525 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Narwhals?

anam-org (a GitHub organization) maintains it in anam-org/metaxy, which has 124 GitHub stars. The repository holds 8 skills in this directory. The repository was last updated on September 30, 2026.

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