TNC
TNC is a command-line tool for turning saved news articles into traceable source spans and extracting source-attributed assertions from them. It also includes tools for comparing source relationships and for gated historical replay. It is intended for researchers and fact-checkers working with saved news archives.
TNC does not decide whether a news claim is true. It also does not automatically determine that two publishers are independent sources.
Install
TNC is published on PyPI as tnc-provenance. (The name tnc on PyPI belongs to
an unrelated project; pip install tnc installs that project, not this one.)
Windows (PowerShell):
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install tnc-provenance
Linux / macOS:
python -m venv .venv
.venv/bin/pip install tnc-provenance
With uv (any platform), if you already use uv:
uv venv
uv pip install tnc-provenance
The venv examples above are the recommended pattern on Windows because pip
places tncprov.exe in a Scripts directory that is not on PATH by default.
Calling the installed executable through the venv path always works. If your
environment is already managed — Conda, a system Python with a configured PATH,
an activated venv — a plain pip install tnc-provenance will work there too.
Command name
The package installs two identical console commands: tnc and tncprov.
On Windows PowerShell, use tncprov. PowerShell's NetTCPIP module defines
tnc as an alias for Test-NetConnection, and PowerShell resolves aliases
before executables on PATH. Bare tnc in PowerShell runs the networking
cmdlet, not TNC. tncprov has no collision on any platform.
On Linux, macOS, cmd.exe, and any shell without a tnc alias, either command
works.
See it work
The included example article first reports three injuries and later updates that number to five.
Windows:
.\.venv\Scripts\tncprov.exe assertions tests\fixtures\article_v1.html
Linux / macOS:
.venv/bin/tncprov assertions tests/fixtures/article_v1.html
Output:
Admitted: 2
Officials said three people were injured.
Source spans: 2
Officials said five people were injured.
Source spans: 6
Rejected: 0
Each number after Source spans: is a zero-based source-span ordinal. If an
assertion references multiple spans, their ordinals are comma-separated; the
field is not a count.
This preserves both the earlier statement and the later update rather than collapsing them into one claim.
What TNC does
TNC currently provides three main workflows:
- Parse saved article HTML into traceable source spans. Structural types include headings, paragraphs, quotes, corrections, update notices, list items, table cells, and captions.
- Extract supported assertions from those spans. Assertions are admitted only when their source coordinates and verbatim support validate.
- Run gated historical replay. Archived evidence can be evaluated against capture time, review state, and replay policy before a historical snapshot is released.
The first two workflows operate directly on local HTML files. Historical replay has a stricter trust model and is described separately below.
Using your own article
Pass a local file path:
.\.venv\Scripts\tncprov.exe parse "C:\path\to\saved article.html"
.\.venv\Scripts\tncprov.exe assertions "C:\path\to\saved article.html"
The input must be a local UTF-8 HTML file using a supported article structure. URLs, PDFs, screenshots, and plain-text files are not inputs to these commands. The examples in this README require no API key or article download.
For local parse and assertions runs, TNC records the parsing time as an
observation time. That does not establish when the article was first published
or historically available.
Source spans
A source span is an ordered, immutable piece of article text.
TNC recognizes these structural types:
paragraphheadingquotecaptionlist_itemtable_cellcorrectionupdate_notice
tnc parse prints tab-separated ordinal, structural type, and normalized
text. The ordinal is the coordinate used by extracted assertions, not an HTML
line number. For example, Source spans: 6 means the assertion is supported by
source-span ordinal 6.
To inspect one span directly:
uv run tnc parse tests/fixtures/article_v1.html --span 6
Omit --span to show all spans. If the ordinal does not exist, TNC reports an
error.
Assertions
Run:
uv run tnc assertions path\to\article.html
The current extractor deliberately recognizes a narrow attribution pattern:
<speaker> <operator> <proposition>
Supported source-text operators are:
reportedconfirmeddeniedestimatedallegedexpectedsaid
The current source-text mapping is:
said→REPORTED(the extracted predicate remainssaid)
An assertion is admitted when its source coordinates exist and its recorded verbatim support is present in those source spans.
Admission does not mean TNC verified that the proposition is true. Rejected candidates are listed with reasons; unsupported or ambiguous wording may be skipped rather than guessed. Only the first sentence of each span is considered. Sentence detection has limited punctuation rules, including support for common titles and decimal numbers; ambiguous initials and dotted abbreviations cause the span to be skipped.
Source relationships and provenance
TNC also contains a provenance pipeline for examining relationships between
documents. The Python API measures overlap separately from the relationship
rules; it is not currently exposed as a tnc provenance CLI command.
Existing document-level judgments are:
explicitly_citeslikely_derived_fromreprint_of
The classifier may also return no judgment. No judgment does not establish that two sources are independent.
Reviewed shared_source_evidence can account for common wire-service,
syndication, or underlying-authority material without treating similar wording
as proof that one publisher copied another. When supplied, it prevents overlap
alone from triggering reprint or derivation labels, even for identical wording;
an explicit citation still takes priority. Without that annotation, the existing
overlap rules remain in effect.
Callers must supply reviewed evidence to infer_source_relation or
measure_provenance_signals. The API does not automatically load review files
or infer lineage from a wire-service name. The reviewed comparisons are recorded
in the casualty provenance review
and its pair annotations.
For a complete CLI-to-Python example using those frozen inputs, see the source-provenance walkthrough.
Historical replay
Historical replay is an advanced, fail-closed workflow.
Historical replay requires a configured review store; the included examples return MISSING_REVIEW until one is set up.
The repository currently includes archived ABC captures and machinery that checks exact saved body hashes, archive index evidence, capture-time constraints, review state, transition configuration, and release receipts.
The source-hosted CLI intentionally starts with no trusted review authority.
Real included captures therefore remain blocked with MISSING_REVIEW rather
than being released automatically. Archive presence alone is not promoted into
an approved historical claim.
Example:
uv run tnc historical-replay --document abc-early --version abc-early-archive-20130521120016 --time 2013-05-21T12:00:16Z
A real capture without trusted review currently returns an unverified
result. Passing the synthetic replay tests does not establish that the saved live
articles were available at a particular historical time.
See the historical replay CLI documentation for the complete trust and release model.
Limits
TNC is intentionally conservative.
It does not:
- determine whether a proposition is true;
- infer source independence automatically;
- fetch articles from URLs or accept non-HTML input such as PDFs, screenshots, or plain text;
- treat archive capture as proof of first publication time;
- release historical evidence without the required review state;
- act as a general natural-language claim extractor.
The current assertion extractor uses deliberately limited grammatical and sentence-boundary rules. An empty extraction result does not mean an article contains no claims.
Project map
| Location | Purpose |
|---|---|
src/tnc/ |
CLI entry points and top-level package integration |
src/tnc/ingestion/ |
Saved content, article parsing, and ingestion |
src/tnc/spans/ |
Source spans, assertions, state transitions, and replay |
src/tnc/provenance/ |
Source relationships, review, admission, and historical evidence logic |
tests/fixtures/ |
Small deterministic examples and replay fixtures |
tests/golden/ |
Expected parsed output used to detect changes |
corpus/tib_run_a/ |
Frozen Moore tornado reporting, archive evidence, and review material |
Frozen corpus objects are evidence and should not be reformatted or modified casually. Their hashes identify their exact bytes. See the corpus guide for acquisition history, availability uncertainty, and review requirements.
Help
uv run tnc --help
uv run tnc parse --help
uv run tnc assertions --help
uv run tnc historical-replay --help
| Message or symptom | What to check |
|---|---|
uv is not recognized |
Ensure uv is installed and available in the current terminal. |
| Project configuration cannot be found | Run commands from the checkout containing pyproject.toml. |
| File cannot be opened | Check the path and quote it if it contains spaces. |
| File cannot be decoded or layout is unsupported | Use UTF-8 HTML with a supported article structure; try an included fixture first. |
| No source span with that ordinal | Parse without --span to see the available ordinals. |
Admitted: 0 |
The extractor may not support the wording; inspect the parsed text. |
Input and argument errors return a nonzero exit code. In PowerShell,
$LASTEXITCODE shows the exit code of the command that just ran.
Acknowledgments
Development of TNC was assisted by AI coding tools including ChatGPT (OpenAI), DeepSeek, and Gemini (Google). The project's design decisions, review, and validation are SolarPluto's responsibility.
Development
TNC is developed from a source checkout using uv.
Clone and sync the locked environment:
git clone https://github.com/SolarPluto/TNC
cd TNC
uv sync --locked
Run any command from the source checkout via uv run:
uv run tnc assertions tests/fixtures/article_v1.html
uv run tnc parse tests/fixtures/article_v1.html
Run the test suite:
uv run pytest -q
Tests cover parsing, CLI behavior, assertions, provenance, temporal behavior, replay fixtures, and frozen-body integrity.
Release files for tnc-provenance 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tnc_provenance-0.1.2.tar.gz | 206.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tnc_provenance-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 478.4 kB
Release files / tnc_provenance-0.1.2.tar.gz
| Download URL | tnc_provenance-0.1.2.tar.gz |
|---|---|
| Size | 206.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a1297255a9fe0476402df001c8d1e7a46e931f8da8cc4ccc689dda089b1ca5ca
|
|
BLAKE2b-256 checksum How to use checksums |
78b40a83e7d9b4437814ae5f2abfaf8a14ea042af384f35e8114ce0cda205161
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / tnc_provenance-0.1.2-py3-none-any.whl
| Download URL | tnc_provenance-0.1.2-py3-none-any.whl |
|---|---|
| Size | 271.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6cd09fb1ba07083a1be765f06a558d6509f3dfb845a39f729911fbf974a2310a
|
|
BLAKE2b-256 checksum How to use checksums |
57f61937eaa9afb7e80699ad37ef20ef04c9df3e2e85a623029c3abdf1b72486
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|