Skip to content

Commit 8a3e5ca

Browse files
committed
docs(site): drop numeric stats; first round of persona-review fixes
Per review feedback, remove count-style statistics from module overviews and landing (field type counts, hook event counts, namespace tallies) in favor of qualitative descriptions — counts drift from the implementation and don't help readers. Also lands the first fixes from the reader-persona review pass: - ai/actions-as-tools: remove walkthrough of HITL demo tests that do not exist in examples/app-todo (only mcp-actions/seed tests exist); point at the real delete_completed danger action instead - ai/agents, ai/natural-language-queries: fix orphaned 'see above' references and same-page anchors that now live on sibling pages Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018r9mjpF7jr9BKdzEmUE4uZ
1 parent b1ec299 commit 8a3e5ca

12 files changed

Lines changed: 77 additions & 167 deletions

File tree

content/docs/ai/actions-as-tools.mdx

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -116,21 +116,13 @@ The queue is exposed via four REST endpoints (`GET`, `GET/:id`, `POST/:id/approv
116116

117117
### End-to-end example
118118

119-
Two runnable demos live in `examples/app-todo/test/`:
120-
121-
- **`ai-hitl.test.ts`** — drives the tool registry directly (no LLM dependency). Asserts the full lifecycle: `variant:'danger'` action registered as a tool → invocation returns `pending_approval` without executing → row persisted → `approvePendingAction(id, actor)` re-runs the handler → row transitions to `executed`. Reject path is also covered.
122-
123-
```bash
124-
pnpm --filter @example/app-todo test:hitl
125-
```
126-
127-
- **`ai-hitl-llm.test.ts`** — same scenario but with a real model behind Vercel AI Gateway. The LLM is given the auto-generated tool description and asked to "delete all completed tasks"; the framework returns `pending_approval` so the model summarises the wait instead of retrying. After we call approve from the test, the deletion completes.
128-
129-
```bash
130-
AI_GATEWAY_API_KEY=vck_... pnpm --filter @example/app-todo test:hitl:llm
131-
```
132-
133-
Gated on `AI_GATEWAY_API_KEY` (or `OPENAI_API_KEY`); exits 0 with a notice if no key is provided, so it can be wired into CI without leaking spend.
119+
The full lifecycle: a `variant:'danger'` action registered as a tool →
120+
invocation returns `pending_approval` without executing → row persisted →
121+
`approvePendingAction(id, actor)` re-runs the handler → row transitions to
122+
`executed` (the reject path mirrors it). The todo example's
123+
`delete_completed` action (`examples/app-todo/src/actions/task.actions.ts`)
124+
is authored exactly for this: `variant: 'danger'` + `confirmText` route it
125+
through the approval queue.
134126

135127
A trimmed view of the integration path:
136128

content/docs/ai/agents.mdx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ is in — the user never picks from a roster:
1818

1919
Both agents are part of the **cloud / Enterprise** in-UI AI runtime (cloud ADR-0025).
2020
The **open edition** ships neither — it uses `@objectstack/mcp` (BYO-AI) for data
21-
query and source-mode authoring instead (see the note above).
21+
query and source-mode authoring instead (see the callout in the
22+
[AI Overview](/docs/ai)).
2223

2324
Within the cloud / EE runtime there is no per-turn intent classifier and no agent
2425
dropdown: the surface binds the agent (data console → `ask`, Studio → `build`). A
@@ -64,7 +65,7 @@ them via the chat endpoint.
6465
<Callout type="info">
6566
Agent tools are **references** to existing Actions, Flows, or queries — you do
6667
not define ad-hoc tool names with inline parameter schemas here. See
67-
[Actions as Tools](#actions-as-tools-explicit-opt-in) for how an Action becomes
68+
[Actions as Tools](/docs/ai/actions-as-tools) for how an Action becomes
6869
LLM-callable.
6970
</Callout>
7071

@@ -219,7 +220,7 @@ Use the data tools to query records and aggregate metrics.`,
219220
},
220221

