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
10 changes: 10 additions & 0 deletions .changeset/31112-data-skill-examples-checked.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@objectstack/skills': patch
---

docs(skills): more of objectstack-data's self-contained examples are type-checked by `check:skill-examples`, and the `Field.*` factory example imports from the subpath that exports it

Clause-②: no
Metadata-protocol: none

Fourteen more fenced TypeScript blocks in `skills/objectstack-data` carry the opt-in check marker, so 25 of its 95 are now compiled against the spec: 8 of 10 in `rules/indexing.md` (index lists typed as `ObjectIndex[]` from `@objectstack/spec/data`), 4 more in `references/data-hooks.md` (the full `defineHook` definition and three pattern hooks, which gain their `defineHook` import), and one each in `rules/lifecycle.md` and `rules/field-types.md`. One example was wrong: `rules/field-types.md` imported `Field` from `@objectstack/spec`, which does not export it (the spec root has no namespace exports); it now imports from `@objectstack/spec/data`, and the prose no longer names the root. The ✅ script example in `rules/validation.md` gains the `name` every validation rule requires. The rest stay unmarked: fragments, shapes, ❌ examples, and the blocks whose file had no token headroom left to pay for a wrapper. No rule's teaching changes.
11 changes: 11 additions & 0 deletions skills/objectstack-data/references/data-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Reference companion to `objectstack-data/SKILL.md`, and the catalog's anchor for
data hooks. Covers the lifecycle events, the sandboxed `body` (and the deprecated
inline `handler`), the `HookContext` API, registration, and the canonical patterns
(delete guards, audit trails, cross-object writes, read masking).
`check:skill-examples` skips the unmarked blocks: fragments, shapes, and pattern 4's bare `any`.


---
Expand Down Expand Up @@ -79,6 +80,7 @@ annotation can't catch — a non-`snake_case` `name`, a misspelled key routed
through a spread — fail while you author instead of at deploy, and the export
carries defaults already materialized.

<!-- os:check -->
```typescript
import { P } from '@objectstack/spec';
import { defineHook } from '@objectstack/spec/data';
Expand Down Expand Up @@ -736,7 +738,10 @@ additionally require the `api.read` / `api.write` capabilities.

### 1. Preventing Deletion (sandboxed `body`)

<!-- os:check -->
```typescript
import { defineHook } from '@objectstack/spec/data';

