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
12 changes: 12 additions & 0 deletions .changeset/formula-data-skill-antipatterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
---

docs(skill): add verified anti-pattern deltas to the formula and data authoring skills

Documentation-only. Both skills were already mature, so this adds only the
*verified* gaps from this season (no padding):
- objectstack-formula: only the stdlib + CEL built-ins are callable — an UNKNOWN
function now FAILS `objectstack build` (#1877), not a silent runtime no-op.
- objectstack-data: a `multiple: true` lookup is an ARRAY column (not a junction
object) — reference positionally (#1872); and on insert an omitted optional
field reads as `null` in validation predicates (#1871).
14 changes: 13 additions & 1 deletion skills/objectstack-data/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,15 +295,27 @@ See [rules/field-types.md](./rules/field-types.md) for full reference.
|:--------|:---------------|
| One-to-Many (independent) | `lookup` field on child |
| One-to-Many (owned) | `master_detail` field on child |
| Many-to-Many | Junction object with two `lookup` fields |
| Many-to-Many (simple) | multi-value `lookup` (`multiple: true`) — an **array column** of ids |
| Many-to-Many (with attributes) | Junction object with two `lookup` fields |
| Hierarchical | `tree` field (self-reference) |

See [rules/relationships.md](./rules/relationships.md) for detailed examples.

> **`multiple: true` lookup ≠ junction object.** A multi-value lookup
> (`{ type: 'lookup', reference: 'x', multiple: true }`) is stored and read as an
> **array of ids** on the record — reference elements positionally
> (`{record.tags.0}` in flow values). It is NOT a junction table. Reach for a
> **junction object** (two lookups) only when the relationship itself carries
> attributes (role, added_at, …). (#1872)

### Validation Patterns

**⚠️ Script validation is inverted:** Validation **fails** when expression is `true`.

> On **insert**, an optional field omitted from the payload reads as `null` in a
> validation predicate — so `record.due_date == null` matches an omitted field the
> same as an explicit `null` (#1871). (On update, the prior record supplies it.)

Common validation types:
- `script` — Formula expression (inverted logic)
- `unique` — Composite uniqueness
Expand Down
7 changes: 7 additions & 0 deletions skills/objectstack-formula/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,13 @@ Registered automatically. Source:
If you need a helper that doesn't exist, prefer adding it to the stdlib
(small, pure, dependency-free) over inlining a complex CEL expression.

> **Only the stdlib above + CEL built-ins (`has`, `size`, `contains`,
> `startsWith`, `endsWith`, `matches`, `min`, `max`, …) are callable.** An
> UNKNOWN function — `PRIOR()`, a legacy `ISBLANK()`, a typo'd `isBlnk()` — now
> **fails `objectstack build`** with a "no matching overload" type error (#1877),
> rather than silently no-op'ing the predicate at run time. Use `previous.x`
> (not `PRIOR()`), `isBlank()` (not `ISBLANK()`).

---

## Mandatory patterns for AI emission
Expand Down