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
47 changes: 47 additions & 0 deletions .changeset/prose-example-gate-covers-docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
"@objectstack/spec": patch
---

feat(spec): the example type-check gate now covers `content/docs`, not just `skills/`

`check:skill-examples` compiles the TypeScript in prose against the built spec, so
an example that stops compiling fails CI instead of quietly teaching code that no
longer works. It does its job — it caught the broken `defineTool` example when
`tool.requiresConfirmation` was removed (#3715).

But it only ever walked `skills/`:

| Tree | files with `ts` blocks | compiled |
|---|---|---|
| `skills/` | 9 | **9** |
| `content/docs/` (hand-written) | 124 | **0** |

The identical break in a docs page would have shipped. Docs examples are copied
verbatim by humans and AI exactly like skill examples, so a gate covering a
fraction of the surface it appears to cover reads as coverage — the same shape as
the stale-evidence and orphan-proof warnings fixed in #3857 / #3868.

The walker is now a `SOURCE_ROOTS` list, and this lands the first batch: **164
docs blocks across 63 pages, taking the gate from 32 to 196 checked examples**.
`content/docs/references/` is excluded — `build-docs.ts` regenerates it from the
schemas, so it cannot drift independently of its source.

**The marker is now per-format.** MDX has no HTML comments: `<!-- os:check -->` in
a `.mdx` fails the fumadocs build outright — *"Unexpected character `!`… to create
a comment in MDX, use `{/* text */}`"*. Caught by building the docs site, after
the first attempt broke 60+ pages. `skills/**/*.md` keeps `<!-- os:check -->`;
`content/docs/**/*.mdx` uses `{/* os:check */}`. Both spellings are recognised for
**orphan** detection, so a wrong-format marker fails loudly rather than silently
checking nothing — the existing guard's philosophy, extended to the new failure
mode this change introduces.

The batch was measured rather than guessed: marking all 780 docs blocks and
compiling showed which are self-contained. One subtlety worth recording — a block
that "passes" inside a 780-file program can be leaning on globals declared by
*other* blocks (a file with no import/export is a global script), so the set was
converged by recompiling and dropping newly-failing blocks until green. 164 stand
on their own.

The remaining blocks are mostly fragments (a `columns: [...]` subtree), which the
gate's opt-in design already anticipates. Whether any are genuine rot is worth a
follow-up now that the machinery reaches them.
1 change: 1 addition & 0 deletions content/docs/ai/actions-as-tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Add an optional `ai:` block to give the model a precise, LLM-facing description
spec; `list_actions` surfaces `ai.description` to the model, falling back to the
UI `label` when it is absent.

{/* os:check */}
```typescript
export const triageCaseAction = {
name: 'triage_case',
Expand Down
1 change: 1 addition & 0 deletions content/docs/ai/agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ point at, and reach it through `@objectstack/mcp` (BYO-AI).

## Sales Assistant Agent

{/* os:check */}
```typescript
import { defineAgent } from '@objectstack/spec/ai';

Expand Down
1 change: 1 addition & 0 deletions content/docs/api/environment-routing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ depending on request context.

Enable scoped route registration in `objectstack.config.ts`:

{/* os:check */}
```typescript
import { defineStack } from '@objectstack/spec';

Expand Down
2 changes: 2 additions & 0 deletions content/docs/api/error-handling-client.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ This guide covers best practices for handling ObjectStack API errors in client a

Every error response from ObjectStack follows this shape:

{/* os:check */}
```typescript
interface ErrorResponse {
success: false; // Always false for error responses
Expand Down Expand Up @@ -56,6 +57,7 @@ Error `code` values are lowercase snake_case (`validation_error`, `permission_de

Create a typed error parser to extract structured information from API responses:

{/* os:check */}
```typescript
import type { ErrorResponse } from '@objectstack/spec/api';

Expand Down
2 changes: 2 additions & 0 deletions content/docs/api/error-handling-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ lowercase snake_case members of `StandardErrorCode` (see
`@objectstack/spec/api`), e.g. `validation_error`, `resource_conflict`,
`permission_denied`.

{/* os:check */}
```typescript
import type { ErrorResponse, StandardErrorCode } from '@objectstack/spec/api';

Expand Down Expand Up @@ -289,6 +290,7 @@ export const EnrichTaskWithAI: Hook = {

Prevent cascading failures when an external service is down:

{/* os:check */}
```typescript
class CircuitBreaker {
private failures = 0;
Expand Down
3 changes: 3 additions & 0 deletions content/docs/automation/flows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ A **Flow** is a visual automation that orchestrates business logic through conne

## Basic Structure

{/* os:check */}
```typescript
const approvalFlow = {
name: 'order_approval',
Expand Down Expand Up @@ -564,6 +565,7 @@ Edges connect nodes and define the execution path:

Flows use variables to pass data between nodes and to/from callers:

{/* os:check */}
```typescript
variables: [
{ name: 'input_id', type: 'text', isInput: true, isOutput: false },
Expand Down Expand Up @@ -738,6 +740,7 @@ is retired; model the same logic as a Flow.

### Record-change flow

{/* os:check */}
```typescript
import type { Flow } from '@objectstack/spec/automation';

Expand Down
1 change: 1 addition & 0 deletions content/docs/automation/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Automation is ObjectStack's process engine: you attach business logic to the dat

The smallest useful automation is a hook (from the CRM example app):

{/* os:check */}
```typescript
import type { Hook, HookContext } from '@objectstack/spec/data';

Expand Down
2 changes: 2 additions & 0 deletions content/docs/automation/workflows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This page keeps the historical route but documents the current split.
Use Flow when something should happen after a record event, schedule, button, or
subflow call:

{/* os:check */}
```typescript
import type { Flow } from '@objectstack/spec/automation';

Expand Down Expand Up @@ -63,6 +64,7 @@ registration/runtime.
Use `StateMachineSchema` when the core requirement is "this object can only move
through these states by these events."

{/* os:check */}
```typescript
import type { StateMachineConfig } from '@objectstack/spec/automation';

Expand Down
10 changes: 10 additions & 0 deletions content/docs/concepts/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ description — data (objects), automation (flows), access (permissions), and in

### Example: Defining a Customer Object

{/* os:check */}
```typescript
// src/objects/customer.object.ts
import { ObjectSchema, Field } from '@objectstack/spec/data';
Expand Down Expand Up @@ -148,6 +149,7 @@ That's the job of the other layers.

### Example: Permission Rules

{/* os:check */}
```typescript
// src/permissions/sales_rep.permission.ts
import { definePermissionSet } from '@objectstack/spec';
Expand Down Expand Up @@ -175,6 +177,7 @@ export const SalesRepPermission = definePermissionSet({

### Example: Workflow Automation

{/* os:check */}
```typescript
// src/flows/high_value_customer.flow.ts
import { defineFlow } from '@objectstack/spec';
Expand Down Expand Up @@ -221,6 +224,7 @@ Kernel **orchestrates** these rules at runtime, independent of the data structur

### Example: List View

{/* os:check */}
```typescript
// src/views/customer.view.ts
import { defineView } from '@objectstack/spec';
Expand All @@ -245,6 +249,7 @@ export const CustomerView = defineView({

### Example: Form View

{/* os:check */}
```typescript
// src/views/customer.view.ts
import { defineView } from '@objectstack/spec';
Expand Down Expand Up @@ -300,6 +305,7 @@ User clicks "Save"

### Step 2: UI Layer Sends Request

{/* os:check */}
```typescript
// ObjectUI dispatches an action to Kernel
const request = {
Expand Down Expand Up @@ -375,6 +381,7 @@ for (const workflow of workflows) {

### Step 7: ObjectUI Updates Display

{/* os:check */}
```typescript
// Kernel returns success response
// UI optimistically updates the screen
Expand All @@ -388,6 +395,7 @@ Here's how all three protocols collaborate for a **Kanban Board** feature:

### 1. ObjectQL: Define the Data

{/* os:check */}
```typescript
import { ObjectSchema, Field } from '@objectstack/spec/data';

Expand Down Expand Up @@ -426,6 +434,7 @@ export const Opportunity = ObjectSchema.create({

### 2. Kernel: Define Business Rules

{/* os:check */}
```typescript
import { defineFlow } from '@objectstack/spec';

Expand All @@ -450,6 +459,7 @@ export const OpportunityWonFlow = defineFlow({

### 3. ObjectUI: Define the Kanban View

{/* os:check */}
```typescript
import { defineView } from '@objectstack/spec';

Expand Down
2 changes: 2 additions & 0 deletions content/docs/concepts/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ ObjectStack centralizes the "Intent" into a **single Protocol Definition**:

<Tabs items={['Array Format', 'Map Format']}>
<Tab value="Array Format">
{/* os:check */}
```typescript
// ONE definition — everything else derives from it
import { defineStack } from '@objectstack/spec';
Expand All @@ -57,6 +58,7 @@ export default defineStack({
```
</Tab>
<Tab value="Map Format">
{/* os:check */}
```typescript
// Map keys become the `name` field automatically
import { defineStack } from '@objectstack/spec';
Expand Down
5 changes: 5 additions & 0 deletions content/docs/concepts/metadata-driven.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ ObjectStack centralizes the "Intent" into a **single TypeScript-authored, Zod-va

### Example: The ObjectStack Way

{/* os:check */}
```typescript
// ONE definition (in objectstack.config.ts)
import { ObjectSchema, Field } from '@objectstack/spec/data';
Expand Down Expand Up @@ -112,6 +113,7 @@ In metadata-driven development, we embrace three core truths:

The UI doesn't "build" a form; it **projects** the Object schema into visual components.

{/* os:check */}
```typescript
// Conceptual: the schema IS the form. The runtime renders a view
// definition (a FormView metadata object) — there is no FormView
Expand Down Expand Up @@ -234,6 +236,7 @@ data, API, UI, and permissions in a single change — it can answer *"what break
if I change this?"* instead of grepping and hoping. That turns AI from an
autocomplete tool into a real co-maintainer.

{/* os:check */}
```typescript
// All you need:
import { ObjectSchema, Field } from '@objectstack/spec/data';
Expand Down Expand Up @@ -332,6 +335,7 @@ When defining objects and metadata in ObjectStack, follow these strict rules and
### 1. Always Use `ObjectSchema.create()` with `Field.*` Helpers

**✅ Correct:**
{/* os:check */}
```typescript
import { ObjectSchema, Field } from '@objectstack/spec/data';

Expand Down Expand Up @@ -476,6 +480,7 @@ export const Account = ObjectSchema.create({

### Quick Reference

{/* os:check */}
```typescript
import { ObjectSchema, Field } from '@objectstack/spec/data';

Expand Down
2 changes: 2 additions & 0 deletions content/docs/data-modeling/analytics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ the object graph, and every surface references the same definition.

## Authoring a dataset

{/* os:check */}
```ts
// src/datasets/sales.dataset.ts
import { defineDataset } from '@objectstack/spec/ui';
Expand Down Expand Up @@ -110,6 +111,7 @@ A `metric` (KPI) widget omits `dimensions` and shows the single measure value.

## Binding a report

{/* os:check */}
```ts
export const SalesByStageReport = {
name: 'sales_by_stage',
Expand Down
1 change: 1 addition & 0 deletions content/docs/data-modeling/external-datasources.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ the plain multi-datasource routing of **managed** databases ObjectStack owns, se
Use `defineDatasource` with `schemaMode: 'external'`. The `external` block carries
the federation policy (write gate, boot validation, credentials).

{/* os:check */}
```typescript
import { defineDatasource } from '@objectstack/spec/data';

Expand Down
1 change: 1 addition & 0 deletions content/docs/data-modeling/fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ A **Field** defines an individual property within an Object. ObjectStack provide

Fields are defined inside an Object's `fields` map using `Field.*` factory methods:

{/* os:check */}
```typescript
import { ObjectSchema, Field } from '@objectstack/spec/data';

Expand Down
2 changes: 2 additions & 0 deletions content/docs/data-modeling/formulas.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ plus the ObjectStack standard library.

Every expression in metadata is persisted as the same envelope:

{/* os:check */}
```ts
type Expression = {
dialect: 'cel' | 'cron' | 'template';
Expand Down Expand Up @@ -85,6 +86,7 @@ At **input** time you may write a bare string for shorthand — the spec
transforms it into the right envelope based on the field type. The compiled
artifact always contains the full envelope (and, after M9.2, the AST).

{/* os:check */}
```ts
import { F, P, cel, cron, tmpl } from '@objectstack/spec';
import { ObjectSchema, Field } from '@objectstack/spec/data';
Expand Down
1 change: 1 addition & 0 deletions content/docs/data-modeling/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Every ObjectStack application starts here: you declare **objects** (business ent

A real object definition looks like this (from the CRM example app):

{/* os:check */}
```typescript
import { ObjectSchema, Field } from '@objectstack/spec/data';

Expand Down
1 change: 1 addition & 0 deletions content/docs/data-modeling/indexing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ description: Optimize query performance with single-field, unique, compound, and

Optimize query performance with indexes:

{/* os:check */}
```typescript
indexes: [
// Single field index
Expand Down
3 changes: 3 additions & 0 deletions content/docs/data-modeling/objects.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ An **Object** is the foundational metadata type in ObjectStack. It defines a bus

## Basic Structure

{/* os:check */}
```typescript
import { ObjectSchema, Field } from '@objectstack/spec/data';

Expand Down Expand Up @@ -192,6 +193,7 @@ entirely.

Optimize query performance:

{/* os:check */}
```typescript
indexes: [
{ fields: ['name'], type: 'btree', unique: false },
Expand Down Expand Up @@ -281,6 +283,7 @@ listing the verb in `apiMethods`.

## Complete Example

{/* os:check */}
```typescript
import { ObjectSchema, Field } from '@objectstack/spec/data';

Expand Down
Loading
Loading