Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion skills/objectstack-data/evals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ JSON fixtures in the objectui shape (`skill_name`, `evals[]`: `prompt`,

- `objects-fields-relationships.json` — naming, field types (`secret` vs
`password`), relationships (junction vs multi-value lookup,
`deleteBehavior`), validation (`state_machine`, unique **index**),
`deleteBehavior`, on a `user` field too), validation (`state_machine`, unique **index**),
conditional field rules.
- `hooks-security-seeds-datasources.json` — search mirrors via hooks,
permission-set scopes, external datasources, seeds (`externalId`,
Expand Down
19 changes: 19 additions & 0 deletions skills/objectstack-data/evals/objects-fields-relationships.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,25 @@
"name: 'crm_account'"
]
}
},
{
"id": 6,
"prompt": "On `ticket`, `owner` is the agent who holds it and must always be set; deleting that agent's user account must be blocked while they still hold tickets. `watchers` is several users, and a deleted user should just drop out of the list.",
"expected_output": "Both are `type: 'user'` fields, each with an authored `deleteBehavior` — a `user` field joins the referential delete walk only when it declares one. `owner`: `required: true` with `deleteBehavior: 'restrict'` (deleting a held user is refused with `409 DELETE_RESTRICTED`; `set_null` on a required field would refuse too). `watchers`: `multiple: true` with `deleteBehavior: 'set_null'` (only the deleted member is removed; an emptied set is `[]`). Neither is a `lookup` to a `user` object, which does not exist, and a `user` field with no `deleteBehavior` keeps the deleted user's id.",
"files": [],
"assertions": {
"must_contain": [
"type: 'user'",
"deleteBehavior: 'restrict'",
"deleteBehavior: 'set_null'",
"multiple: true",
"required: true"
],
"must_not_contain": [
"reference: 'user'",
"deleteBehavior: 'cascade'"
]
}
}
]
}
16 changes: 1 addition & 15 deletions skills/objectstack-data/rules/field-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ options: [
| `lookup` | Reference another object (independent) | `reference`, `lookupFilters`, `multiple`, `deleteBehavior`, `lookupAutofill` |
| `master_detail` | Parent–child with lifecycle control | `reference`, `deleteBehavior` (`cascade`/`restrict` — `set_null` is refused), `lookupAutofill` |
| `tree` | Hierarchical self-reference | `reference` — optional; when written, this object's own name (any other object is refused at parse) |
| `user` | Person picker — a lookup specialized to `sys_user` (assignee, watchers). Stored identically to `lookup` | `multiple` (collaborators), `defaultValue: 'current_user'` |
| `user` | Person picker — a lookup specialized to `sys_user` (assignee, watchers). Stored identically to `lookup` | `multiple` (collaborators), `defaultValue: 'current_user'`, `deleteBehavior` (no default — declared, deleting the user acts as on `lookup`: `cascade` deletes the holders, `set_null` clears the field or drops that member, `restrict` refuses with `409 DELETE_RESTRICTED`; undeclared, the field keeps the deleted user's id) |

**`lookupAutofill: [{ target, source, overwrite? }]`** (single-value `lookup` /
`master_detail`): a pick copies the picked record's `source` into this object's
Expand Down Expand Up @@ -203,20 +203,6 @@ const total = {
};
```

### Summary (Roll-up)

```typescript
{
type: 'summary',
summaryOperations: {
object: 'invoice_line_item', // child object to aggregate
field: 'amount', // child field
function: 'sum', // 'count' | 'sum' | 'min' | 'max' | 'avg'
// relationshipField / filter — optional (see rules/relationships.md)
},
}
```

### Autonumber

```typescript
Expand Down
24 changes: 9 additions & 15 deletions skills/objectstack-data/rules/relationships.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Relationship Patterns

- [Relationship Types](#relationship-types) · [When to Use lookup vs master_detail](#when-to-use-lookup-vs-master_detail)
- [Relationship Types](#relationship-types)
- [Patterns](#patterns) — [lookup](#one-to-many-lookup) · [master_detail](#one-to-many-master_detail) · [Junction Object](#many-to-many-junction-object) · [tree](#hierarchical-tree-self-reference)
- [Delete Behaviors](#delete-behaviors) · [Roll-up Summaries](#roll-up-summaries)
- [Inline Editing (Master-Detail Entry)](#inline-editing-master-detail-entry) · [Detail-page related lists](#detail-page-related-lists-the-read-side-mirror)
Expand All @@ -16,20 +16,6 @@ Guide for modeling relationships between objects using `lookup`, `master_detail`
| `master_detail` | Coupled (cascade delete) | Forced only under `controlled_by_parent`; else lint-warned | Inherits parent; owner opt-in `ownerMayEdit` (below) | Supported via `summary` | "Owned by" |
| `tree` | Self-reference | Optional — and so is `reference`, which when written must name this object itself; any other object is refused at parse, not lint-warned | N/A | Not available | Hierarchical |

## When to Use lookup vs master_detail

### Use `lookup` When:
- Child record can exist independently
- Parent deletion should only clear the FK (`deleteBehavior: 'set_null'`, the default)
- Relationship is optional
- **Example:** `task.assigned_to → user` (task can exist without assignment)

### Use `master_detail` When:
- Child record is meaningless without parent
- Parent deletion should cascade to children
- Relationship is mandatory
- **Example:** `invoice_line_item.invoice_id → invoice` (line items belong to invoice)

## Patterns

### One-to-Many: lookup
Expand Down Expand Up @@ -186,6 +172,14 @@ an authored `set_null` is refused at publish (it would orphan the detail row).
}
```

The walk also reads `lookup` and `tree` (`set_null` by default — the field is
cleared; a `multiple` field loses only that member — or an authored `cascade` /
`restrict`), and a `user` field only when it declares a value: deleting the user
then cascades, clears or refuses exactly as a `lookup` does; undeclared, the
field keeps the deleted user's id. `restrict`, and `set_null` on a `required`
field (a `multiple` one only when the removal would empty it), refuse the delete
with `409 DELETE_RESTRICTED`.

## Roll-up Summaries

A parent `summary` field aggregates a child collection. The engine recomputes it
Expand Down
Loading