const protectStrategicAccounts = defineHook({
name: 'crm_protect_strategic_accounts',
object: 'account',
Expand All @@ -760,7 +765,10 @@ const protectStrategicAccounts = defineHook({

### 2. Creating Related Records (audit trail, sandboxed `body`)

<!-- os:check -->
```typescript
import { defineHook } from '@objectstack/spec/data';

const createAuditTrail = defineHook({
name: 'crm_audit_trail',
object: ['account', 'contact', 'opportunity'],
Expand All @@ -784,7 +792,10 @@ const createAuditTrail = defineHook({

### 3. Multi-Object Logic (sandboxed `body`)

<!-- os:check -->
```typescript
import { defineHook } from '@objectstack/spec/data';

const cascadeAccountUpdate = defineHook({
name: 'crm_cascade_account_updates',
object: 'account',
Expand Down
2 changes: 2 additions & 0 deletions skills/objectstack-data/references/examples-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ and the lock banner reads.

### Schema

A type excerpt, so `check:skill-examples` skips it:

```ts
protection?: {
/** Lock level — controls what Studio can do to this item. */
Expand Down
11 changes: 6 additions & 5 deletions skills/objectstack-data/rules/field-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@

## Two spellings: `Field.*` factory or object literal

The tables below give the **object-literal** spelling. Real apps overwhelmingly
use the equivalent **`Field.*` factory** (`@objectstack/spec`) — same schema, plus
The tables below give the **object-literal** spelling. Real apps mostly
use the equivalent **`Field.*` factory** — same schema, plus
per-helper JSDoc and inference (`Field.lookup`'s const generic is what lets
`defineSeed()` type its reference values). A helper exists for every type below
except a few authored as literals (`tags`, `progress`, `repeater`, `vector`).

<!-- os:check -->
```typescript
import { Field } from '@objectstack/spec';
import { Field } from '@objectstack/spec/data';

fields: {
const fields = {
name: Field.text({ required: true, maxLength: 255 }),
stage: Field.select(['new', 'won'], { required: true }), // options 1st
account_id: Field.lookup('account', { required: true }), // reference 1st
}
};
```

> **Config columns list only real `FieldSchema` keys.** Per-type display knobs
Expand Down
58 changes: 41 additions & 17 deletions skills/objectstack-data/rules/indexing.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Index Strategy

Guide for creating efficient database indexes in ObjectStack.
The two blocks holding ❌ keys are left unmarked: `check:skill-examples` would refuse them.

## Default Behavior

Expand Down Expand Up @@ -34,13 +34,16 @@ and it is the whole surface *because* it is all the driver materializes:

## Syntax

<!-- os:check -->
```typescript
indexes: [
import type { ObjectIndex } from '@objectstack/spec/data';

const indexes: ObjectIndex[] = [
{ fields: ['status', 'created_at'] }, // plain composite
{ fields: ['email'], unique: 'organization' }, // unique per org
{ fields: ['hostname'], unique: 'global' }, // unique platform-wide
{ name: 'idx_acct_status', fields: ['status'] }, // custom name
]
];
```

## Unique scope — always state it (ADR-0120)
Expand Down Expand Up @@ -83,22 +86,28 @@ Notes an author has to know:

### Composite Index (Multi-Column)

<!-- os:check -->
```typescript
indexes: [
import type { ObjectIndex } from '@objectstack/spec/data';

const indexes: ObjectIndex[] = [
// Most specific first (status), then sort key
{ fields: ['status', 'created_at'] },

// Can satisfy queries like:
// - WHERE status = 'active'
// - WHERE status = 'active' ORDER BY created_at DESC
// - WHERE status = 'active' AND created_at > '2026-01-01'
]
];
```

### Unique Index

<!-- os:check -->
```typescript
indexes: [
import type { ObjectIndex } from '@objectstack/spec/data';

const indexes: ObjectIndex[] = [
// Single column, one holder per organization
{ fields: ['email'], unique: 'organization' },

Expand All @@ -108,7 +117,7 @@ indexes: [

// Single column, one holder across the whole installation
{ fields: ['hostname'], unique: 'global' },
]
];
```

## Incorrect vs Correct
Expand All @@ -125,12 +134,15 @@ indexes: [

### ✅ Correct — Declare Only What the Driver Materializes

<!-- os:check -->
```typescript
indexes: [
import type { ObjectIndex } from '@objectstack/spec/data';

const indexes: ObjectIndex[] = [
{ fields: ['status'] }, // ✅ unique: false is the default
{ fields: ['email'], unique: 'organization' }, // ✅ the scope is required
{ fields: ['description', 'notes'] }, // ✅ plain index; see below for full-text
]
];
```

### Composite index order
Expand Down Expand Up @@ -184,11 +196,14 @@ uniqueness.

ObjectStack auto-generates index names. To specify custom names:

<!-- os:check -->
```typescript
{
import type { ObjectIndex } from '@objectstack/spec/data';

const index: ObjectIndex = {
name: 'idx_account_status_created', // Custom name
fields: ['status', 'created_at'],
}
};
```

**Auto-generated pattern:** `idx_{object}_{field1}_{field2}_{...}`
Expand All @@ -197,11 +212,14 @@ ObjectStack auto-generates index names. To specify custom names:

### Filter by Status + Sort by Date

<!-- os:check -->
```typescript
import type { ObjectIndex } from '@objectstack/spec/data';

// Query: WHERE status = 'active' ORDER BY created_at DESC LIMIT 50
indexes: [
const indexes: ObjectIndex[] = [
{ fields: ['status', 'created_at'] },
]
];
```

### Multi-Tenant Queries
Expand All @@ -212,11 +230,14 @@ multi-tenant host the platform adds its single-column index. A non-unique
composite may lead with it; a unique one says `unique: 'organization'` instead
(above):

<!-- os:check -->
```typescript
import type { ObjectIndex } from '@objectstack/spec/data';

// Query: WHERE organization_id = X AND ...
indexes: [
const indexes: ObjectIndex[] = [
{ fields: ['organization_id', 'status', 'created_at'] },
]
];
```

### Text Search / Containment / Geospatial
Expand All @@ -226,11 +247,14 @@ is **not** declarable — see "Access methods and partial indexes" above. Declar
the plain index if the column is also filtered or sorted normally, and create
the specialised one from a database-layer migration on the dialect you run.

<!-- os:check -->
```typescript
import type { ObjectIndex } from '@objectstack/spec/data';

// Declaration: plain, portable, and all the driver can build
indexes: [
const indexes: ObjectIndex[] = [
{ fields: ['description'] }, // text search: add a fulltext/GIN index in a migration
{ fields: ['tags'] }, // containment (tags @> [...]): GIN, in a migration
{ fields: ['location'] }, // ST_DWithin(...): GIST, in a migration
]
];
```
3 changes: 2 additions & 1 deletion skills/objectstack-data/rules/lifecycle.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Data Lifecycle & Retention

Guide for declaring how long an object's data lives and how its space is
Declaring how long an object's data lives and how its space is
reclaimed (ADR-0057). Distinct from
**lifecycle hooks** (`beforeInsert` / `afterUpdate` …), which run logic on
writes: this page covers the rows' retention, rotation and archival.
Expand Down Expand Up @@ -28,6 +28,7 @@ expired rows under a system context, and reclaims driver space.

## Syntax

<!-- os:check -->
```typescript
import { ObjectSchema, Field } from '@objectstack/spec/data';

Expand Down
8 changes: 5 additions & 3 deletions skills/objectstack-data/rules/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- [External / Custom Validation → Lifecycle Hooks](#external--custom-validation--lifecycle-hooks) · [Validation Properties](#validation-properties)
- [Incorrect vs Correct](#incorrect-vs-correct)

Unmarked blocks are fragments; `check:skill-examples` skips them.

## Available Rule Types

The **complete** set of `type` discriminators accepted by `ValidationRuleSchema`:
Expand Down Expand Up @@ -85,7 +87,7 @@ condition: P`record.start_date != null && record.end_date != null && record.end_

## Uniqueness — Use Unique Indexes

There is **no `unique` validation type**. Uniqueness — including composite
Uniqueness — including composite
uniqueness — is declared as a unique **index** on the object:

```typescript
Expand Down Expand Up @@ -149,8 +151,7 @@ validations: [

## Cross-Field Validation

Same inverted semantics as `script` — the predicate is the **failure**
condition. `fields` lists the fields involved (used for error targeting).
`fields` lists the fields involved (used for error targeting).

```typescript
validations: [
Expand Down Expand Up @@ -321,6 +322,7 @@ priority: 1000 // User validations (run last)

```typescript
{
name: 'no_draft_publish',
type: 'script',
condition: P`record.status == 'draft'`,
message: 'Cannot publish draft records',
Expand Down
Loading