# JOE Schema Summary — Rules and Prompts

Purpose: Provide a consistent, agent‑friendly summary for each schema (including instance‑specific ones) so tools and UIs can reason without parsing full runtime schema files.

These summaries are served by MCP hydrate and by GET `/API/schema/:name?summaryOnly=true`.

## What to include in every summary

- description: One‑sentence explanation of what the schema represents
- purpose: When/why to use it; context among related schemas
- wip: true if the schema is under active development
- source: 'core' | 'instance' (filled automatically; you can override)
- labelField: Primary human label (prefer name → title → label)
- defaultSort: { field, dir } sensible default (prefer joeUpdated desc; pages often path asc; posts often post_date desc)
- searchableFields: Array of string field names that are good for text queries
- allowedSorts: Whitelist of sortable fields agents should use
- relationships:
  - outbound: Array of { field, targetSchema, cardinality } where cardinality is 'one' | 'many' (auto-generated from fields; see Relationships section below)
  - inbound: { graphRef: 'server/relationships.graph.json' } (do not hand‑maintain inbound; it's computed from the graph later)
- joeManagedFields: ["created","joeUpdated"] (always include)
- fields: Array of normalized field descriptors:
  - Required: name, type
  - Optional: isArray, isReference, targetSchema, enumValues, display, comment, tooltip, format

Notes
- display/comment/tooltip are enriched automatically at runtime from the schema’s field definitions; include them in the summary when they clarify usage.
- For select fields, add enumValues when known (e.g., role, priority, content_type).

## Relationships (outbound)

**Auto-generation**: The `relationships.outbound` array is automatically generated from fields in your schema definition that have `isReference: true` and a `targetSchema` value. Cardinality is determined automatically:
- `isArray: true` or `type: 'objectList'` → `cardinality: 'many'`
- Single reference field → `cardinality: 'one'`

**How it works**: In your schema file, mark reference fields like this:
```javascript
fields: [
  { name: 'project', type: 'string', isReference: true, targetSchema: 'project' },
  { name: 'members', type: 'string', isArray: true, isReference: true, targetSchema: 'user' }
]
```
The system will automatically generate:
```javascript
relationships: {
  outbound: [
    { field: 'project', targetSchema: 'project', cardinality: 'one' },
    { field: 'members', targetSchema: 'user', cardinality: 'many' }
  ]
}
```

**Manual override**: You can manually specify `relationships.outbound` in the `summary` block if you need to:
- Add relationships not detected by field analysis (e.g., implicit relationships)
- Correct incorrectly inferred relationships
- Provide clearer field names or target schemas

**Best practice**: Let automatic generation handle most cases. Ensure your schema fields are properly marked with `isReference: true` and `targetSchema`, and relationships will be generated automatically. Only manually specify if you need to override or add relationships not captured by field definitions.

## Cardinality guidance

- A single reference (select/goto a single item) → 'one'
- Arrays, objectList, or multi‑select references → 'many'

## Heuristics (when unsure)

- labelField: name → title → label
- defaultSort: joeUpdated desc; pages: path asc; posts: post_date desc
- searchableFields: name, info, description, plus any user‑visible identifiers (code, slug, email)
- allowedSorts: include dates (joeUpdated, created, due_date), labelField, and common categoricals

## Authoring prompt (for new instance‑specific schemas)

Use this prompt to draft a `summary` block to paste into the schema file.

```
You are defining a curated summary for a JOE schema named <SCHEMA_NAME>.
Return strictly a JSON object with these keys:
{
  "description": "…",
  "purpose": "…",
  "labelField": "…",
  "defaultSort": { "field": "…", "dir": "asc|desc" },
  "searchableFields": ["…"],
  "allowedSorts": ["…"],
  "relationships": {
    "outbound": [
      { "field": "<field>", "targetSchema": "<schema>", "cardinality": "one|many" }
    ],
    "inbound": { "graphRef": "server/relationships.graph.json" }
  },
  "joeManagedFields": ["created","joeUpdated"],
  "fields": [
    {
      "name": "…",
      "type": "string|number|boolean|date-time|object|objectList|select|…",
      "isArray": false,
      "isReference": false,
      "targetSchema": null,
      "enumValues": [ ],
      "display": "…",
      "comment": "…",
      "tooltip": "…",
      "format": "date|date-time|…"
    }
  ]
}
Rules:
- Only include fields that materially help an agent construct/validate objects or reason about references.
- Prefer concise, high‑signal text for description and purpose.
- Do NOT populate inbound manually; leave graphRef as is.
- Include enumValues for selects where known; omit when unknown.
- For relationships.outbound: Only specify manually if you need to override automatic detection. Otherwise, ensure your schema fields have `isReference: true` and `targetSchema` set, and relationships will be auto-generated.
```

## Example (task)

```
summary: {
  description: "Scheduled unit of work tracked for users and projects.",
  purpose: "Use to plan and execute work; references status, project, members, tags.",
  labelField: "name",
  defaultSort: { field: "joeUpdated", dir: "desc" },
  searchableFields: ["name","info","description","_id"],
  allowedSorts: ["joeUpdated","created","due_date","priority","name"],
  relationships: {
    outbound: [
      { field: "status", targetSchema: "status", cardinality: "one" },
      { field: "project", targetSchema: "project", cardinality: "one" },
      { field: "members", targetSchema: "user", cardinality: "many" },
      { field: "tags", targetSchema: "tag", cardinality: "many" }
    ],
    inbound: { graphRef: "server/relationships.graph.json" }
  },
  joeManagedFields: ["created","joeUpdated"],
  fields: [
    { name: "_id", type: "string" },
    { name: "itemtype", type: "string" },
    { name: "name", type: "string" },
    { name: "status", type: "string", isReference: true, targetSchema: "status" },
    { name: "project", type: "string", isReference: true, targetSchema: "project" },
    { name: "members", type: "string", isArray: true, isReference: true, targetSchema: "user" },
    { name: "tags", type: "string", isArray: true, isReference: true, targetSchema: "tag" },
    { name: "priority", type: "number", enumValues: [1,2,3,1000] },
    { name: "joeUpdated", type: "date-time" },
    { name: "created", type: "date-time" }
  ]
}
```

## How to validate quickly

- Open: `/API/schema/<name>?summaryOnly=true`
- Check: 
  - description/purpose read well
  - labelField exists in fields
  - allowedSorts are valid
  - **relationships.outbound entries match fields with `isReference: true`** (or are intentional overrides)
  - outbound list references real schemas
  - no inbound entries besides graphRef
  - joeManagedFields present

## Notes for maintainers

- Runtime will enrich fields with display/comment/tooltip from the schema when available.
- Inbound relationships are not hand‑maintained; a graph builder will populate the shared graph later.
- For core vs instance: `source` is inferred automatically but can be overridden inside `summary` if needed.
- **Relationships are auto-generated**: Ensure your schema fields are marked with `isReference: true` and `targetSchema` in the schema definition (not just in the summary). The system will automatically build `relationships.outbound` from these fields. Only manually specify `relationships.outbound` in the summary if you need to override or add relationships not captured by field definitions.