221222
// Built-in data tools (query_records / get_record / aggregate_data) are
222-
// available to agents automatically once registered — see "Actions as Tools".
223+
// available to agents automatically once registered — see "Natural Language Queries".
223224
tools: [
224225
{ type: 'flow', name: 'analyze_pipeline', description: 'Analyze sales pipeline health' },
225226
],

content/docs/ai/natural-language-queries.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ description: How agents query live data through the built-in data tools (query_r
77

88
Part of the [AI module](/docs/ai) — how natural-language questions become ObjectQL queries at runtime.
99

10+
The data tools described here ship with `@objectstack/service-ai`, i.e. the
11+
**cloud / Enterprise** in-UI AI runtime (see the callout in the
12+
[AI Overview](/docs/ai)). On the open edition, point your own AI at the same
13+
objects and queries through `@objectstack/mcp` instead.
14+
1015
Agents query your data through the built-in **data tools**
1116
`query_records`, `get_record`, and `aggregate_data` — which the LLM calls with
1217
structured arguments. These run as ordinary [ObjectQL](/docs/protocol/objectql) queries over

content/docs/automation/index.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Automation
3-
description: 18 hook events, 20+ extensible flow node types, scheduled flows, approvals, and durable webhooks — the process engine that reacts to your data.
3+
description: Hooks, flows, workflows, approvals, scheduled jobs, and durable webhooks — the process engine that reacts to your data.
44
---
55

66
# Automation
@@ -27,8 +27,8 @@ export const OpportunityStageHook: Hook = {
2727

2828
## The building blocks
2929

30-
- **Hooks** intercept **18 lifecycle events** before/after each of find, findOne, count, aggregate, insert, update, delete, updateMany, deleteMany. Hooks support priority ordering, fire-and-forget `async` mode, CEL `condition` guards, and a `retryPolicy` with backoff ([Hooks](/docs/automation/hooks)).
31-
- **Flows** are node-based processes with **20 built-in node types** (decision, loop, create/update/delete record, http, notify, script, screen, wait, subflow, parallel/join gateways, …) — and the set is **registry-extensible**: plugins can contribute new node types via `registerNodeExecutor`. Flows launch on record changes, on a schedule, from a screen, or via API ([Flows](/docs/automation/flows)).
30+
- **Hooks** intercept every record operation with before/after events — reads, writes, deletes, and their bulk variants. Hooks support priority ordering, fire-and-forget `async` mode, CEL `condition` guards, and a `retryPolicy` with backoff ([Hooks](/docs/automation/hooks)).
31+
- **Flows** are node-based processes built from nodes like decision, loop, record operations, http, notify, script, screen, wait, subflow, and parallel/join gateways — and the set is **registry-extensible**: plugins can contribute new node types via `registerNodeExecutor`. Flows launch on record changes, on a schedule, from a screen, or via API ([Flows](/docs/automation/flows)).
3232
- **Workflows** model a record's lifecycle as a **finite state machine**: states, transitions, and guards ([Workflows](/docs/automation/workflows)).
3333
- **Approvals** are flow nodes with approver resolution, approve/reject decisions, and escalation ([Approvals](/docs/automation/approvals)).
3434
- **Webhooks** deliver events to external systems through a **durable outbox** — exponential/linear/fixed retry with dead-lettering, HMAC signing, and an admin redeliver endpoint ([Webhook Delivery](/docs/automation/webhooks)).
@@ -39,7 +39,7 @@ Rule of thumb: model *state* with workflows, model *steps* with flows, use hooks
3939
## What's in this module
4040

4141
<Cards>
42-
<Card href="/docs/automation/hooks" title="Hooks" description="18 before/after events on record operations" />
42+
<Card href="/docs/automation/hooks" title="Hooks" description="Before/after events on record operations" />
4343
<Card href="/docs/automation/hook-bodies" title="Hook & Action Bodies (L1/L2)" description="The two authoring levels for hook logic" />
4444
<Card href="/docs/automation/flows" title="Flows" description="Node-based automation, screen flows, scheduled flows" />
4545
<Card href="/docs/automation/workflows" title="Workflows" description="State-machine lifecycle modeling" />

content/docs/data-modeling/field-types.mdx

Lines changed: 44 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
---
22
title: Field Type Gallery
3-
description: Complete reference for all 48 ObjectStack field types with per-type configuration properties
3+
description: Complete reference for every ObjectStack field type with per-type configuration properties
44
---
55

66
# Field Type Gallery
77

8-
ObjectStack provides **49 field types** covering every data modeling need — from basic text and numbers to AI vectors and rich media. This guide organizes them by category with per-type configuration details.
8+
ObjectStack provides a **comprehensive set of field types** covering every data modeling need — from basic text and numbers to AI vectors and rich media. This guide organizes them by category with per-type configuration details.
99

1010
<Callout type="info">
1111
**Source:** `packages/spec/src/data/field.zod.ts`
@@ -24,7 +24,6 @@ Single-line plain text input.
2424
| `maxLength` | `number` || Maximum character length |
2525
| `minLength` | `number` || Minimum character length |
2626
| `format` | `string` || Validation format pattern |
27-
| `caseSensitive` | `boolean` || Whether text comparisons are case-sensitive |
2827

2928
```typescript
3029
{ name: 'first_name', label: 'First Name', type: 'text', maxLength: 100 }
@@ -303,10 +302,19 @@ Reference to a record in another object (foreign key).
303302
|:---|:---|:---|:---|
304303
| `reference` | `string` | **required** | Target object name (snake_case) |
305304
| `referenceFilters` | `string[]` || Filters applied to lookup dialogs (e.g. `"active = true"`) |
306-
| `deleteBehavior` | `'restrict' \| 'cascade' \| 'set_null'` | `'set_null'` | Behavior when referenced record is deleted |
305+
| `deleteBehavior` | `'restrict' \| 'cascade' \| 'set_null'` | `'set_null'` | Behavior when referenced record is deleted (a *required* lookup left at the default `set_null` is escalated to `restrict`, since a NOT NULL foreign key cannot be cleared) |
307306

308307
```typescript
309-
{ name: 'assigned_to', label: 'Assigned To', type: 'lookup', reference: 'user' }
308+
{ name: 'company', label: 'Company', type: 'lookup', reference: 'account' }
309+
```
310+
311+
### `user`
312+
Person picker — a lookup specialized to the built-in `sys_user` object. Stored identically to `lookup` (foreign key to `sys_user.id`; `multiple: true` stores an array) and resolved through the same `$expand` machinery. Supports `defaultValue: 'current_user'` to stamp the acting user's id on insert.
313+
314+
```typescript
315+
Field.user({ label: 'Assignee' })
316+
Field.user({ label: 'Watchers', multiple: true })
317+
Field.user({ label: 'Owner', defaultValue: 'current_user' })
310318
```
311319

312320
### `master_detail`
@@ -419,15 +427,16 @@ Audio file attachment.
419427
## Computed Types
420428

421429
### `formula`
422-
Calculated field using an expression.
430+
Calculated field using a CEL expression (see [Expressions](/docs/data-modeling/formulas)).
423431

424432
| Property | Type | Default | Description |
425433
|:---|:---|:---|:---|
426-
| `expression` | `string` | **required** | Calculation expression |
427-
| `cached` | `object` || Cache settings for computed result |
434+
| `expression` | `string \| Expression` | **required** | CEL calculation expression |
435+
| `returnType` | `'number' \| 'text' \| 'boolean' \| 'date'` || Declared value type of the computed result |
436+
| `dependencies` | `string[]` || Fields the formula depends on |
428437

429438
```typescript
430-
{ name: 'total', label: 'Total', type: 'formula', expression: 'quantity * unit_price' }
439+
{ name: 'total', label: 'Total', type: 'formula', expression: 'record.quantity * record.unit_price', returnType: 'number' }
431440
```
432441

433442
### `summary`
@@ -497,23 +506,14 @@ Name-keyed map of embedded sub-objects (`Record<string, SubObject>`). Insertion
497506
## Enhanced Types
498507
499508
### `location`
500-
Geographic coordinates with optional map display.
501-
502-
| Property | Type | Default | Description |
503-
|:---|:---|:---|:---|
504-
| `displayMap` | `boolean` | — | Show interactive map |
505-
| `allowGeocoding` | `boolean` | — | Enable address-to-coordinates conversion |
509+
Geographic coordinates. Stored as `{ latitude, longitude, altitude?, accuracy? }` (latitude −90..90, longitude −180..180). No per-type config properties.
506510
507511
```typescript
508-
{ name: 'headquarters', label: 'Location', type: 'location', displayMap: true, allowGeocoding: true }
512+
{ name: 'headquarters', label: 'Location', type: 'location' }
509513
```
510514
511515
### `address`
512-
Structured postal address.
513-
514-
| Property | Type | Default | Description |
515-
|:---|:---|:---|:---|
516-
| `addressFormat` | `'us' \| 'uk' \| 'international'` | — | Address format template |
516+
Structured postal address. Stored as `{ street, city, state, postalCode, country, countryCode, formatted }` (all parts optional). No per-type config properties.
517517
518518
```typescript
519519
{ name: 'billing_address', label: 'Billing Address', type: 'address' }
@@ -525,11 +525,9 @@ Source code with syntax highlighting.
525525
| Property | Type | Default | Description |
526526
|:---|:---|:---|:---|
527527
| `language` | `string` | — | Programming language for highlighting |
528-
| `theme` | `string` | — | Editor theme |
529-
| `lineNumbers` | `boolean` | — | Show line numbers |
530528
531529
```typescript
532-
{ name: 'snippet', label: 'Code Snippet', type: 'code', language: 'typescript', lineNumbers: true }
530+
{ name: 'snippet', label: 'Code Snippet', type: 'code', language: 'typescript' }
533531
```
534532
535533
### `json`
@@ -540,28 +538,23 @@ Raw JSON data.
540538
```
541539
542540
### `color`
543-
Color picker with format options.
544-
545-
| Property | Type | Default | Description |
546-
|:---|:---|:---|:---|
547-
| `colorFormat` | `'hex' \| 'rgb' \| 'rgba' \| 'hsl'` | — | Color value format |
548-
| `allowAlpha` | `boolean` | — | Allow alpha transparency |
549-
| `presetColors` | `string[]` | — | Preset color palette |
541+
Color picker. Stores the color value as a string. No per-type config properties.
550542
551543
```typescript
552-
{ name: 'brand_color', label: 'Brand Color', type: 'color', colorFormat: 'hex', presetColors: ['#FF0000', '#00FF00', '#0000FF'] }
544+
{ name: 'brand_color', label: 'Brand Color', type: 'color' }
553545
```
554546
555547
### `rating`
556548
Star rating input.
557549
558550
| Property | Type | Default | Description |
559551
|:---|:---|:---|:---|
560-
| `maxRating` | `number` | `5` | Maximum rating value |
561-
| `allowHalf` | `boolean` | `false` | Allow half-star ratings |
552+
| `max` | `number` | `5` | Maximum rating value (set by the `Field.rating(max)` factory) |
562553
563554
```typescript
564-
{ name: 'satisfaction', label: 'Rating', type: 'rating', maxRating: 5, allowHalf: true }
555+
{ name: 'satisfaction', label: 'Rating', type: 'rating', max: 5 }
556+
// or with the factory:
557+
// satisfaction: Field.rating(5, { label: 'Rating' })
565558
```
566559
567560
### `slider`
@@ -571,12 +564,10 @@ Range slider input.
571564
|:---|:---|:---|:---|
572565
| `min` | `number` | — | Minimum value |
573566
| `max` | `number` | — | Maximum value |
574-
| `step` | `number` | — | Step increment |
575-
| `showValue` | `boolean` | — | Display current value |
576-
| `marks` | `object` | — | Tick marks on slider |
567+
| `step` | `number` | `1` | Step increment |
577568
578569
```typescript
579-
{ name: 'confidence', label: 'Confidence', type: 'slider', min: 0, max: 100, step: 5, showValue: true }
570+
{ name: 'confidence', label: 'Confidence', type: 'slider', min: 0, max: 100, step: 5 }
580571
```
581572
582573
### `signature`
@@ -587,17 +578,10 @@ Digital signature capture.
587578
```
588579
589580
### `qrcode`
590-
QR/barcode generator and scanner.
591-
592-
| Property | Type | Default | Description |
593-
|:---|:---|:---|:---|
594-
| `barcodeFormat` | `'qr' \| 'ean13' \| 'ean8' \| 'code128' \| 'code39' \| 'upca' \| 'upce'` | — | Barcode format type |
595-
| `qrErrorCorrection` | `'L' \| 'M' \| 'Q' \| 'H'` | — | QR error correction level (only when `barcodeFormat` is `'qr'`) |
596-
| `displayValue` | `boolean` | — | Display human-readable value below barcode/QR code |
597-
| `allowScanning` | `boolean` | — | Enable camera scanning |
581+
QR/barcode value rendered as a scannable code. No per-type config properties.
598582
599583
```typescript
600-
{ name: 'asset_tag', label: 'Asset Tag', type: 'qrcode', allowScanning: true }
584+
{ name: 'asset_tag', label: 'Asset Tag', type: 'qrcode' }
601585
```
602586
603587
### `progress`
@@ -623,17 +607,14 @@ Vector embeddings for semantic search and similarity.
623607
624608
| Property | Type | Default | Description |
625609
|:---|:---|:---|:---|
626-
| `vectorConfig.dimensions` | `number` | **required** | Vector dimensionality (e.g., 1536 for OpenAI) |
627-
| `vectorConfig.distanceMetric` | `'cosine' \| 'euclidean' \| 'dotProduct' \| 'manhattan'` | `'cosine'` | Distance calculation method |
628-
| `vectorConfig.normalized` | `boolean` | `false` | Whether vectors are pre-normalized |
629-
| `vectorConfig.indexed` | `boolean` | `true` | Enable vector indexing |
630-
| `vectorConfig.indexType` | `'hnsw' \| 'ivfflat' \| 'flat'` | — | Index algorithm |
610+
| `dimensions` | `number` | **required** | Vector dimensionality (e.g., 1536 for OpenAI embeddings) |
611+
612+
The nested `vectorConfig` object (with `distanceMetric`, `indexed`, `indexType`, …) is retained for back-compat only and is a runtime no-op — set the flat `dimensions` property instead.
631613
632614
```typescript
633-
{
634-
name: 'embedding', label: 'Embedding', type: 'vector',
635-
vectorConfig: { dimensions: 1536, distanceMetric: 'cosine', indexed: true, indexType: 'hnsw' }
636-
}
615+
{ name: 'embedding', label: 'Embedding', type: 'vector', dimensions: 1536 }
616+
// or with the factory:
617+
// embedding: Field.vector(1536, { label: 'Embedding' })
637618
```
638619
639620
---
@@ -647,7 +628,7 @@ These properties are available on **all** field types:
647628
| `name` | `string` | **required** | Machine name (snake_case) |
648629
| `label` | `string` | — | Human-readable display label |
649630
| `description` | `string` | — | Field description / help text |
650-
| `type` | `FieldType` | **required** | One of the 49 field types |
631+
| `type` | `FieldType` | **required** | One of the supported field types |
651632
| `required` | `boolean` | `false` | Whether the field is required |
652633
| `unique` | `boolean` | `false` | Enforce uniqueness |
653634
| `multiple` | `boolean` | `false` | Allow array of values |
@@ -660,83 +641,16 @@ These properties are available on **all** field types:
660641
| `defaultValue` | `any` | — | Default value for new records |
661642
| `group` | `string` | — | Field grouping / section |
662643
| `inlineHelpText` | `string` | — | Inline help tooltip |
663-
| `trackFeedHistory` | `boolean` | — | Track changes in activity feed |
664-
| `encryptionConfig` | `object` | — | Field-level encryption settings |
665-
| `maskingRule` | `object` | — | Data masking configuration |
644+
| `trackHistory` | `boolean` | — | Render this field's value changes as entries on the record activity timeline |
645+
| `requiredPermissions` | `string[]` | — | Capabilities required to read/edit this field (masked on read, denied on write) |
666646
| `dependencies` | `string[]` | — | Names of fields this field depends on (formulas, visibility rules, etc.) |
667-
| `dataQuality` | `object` | — | Data quality validation rules |
668647
| `visibleWhen` | `Expression` | — | Show field only when predicate is true |
669648
| `readonlyWhen` | `Expression` | — | Make field read-only when predicate is true |
670649
| `requiredWhen` | `Expression` | — | Require field when predicate is true |
671650
| `conditionalRequired` | `Expression` | — | Deprecated alias of `requiredWhen` |
672651
673652
---
674653
675-
## Field Type Decision Tree
676-
677-
Use this guide to choose the right field type:
678-
679-
```
680-
Is it text?
681-
├── Short text (< 255 chars) → text
682-
├── Long texttextarea
683-
├── Formatted textmarkdown | html | richtext
684-
├── Email addressemail
685-
├── URLurl
686-
├── Phone numberphone
687-
├── Login password (one-way hash) → password
688-
├── Reversible secret (API key/token) → secret
689-
└── Source codecode
690-
691-
Is it a number?
692-
├── Integer or decimalnumber
693-
├── Money amountcurrency
694-
└── Percentagepercent
695-
696-
Is it a date/time?
697-
├── Date onlydate
698-
├── Date + timedatetime
699-
└── Time onlytime
700-
701-
Is it a choice?
702-
├── Single choice (dropdown) → select
703-
├── Single choice (visible) → radio
704-
├── Multiple choices (dropdown) → multiselect
705-
└── Multiple choices (visible) → checkboxes
706-
707-
Is it a reference?
708-
├── Simple foreign keylookup
709-
├── Parent-child (cascade delete) → master_detail
710-
└── Self-referential hierarchytree
711-
712-
Is it a file?
713-
├── Any filefile
714-
├── Imageimage
715-
├── Profile pictureavatar
716-
├── Videovideo
717-
└── Audioaudio
718-
719-
Is it computed?
720-
├── Formula calculationformula
721-
├── Roll-up summarysummary
722-
└── Auto-incrementing IDautonumber
723-
724-
Is it embedded structured data (stored as JSON, no separate table)?
725-
├── Single embedded sub-objectcomposite
726-
├── Repeating embedded arrayrepeater
727-
└── Name-keyed map of sub-objectsrecord
728-
729-
Is it specialized?
730-
├── Geographic locationlocation
731-
├── Postal addressaddress
732-
├── Yes/No toggleboolean | toggle
733-
├── Star ratingrating
734-
├── Range sliderslider
735-
├── Color pickercolor
736-
├── Raw JSONjson
737-
├── Tags/Labelstags
738-
├── Progress barprogress
739-
├── Signaturesignature
740-
├── QR/Barcodeqrcode
741-
└── AI embeddingvector
742-
```
654+
## Choosing a Field Type
655+
656+
Not sure which type fits your data? Follow the [Field Type Decision Tree](/docs/data-modeling/field-type-decision-tree) — a flowchart, quick-reference tables, and a use-case mapping for every type in this gallery.

0 commit comments

Comments
 (0)