Skip to content

Commit 9ff4b9a

Browse files
os-zhuangclaude
andauthored
docs(skill): verified anti-pattern deltas for formula + data authoring skills (#1922)
Continues the upstream "teach the AI the right pattern" work for the other two authoring skills. Both were already strong (formula already documents the #1491 brace-in-CEL mistake + dialects + isBlank; data already warns about inverted script validation), so this adds ONLY verified deltas from this season — no fabricated/padded anti-patterns: - objectstack-formula: only the stdlib + CEL built-ins are callable; an unknown function (PRIOR(), legacy ISBLANK(), a typo) now FAILS objectstack build ("no matching overload"), not a silent runtime no-op (#1877). - objectstack-data: `multiple: true` lookup is an ARRAY column ≠ junction object — reference positionally `{record.tags.0}` (#1872); on insert an omitted optional field reads as null in validation predicates (#1871). Docs-only; check:skill-docs passes (frontmatter-derived docs unaffected); empty changeset. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3efadb5 commit 9ff4b9a

3 files changed

Lines changed: 32 additions & 1 deletion

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
---
3+
4+
docs(skill): add verified anti-pattern deltas to the formula and data authoring skills
5+
6+
Documentation-only. Both skills were already mature, so this adds only the
7+
*verified* gaps from this season (no padding):
8+
- objectstack-formula: only the stdlib + CEL built-ins are callable — an UNKNOWN
9+
function now FAILS `objectstack build` (#1877), not a silent runtime no-op.
10+
- objectstack-data: a `multiple: true` lookup is an ARRAY column (not a junction
11+
object) — reference positionally (#1872); and on insert an omitted optional
12+
field reads as `null` in validation predicates (#1871).

skills/objectstack-data/SKILL.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,15 +295,27 @@ See [rules/field-types.md](./rules/field-types.md) for full reference.
295295
|:--------|:---------------|
296296
| One-to-Many (independent) | `lookup` field on child |
297297
| One-to-Many (owned) | `master_detail` field on child |
298-
| Many-to-Many | Junction object with two `lookup` fields |
298+
| Many-to-Many (simple) | multi-value `lookup` (`multiple: true`) — an **array column** of ids |
299+
| Many-to-Many (with attributes) | Junction object with two `lookup` fields |
299300
| Hierarchical | `tree` field (self-reference) |
300301

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

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

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

315+
> On **insert**, an optional field omitted from the payload reads as `null` in a
316+
> validation predicate — so `record.due_date == null` matches an omitted field the
317+
> same as an explicit `null` (#1871). (On update, the prior record supplies it.)
318+
307319
Common validation types:
308320
- `script` — Formula expression (inverted logic)
309321
- `unique` — Composite uniqueness

skills/objectstack-formula/SKILL.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,13 @@ Registered automatically. Source:
138138
If you need a helper that doesn't exist, prefer adding it to the stdlib
139139
(small, pure, dependency-free) over inlining a complex CEL expression.
140140

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

143150
## Mandatory patterns for AI emission

0 commit comments

Comments
 (0)