From 2d9e2a02cc2e3644b460036a76f9aa73145fb305 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 06:30:20 +0000 Subject: [PATCH] =?UTF-8?q?docs(skills):=20objectstack-data=20factual=20sw?= =?UTF-8?q?eep=20(1/N)=20=E2=80=94=20field-types,=20relationships,=20index?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Six measured-false behavioral claims corrected against the implementation: - field-types.md: an unknown field key is REFUSED at parse (unrecognized_keys), not "silently stripped"; same for an authored vectorConfig. - field-types.md: a wrong-cased date token ({yyyy}) parses as a {field} reference and renders EMPTY — it is not emitted literally. - relationships.md: roll-up summary fields DO work over a lookup child. - relationships.md: master_detail required is forced only under sharingModel: 'controlled_by_parent'; elsewhere it is a lint warning. - indexing.md: foreign keys are NOT auto-indexed. Token ratchet: all three files shrink (-6 / -5 / -1 = -12). Part of #13675 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de --- skills/objectstack-data/rules/field-types.md | 10 +++++----- skills/objectstack-data/rules/indexing.md | 7 +++---- skills/objectstack-data/rules/relationships.md | 8 +++----- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/skills/objectstack-data/rules/field-types.md b/skills/objectstack-data/rules/field-types.md index e15796771e..a9f2967f3c 100644 --- a/skills/objectstack-data/rules/field-types.md +++ b/skills/objectstack-data/rules/field-types.md @@ -3,8 +3,8 @@ Quick reference for choosing the right field type from 49 available options. > **Config columns list only real `FieldSchema` keys.** Per-type display knobs -> beyond these do **not** exist — an unknown field key is silently stripped at -> parse (dead metadata), so don't invent config like `theme`, `rows`, or +> beyond these do **not** exist — an unknown field key is REFUSED at parse +> (`unrecognized_keys`), so don't invent `theme`, `rows`, or > `fileAttachmentConfig`. Source of truth: > `node_modules/@objectstack/spec/src/data/field.zod.ts`. @@ -343,7 +343,7 @@ The `format` is literal text interleaved with `{...}` tokens: 1. **Every `{field}` you interpolate must be `required: true`** and set before the record is created. An empty interpolated field makes the record number generation *throw* (the compile lint flags a non-existent field as an error, an optional one as a warning). 2. **Put a delimiter between adjacent variable tokens** — `{section}-{zone}{000}`, not `{section}{zone}{000}`. Without one, `('AB','C')` and `('A','BC')` both render prefix `ABC` and share a counter (to keep numbers unique). The literal separator keeps distinct groups apart. 3. **Pad width is a MINIMUM, not a cap.** `{000}` → `001`…`999`, then `1000` (it grows, never wraps). Size it for readability, not as a ceiling. -4. **Only known tokens are interpolated.** Date tokens are **case-sensitive and exact** (`{YYYY}`, not `{yyyy}` or `{YYYY-MM}`). An unrecognized `{...}` is emitted **literally** into the number — `{ YYYY }` (spaces) renders the text `{ YYYY }`. +4. **Only known tokens are interpolated.** Date tokens are **case-sensitive** — `{yyyy}` parses as a `{field}` reference and renders **empty** (rule 1 applies). Only a spelling no field could have — `{ YYYY }`, `{YYYY-MM}` — is emitted **literally**. ### Vector (AI Embeddings) @@ -354,8 +354,8 @@ The `format` is literal text interleaved with `{...}` tokens: } ``` -There is **no** `vectorConfig` block — an authored `vectorConfig` is silently -stripped (dead metadata). `dimensions` is the flat field-level key. +There is **no** `vectorConfig` block — an authored `vectorConfig` is refused at +parse. `dimensions` is the flat field-level key. ## Incorrect vs Correct diff --git a/skills/objectstack-data/rules/indexing.md b/skills/objectstack-data/rules/indexing.md index 8116cd36da..96d718239c 100644 --- a/skills/objectstack-data/rules/indexing.md +++ b/skills/objectstack-data/rules/indexing.md @@ -6,8 +6,7 @@ Guide for creating efficient database indexes in ObjectStack. ObjectStack automatically creates indexes for: - Primary keys (`id`) -- Foreign keys (lookup/master_detail fields) -- Unique constraints +- Field-level `unique` — **not** foreign keys: declare those **Only declare non-default values.** `unique` defaults to `false` — omit it when using the default. @@ -83,7 +82,7 @@ Notes an author has to know: ### ✅ Always Index -1. **Foreign keys** — Automatic, but verify +1. **Foreign keys** — declare them; never automatic 2. **Filter fields** — Columns used in WHERE clauses 3. **Sort fields** — Columns used in ORDER BY 4. **Unique constraints** — Enforce uniqueness at DB level @@ -328,7 +327,7 @@ SHOW INDEX FROM your_table; ## Best Practices -1. **Index foreign keys** — Always (automatic in ObjectStack) +1. **Index foreign keys** — always; declare each one 2. **Composite for common queries** — Combine frequently filtered columns 3. **Order matters** — Most selective field first 4. **Partial for subsets** — but build it in a migration, not a declaration diff --git a/skills/objectstack-data/rules/relationships.md b/skills/objectstack-data/rules/relationships.md index 1018426b25..e2656dda09 100644 --- a/skills/objectstack-data/rules/relationships.md +++ b/skills/objectstack-data/rules/relationships.md @@ -6,23 +6,21 @@ Guide for modeling relationships between objects using `lookup`, `master_detail` | Type | Lifecycle | Required | Sharing | Roll-ups | Use Case | |:-----|:----------|:---------|:--------|:---------|:---------| -| `lookup` | Independent | Optional by default | Independent | Not available | "Related to" | -| `master_detail` | Coupled (cascade delete) | Always required | Inherits parent | Supported via `summary` | "Owned by" | +| `lookup` | Independent | Optional by default | Independent | Supported via `summary` | "Related to" | +| `master_detail` | Coupled (cascade delete) | Forced only under `controlled_by_parent`; else lint-warned | Inherits parent | Supported via `summary` | "Owned by" | | `tree` | Self-reference | Optional | N/A | Not available | Hierarchical | ## When to Use lookup vs master_detail ### Use `lookup` When: - Child record can exist independently -- Parent deletion should not affect child (`deleteBehavior: 'set_null'`) -- No roll-up aggregations needed +- 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 -- Need roll-up summaries (count, sum, min, max, avg) - Relationship is mandatory - **Example:** `invoice_line_item.invoice_id → invoice` (line items belong to invoice)