# Record-ID Conventions for FinancialDataRegistry

`FinancialDataRegistry` is a generic `bytes32 → CID` registry. To keep
on-chain records human-discoverable, FF For Future uses a prefix
convention for `recordId`s.

## The convention

```
recordId = keccak256(abi.encodePacked("<kind>:<deal>:<variant>"))
```

| Prefix | Meaning | Example label | Example key |
|--------|---------|---------------|-------------|
| `data:` | Underlying private-credit dataset (the borrower file) | `Thornfields Capital AG — Q1 2026` | `keccak256("data:thornfields-2026-q1")` |
| `idr:`  | A HAL-council IDR analysing a `data:` record | `IDR — Thornfields, Claude` | `keccak256("idr:thornfields-2026-q1:claude-3.5")` |
| `meta:` | Reserved for off-chain pointers (changelogs, runbooks) | `Demo runbook (Pinata)` | `keccak256("meta:thornfields-2026-q1:runbook")` |

The string before the keccak is stored verbatim in `label`, so any
front-end calling `getRecord(recordId)` recovers the human-readable
purpose without needing to invert the hash.

## Linking data → IDR off-chain

Until the contract gains a native `IdrAnchored(dataId, idrId)` event,
the convention IS the link:

```
data:<deal>:<variant>      ← published first by data owner
idr:<deal>:<variant>:<model>  ← published per analysis by analyser
```

A subgraph or indexer can group by the `<deal>:<variant>` substring and
recover the relationship without any contract change. Two IDRs with the
same `<deal>:<variant>` and different `<model>` are the replay pair the
demo proves disagreement on.

## On-chain ergonomics

`getAllRecordIds()` returns the unordered list of all `bytes32` keys.
Front-ends iterate and call `getRecord(recordId)` per key — `label`
contains the prefix so filtering UI-side is trivial:

```javascript
const ids = await registry.getAllRecordIds();
const records = await Promise.all(ids.map(id => registry.getRecord(id)));
const dataRecords = records.filter(([cid, label]) => label.startsWith("data:"));
const idrRecords  = records.filter(([cid, label]) => label.startsWith("idr:"));
```

## Why not extend the contract?

Rob's `FinancialDataRegistry` is intentionally general. Adding
hardcoded `setIdrCid()` / `dataIdrLink` storage would couple the contract
to the FF For Future workflow. The prefix convention keeps the contract
generic and pushes domain semantics off-chain, where they cost nothing
to revise. If the FF audit-chain pattern stabilises across multiple
deals, a `FinancialDataRegistryV2` can add native linkage; v1 doesn't
need it.

## Bytes32 helper (TypeScript)

```typescript
import { keccak256, toUtf8Bytes } from "ethers";

export function recordKey(kind: "data" | "idr" | "meta", deal: string, ...extra: string[]): string {
  const parts = [kind, deal, ...extra];
  return keccak256(toUtf8Bytes(parts.join(":")));
}

// recordKey("data", "thornfields-2026-q1")
// recordKey("idr",  "thornfields-2026-q1", "claude-3.5")
// recordKey("idr",  "thornfields-2026-q1", "gemini-1.5-pro")
```

## Python helper

```python
from eth_utils import keccak

def record_key(kind: str, deal: str, *extra: str) -> bytes:
    parts = ":".join([kind, deal, *extra])
    return keccak(parts.encode())
```
