# Columns, types and units

Three files, one source. `domains.json` contains both tables:
`{ generatedAt, defaultDomain, domains: [...], schemaFields: [...] }`.

## `domains.csv`

One row per focus domain.

| Column | Type | Meaning |
| --- | --- | --- |
| `id` | string | The value you send as `domain`. Stable; kebab-case. |
| `label` | string | Human name, as the API returns it. |
| `description` | string | One-line blurb, as the API returns it. |
| `aliases` | string | Other accepted values, `\|`-separated. Empty when none. |
| `is_default` | boolean | `true` for exactly one row — `fashion`. |
| `example` | string | What a typical photo returns. |
| `empty_message` | string | What the platform says when nothing is shoppable. |
| `subject_singular` | string | The noun the prompt uses for one object. Empty for `fashion`, whose prompt is hand-written rather than generated from a profile. |
| `subject_plural` | string | The noun the prompt uses for the list. |
| `scope` | string | Any scene qualifier, e.g. `in this room or interior`. Empty when unscoped. |
| `name_examples` | string | The `simple_name` examples the prompt offers. |
| `exclusions` | string | What the prompt tells the reader *not* to return. |
| `analysis_prompt_characters` | integer | Length of the assembled reading prompt, in characters. |
| `analysis_prompt_sentences` | integer | Sentence count, by `. ` boundaries. A rough shape metric, not linguistics. |
| `analysis_prompt_sha256` | string | Hex SHA-256 of the assembled prompt. Pin this to detect a change. |
| `schema_field_count` | integer | Properties in the item schema, including `simple_name` and `full_description`. |
| `has_dedicated_extraction_prompt` | boolean | `true` only for `fashion`, whose extraction instruction lives in `src/pruna.js` beside the Pruna call it was tuned against. Every other domain uses the shared generic one. |

## `schema-fields.csv`

One row per (domain, field). This is the structured description the reader must
return for every object it finds.

| Column | Type | Meaning |
| --- | --- | --- |
| `domain_id` | string | Joins to `domains.csv` `id`. |
| `field` | string | The property name in the item object. |
| `type` | string | `string` or `array`. |
| `is_list` | boolean | `true` when the field holds several names. |
| `required` | boolean | `true` for every row — see below. |
| `enum_values` | string | For a closed vocabulary, the allowed values `\|`-separated. Empty otherwise. |
| `max_items` | integer | For a list, its ceiling. Empty for a string. |
| `purpose` | string | What the field is for. Hand-written. |

### Why `required` is always true

The reading call uses a strict JSON schema: `additionalProperties: false`, and
every property listed in `required`. A model cannot quietly skip the awkward
fields — a partial object is not expressible. An *empty list of objects* is, and
is the correct answer for a photo with nothing shoppable in it.

`test/api-platform.test.js` asserts this property for every domain, so a field
added without being required fails the suite.

## Units and conventions

- Booleans are the strings `true` and `false`.
- Multi-value cells use `|` with no surrounding spaces.
- An absent value is an empty cell, never `null`, `NA` or `-`.
- CSV is RFC 4180: comma-separated, `"` quoting, `""` for a literal quote,
  UTF-8, `\n` line endings, one header row.
- `generatedAt` in `domains.json` is an ISO 8601 instant in UTC.

## Checking it parses

```bash
python3 - <<'PY'
import csv, json, hashlib
domains = list(csv.DictReader(open('domains.csv')))
fields  = list(csv.DictReader(open('schema-fields.csv')))
data    = json.load(open('domains.json'))

assert len(domains) == len(data['domains'])
assert len(fields)  == len(data['schemaFields'])
assert sum(d['is_default'] == 'true' for d in domains) == 1

# every field row joins to a domain, and the counts agree
ids = {d['id'] for d in domains}
assert all(f['domain_id'] in ids for f in fields)
for d in domains:
    n = sum(1 for f in fields if f['domain_id'] == d['id'])
    assert n == int(d['schema_field_count']), (d['id'], n)

print(f"{len(domains)} domains, {len(fields)} field rows, consistent")
PY
```

The same three assertions run in the Inspired repository's own test suite
against the generated files, so a broken dataset fails the build rather than
being published.

## Example use

Map the response into your own product record without guessing which fields a
domain carries:

```bash
# which fields will a furniture item have?
awk -F, '$1=="furniture" {print $2}' schema-fields.csv

# which domains describe a material finish, and under what name?
grep -E 'finish' schema-fields.csv | cut -d, -f1,2
```
