Input contract
The native input is a versioned JSON envelope carrying an ordered list of
content blocks. Each block is an object with a UTF-8 text string.
The envelope is capped at 1 MiB, 1000 blocks, and JSON nesting depth 32.
{
"suite": "v1",
"blocks": [
{ "text": "you are a helpful assistant" },
{ "text": "you are a helpful assistant" },
{ "text": "what is the capital of France?" }
]
}
No block text is ever echoed back in the report. Output carries each block's index, counts, and a content hash only. Hashes are pseudonyms — they can be dictionary-guessed and are not anonymization.
Output contract
The report measures UTF-8 bytes and exact whole-block duplicates, and totals the bytes and whitespace tokens the duplicates cost. Two views ship:
- Machine-readable JSON (default) — the stable, versioned record.
- Human-readable evidence report (
--markdown) — a summary header, a duplicate-block table (one row per repeated content hash: occurrences, copies, bytes/tokens per block, wasted bytes/tokens), and the JSON verbatim.
The result is tri-state: unknown is kept distinct from pass and fail.
A run with no measurable blocks is unknown, not a pass.
Exit-code contract
The exit code is part of the contract, so the CLI drops into CI without parsing output:
| Code | Meaning |
|---|---|
0 | pass — no duplicate context found |
1 | fail — duplicate context found |
2 | unknown or unsupported input |
Adapters
Adapters are one-way imports: they translate a recorded run into the block contract. They do not reconstruct, replay, or store the run, and make no provider-token claim.
conereplay-v1
Accepts adapter=conereplay-v1 with trace.events from a
recorded ConeReplay EventRecord. UTF-8 input/output strings are split into blocks.
It does not assert the blocks were all transmitted to a provider.
openai-chat-v1
Accepts adapter=openai-chat-v1 with a chat-completions
messages[] array — the resent-history case. Each message's text becomes
a block, so repeated system/tool context across the array is detected. A string
content becomes one block; null/absent content (for example
an assistant turn carrying only tool_calls) contributes no block; an
array of content parts contributes one block per type=text part, while
non-text parts (image/audio) are out of measurement scope and ignored. Any other
content shape is refused. Caps: the shared 1 MiB / depth-32 envelope limit plus 1000
messages and 1000 content parts per message.
What the measurement does and does not claim
The tokenizer is unicode-whitespace v1.0, labeled explicitly and not a provider-model tokenizer. Counting is deterministic. The tool does not claim model tokens, a billing estimate, substring repetition, semantic duplication, or context truncation. Evaluation never executes payload code, fetches URLs, or contacts a provider. A report needs owner review before disclosure.
Status
Hosted history, teams, and billing are not yet available: by design they stay off until authentication, isolation, retention, and deletion tests pass. See the implementation notes for the exact caps and the acceptance fixtures.