# How the Claim Ledger Connects Source Evidence to Knowledge Assertions in Claude-Obsidian

> Discover how the claim ledger in Claude-Obsidian links source evidence to knowledge assertions using stable IDs and JSON arrays. Learn about provenance, freshness, and independence.

- Repository: [Agrici.Daniel/claude-obsidian](https://github.com/AgriciDaniel/claude-obsidian)
- Tags: architecture
- Published: 2026-08-26

---

**The claim ledger connects knowledge assertions to source evidence through stable identifiers and structured JSON evidence arrays that enforce provenance, freshness, and independence constraints.**

In the **claude-obsidian** repository, provenance is managed through a dual-ledger system that separates raw evidence from derived knowledge. The claim ledger serves as the bridge that ties every assertion back to auditable source material using cryptographically stable identifiers and rigorous validation rules.

## The Dual-Ledger Architecture

Claude-obsidian maintains provenance in two complementary JSON files stored in the vault's `wiki/meta/ledgers/` directory:

- **Source ledger** ([`wiki/meta/ledgers/source-ledger.json`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/wiki/meta/ledgers/source-ledger.json)): Catalogs every piece of raw evidence with its origin, content hash, authority, and review status.
- **Claim ledger** ([`wiki/meta/ledgers/claim-ledger.json`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/wiki/meta/ledgers/claim-ledger.json)): Stores knowledge assertions alongside risk assessments and an **evidence array** that references entries in the source ledger.

This separation ensures that source material exists independently of the claims that reference it, enabling multiple assertions to draw from the same evidence without duplication.

## Creating Stable Source Identifiers

The connection begins with deterministic ID generation in [`claude_obsidian/ledgers.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/ledgers.py). The `stable_source_id()` function (lines 7-14) produces immutable identifiers by hashing a canonical representation of the source:

```python
from claude_obsidian.ledgers import stable_source_id

# Generate a deterministic source ID

origin_kind = "url"
canonical_locator = "https://example.com/article.pdf"
content_sha256 = "d2d2d2..."  # 64-character hex string

src_id = stable_source_id(origin_kind, canonical_locator, content_sha256)

# Returns: "src-a1b2c3d4e5f6..."

```

The function constructs a hash input using the pattern `"{origin_kind}\0{canonical_locator}\0{content_sha256}"`, ensuring that identical source content always produces the same identifier regardless of when or where it is registered.

## Recording and Validating Sources

Once generated, the source ID serves as the key in the source ledger. The `validate_source_ledger` function (lines 1018-1025) enforces that the ID matches the canonical identity derived from the stored origin and content hash. A complete source entry includes:

- `origin`: Kind and locator (file path or URL)
- `content_sha256`: SHA-256 hash of the content
- `authority`: Trust level (e.g., `official`, `community`)
- `review_status`: Current state (`active`, `stale`, `rejected`)
- `pages`: Array of wiki pages citing this source

```python
from claude_obsidian.ledgers import empty_source_ledger

source_ledger = empty_source_ledger()
source_ledger["sources"][src_id] = {
    "origin": {"kind": "url", "locator": "https://example.com/article.pdf"},
    "content_kind": "document",
    "title": "Example Article",
    "authority": "official",
    "content_sha256": content_sha256,
    "review_status": "active",
    "pages": ["wiki/articles/example.md"],
}

```

## Linking Claims to Evidence

The claim ledger establishes connections through an `evidence` array within each claim object. Every element in this array is a structured object containing:

```json
{
  "source_id": "src-a1b2c3d4e5f6...",
  "relation": "supports",
  "locator": "optional section reference"
}

```

Valid relations are defined in the `EVIDENCE_RELATIONS` constant: `supports`, `contradicts`, or `context`. When validating the claim ledger, `validate_claim_ledger` (lines 1054-1064) verifies that every `source_id` resolves to an existing entry in the source ledger and that the relation type is valid.

```python
from claude_obsidian.ledgers import empty_claim_ledger

claim_ledger = empty_claim_ledger()
claim_ledger["claims"]["clm-001"] = {
    "text": "The article confirms that X is true.",
    "risk": "normal",
    "assessment": "accepted",
    "confidence": "high",
    "location": {"path": "wiki/knowledge/x_is_true.md"},
    "reviewed_at": "2024-09-01",
    "evidence": [
        {"source_id": src_id, "relation": "supports"}
    ],
}

```

## Enforcing Evidence Quality and Independence

The validation layer applies sophisticated rules to ensure claims rest on verifiable foundations. For a claim to receive an `accepted` assessment, the validator (lines 987-1000) requires at least one **fresh active support**—a source with `review_status == "active"` that has not exceeded its freshness threshold.

High-risk accepted claims face stricter requirements: they must demonstrate **two independent sources**. The `_independent_group_count` function groups sources by identical origin, content hash, or declared independence key to prevent circular reasoning from duplicate or derivative sources.

Each claim also records a `location` object specifying the wiki page path and optional anchor (`^block-id` or `#heading`). The validator cross-references these against `_markdown_anchor_sets` to confirm that the claimed location actually exists in the vault's Markdown files.

## Running Complete Validation

To verify the integrity of the entire provenance chain:

```python
from claude_obsidian.ledgers import validate_source_ledger, validate_claim_ledger

src_errors = validate_source_ledger(source_ledger)
clm_errors = validate_claim_ledger(claim_ledger, source_ledger)

assert not src_errors, "Source ledger validation failed"
assert not clm_errors, "Claim ledger validation failed"

```

The claim ledger validation specifically checks that all source references resolve correctly, that evidence relations are valid, and that freshness and independence constraints meet the thresholds defined by the claim's risk level.

## Summary

- **Dual-ledger design**: The claim ledger references the source ledger via stable IDs, maintaining separation between evidence and assertions.
- **Cryptographic stability**: Source IDs are deterministically generated from content hashes, ensuring immutable provenance links.
- **Structured evidence**: Claims link to sources through typed relations (`supports`, `contradicts`, `context`) with optional locators.
- **Quality enforcement**: Validation requires fresh active supports and independent sourcing for high-risk claims.
- **Location verification**: Every claim tracks its wiki page location and anchor, validated against actual Markdown content.

## Frequently Asked Questions

### What is the difference between the source ledger and claim ledger in claude-obsidian?

The **source ledger** ([`wiki/meta/ledgers/source-ledger.json`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/wiki/meta/ledgers/source-ledger.json)) catalogs raw evidence files, URLs, and manual entries with their content hashes and review status. The **claim ledger** ([`wiki/meta/ledgers/claim-ledger.json`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/wiki/meta/ledgers/claim-ledger.json)) contains knowledge assertions and metadata (risk, confidence, assessment) along with evidence arrays that point to specific entries in the source ledger. This separation allows multiple claims to reference the same source without duplication.

### How does the system ensure source identifiers remain stable over time?

The `stable_source_id()` function in [`claude_obsidian/ledgers.py`](https://github.com/AgriciDaniel/claude-obsidian/blob/main/claude_obsidian/ledgers.py) generates deterministic identifiers by hashing a canonical string composed of the origin kind, locator, and content SHA-256. This ensures that identical source material always produces the same `src-...` identifier, preventing broken links when sources are re-imported or synchronized across different vault instances.

### What validation rules ensure claims rest on verifiable evidence?

The `validate_claim_ledger` function enforces that every `source_id` in a claim's evidence array exists in the source ledger and that the `relation` value belongs to `EVIDENCE_RELATIONS`. Additionally, accepted claims must have at least one fresh active support (not stale, status `active`), and high-risk claims require two independent sources as calculated by `_independent_group_count`.

### How does the claim ledger handle conflicting evidence for a single assertion?

The evidence array supports multiple entries with different relations, allowing a claim to simultaneously reference sources that `support`, `contradict`, or provide `context` for the assertion. The validation logic evaluates the net evidence state based on source authority and freshness, though the current implementation leaves final assessment decisions to human reviewers who weigh the conflicting sources recorded in the ledger.