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
40 changes: 34 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
outputs:
docs: ${{ steps.changes.outputs.docs }}
core: ${{ steps.changes.outputs.core }}
skilldocs: ${{ steps.changes.outputs.skilldocs }}
generated: ${{ steps.changes.outputs.generated }}
steps:
- name: Checkout repository
uses: actions/checkout@v7
Expand All @@ -38,10 +38,30 @@ jobs:
- 'content/**'
- 'pnpm-lock.yaml'
- '.github/workflows/ci.yml'
skilldocs:
# Inputs AND outputs of every generated artifact checked by the
# `check-generated` job. A path missing here makes that gate dormant on
# exactly the PRs that can break it, so keep the two in lockstep.
generated:
# check:skill-docs — catalog source is skills/*/SKILL.md; the derived
# listings are skills/README.md + the reference page below. (That page
# was content/docs/guides/skills.mdx until #2584 moved it; this filter
# kept watching the old path, so hand-edits to the generated block went
# unchecked from then until now.)
- 'skills/**'
- 'content/docs/guides/skills.mdx'
- 'content/docs/ai/skills-reference.mdx'
- 'packages/spec/scripts/build-skill-docs.ts'
# check:spec-changes / check:upgrade-guide — derived from the ADR-0087
# registries and the protocol version, none of which the skills paths
# above cover. Both gates were dormant on spec-only PRs: a protocol bump
# or a new migration could not trigger the job that verifies them.
- 'packages/spec/scripts/build-spec-changes.ts'
- 'packages/spec/scripts/build-upgrade-guide.ts'
- 'packages/spec/src/migrations/**'
- 'packages/spec/src/conversions/**'
- 'packages/spec/src/kernel/protocol-version.ts'
- 'packages/spec/api-surface.json'
- 'packages/spec/spec-changes.json'
- 'docs/protocol-upgrade-guide.md'
- '.github/workflows/ci.yml'
core:
- 'packages/**'
Expand Down Expand Up @@ -354,10 +374,18 @@ jobs:
- name: Build Docs
run: pnpm --filter @objectstack/docs build

check-skill-docs:
name: Check Skill Docs
# Generated artifacts that are committed to the tree: regenerate, fail on drift.
# Named for the class, not for skill docs alone — it has checked the ADR-0087
# protocol artifacts for a while, and a name that hid them is part of why their
# paths went missing from the filter above.
#
# The generated reference docs (content/docs/references/**) are deliberately NOT
# here: they need to gate spec-only PRs, and this whole job is filter-gated, so
# they live in lint.yml's required, unfiltered "TypeScript Type Check" job.
check-generated:
name: Check Generated Artifacts
needs: filter
if: needs.filter.outputs.skilldocs == 'true'
if: needs.filter.outputs.generated == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,21 @@ jobs:
- name: Type check (@objectstack/spec)
run: pnpm --filter @objectstack/spec exec tsc --noEmit

# Generated-docs gate: content/docs/references/** is generated from the spec
# by `gen:schema && gen:docs` and committed. Nothing regenerated it in CI, so
# it drifted silently — #3076 added RowCrudActionOverride to the spec and the
# public reference docs never learned the type existed. Regenerates and fails
# on any difference.
#
# Deliberately lives in this job, not in ci.yml's "Build Docs": that job is
# gated on a `docs` paths-filter that does not include packages/spec/**, so it
# skips exactly the spec-only PRs that cause this drift (#3076 was one). This
# job has no paths filter and is a required status check, so the gate cannot go
# dormant. It reads src/ + json-schema/ via tsx and needs no build, so it runs
# before the workspace build and fails in ~2s.
- name: Check generated reference docs are in sync with the spec
run: pnpm --filter @objectstack/spec check:docs

# Example apps are AI-authoring reference templates; a red typecheck is a
# bad signal to copy from. tsup transpiles them without a full typecheck,
# so build alone will not catch type drift — typecheck them explicitly.
Expand Down
17 changes: 15 additions & 2 deletions content/docs/references/api/discovery.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ not been verified (may 501 at runtime).
## TypeScript Usage

```typescript
import { ApiRoutes, Discovery, RouteHealthEntry, RouteHealthReport, ServiceInfo, WellKnownCapabilities } from '@objectstack/spec/api';
import type { ApiRoutes, Discovery, RouteHealthEntry, RouteHealthReport, ServiceInfo, WellKnownCapabilities } from '@objectstack/spec/api';
import { ApiRoutes, Discovery, RouteHealthEntry, RouteHealthReport, ServiceInfo, ServiceSelfInfo, WellKnownCapabilities } from '@objectstack/spec/api';
import type { ApiRoutes, Discovery, RouteHealthEntry, RouteHealthReport, ServiceInfo, ServiceSelfInfo, WellKnownCapabilities } from '@objectstack/spec/api';

// Validate data
const result = ApiRoutes.parse(data);
Expand Down Expand Up @@ -132,6 +132,19 @@ const result = ApiRoutes.parse(data);
| **rateLimit** | `Object` | optional | Rate limit and quota info for this service |


---

## ServiceSelfInfo

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **status** | `Enum<'stub' \| 'degraded'>` | ✅ | stub = placeholder or dev fake (do not use for real work); degraded = functional fallback with reduced capability |
| **handlerReady** | `boolean` | optional | Whether the HTTP handler genuinely serves requests. Defaults: false for stub, true for degraded. |
| **message** | `string` | optional | Human-readable explanation, e.g. what to install for the full implementation |


---

## WellKnownCapabilities
Expand Down
2 changes: 1 addition & 1 deletion content/docs/references/data/field.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ const result = Address.parse(data);
| **conditionalRequired** | `string \| Object` | optional | Predicate (CEL) — field is required when TRUE. Alias of `requiredWhen`. |
| **widget** | `string` | optional | Form widget override — names a registered field component (resolved as `field:<widget>`) to render this field instead of the `type` default. Degrades to the `type` renderer when unregistered. e.g. "object-ref", "filter-condition", "recipient-picker". |
| **hidden** | `boolean` | optional | Hidden from default UI |
| **readonly** | `boolean` | optional | Read-only in UI |
| **readonly** | `boolean` | optional | Read-only — never editable in forms, AND server-enforced on UPDATE: a non-system write to this field is silently dropped from the payload (#2948/#3003; symmetric with `readonlyWhen`). INSERT may still seed it (defaultValue, import). |
| **requiredPermissions** | `string[]` | optional | [ADR-0066 D3] Capabilities required to read/edit this field (mask on read, deny on write; AND-gate). |
| **system** | `boolean` | optional | Auto-injected system/audit field (e.g. created_at, updated_by, organization_id). Tools that surface system fields separately from author-declared business fields should branch on this flag. |
| **sortable** | `boolean` | optional | Whether field is sortable in list views |
Expand Down
19 changes: 17 additions & 2 deletions content/docs/references/data/object.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ API Operations Enum
## TypeScript Usage

```typescript
import { ApiMethod, Index, Lifecycle, LifecycleClass, Object, ObjectAccessConfig, ObjectCapabilities, ObjectExtension, ObjectExternalBinding, ObjectFieldGroup, ObjectOwnershipEnum, ObjectRequiredPermissions, PerOperationRequiredPermissions, SoftDeleteConfig, TenancyConfig, VersioningConfig } from '@objectstack/spec/data';
import type { ApiMethod, Index, Lifecycle, LifecycleClass, Object, ObjectAccessConfig, ObjectCapabilities, ObjectExtension, ObjectExternalBinding, ObjectFieldGroup, ObjectOwnershipEnum, ObjectRequiredPermissions, PerOperationRequiredPermissions, SoftDeleteConfig, TenancyConfig, VersioningConfig } from '@objectstack/spec/data';
import { ApiMethod, Index, Lifecycle, LifecycleClass, Object, ObjectAccessConfig, ObjectCapabilities, ObjectExtension, ObjectExternalBinding, ObjectFieldGroup, ObjectOwnershipEnum, ObjectRequiredPermissions, PerOperationRequiredPermissions, RowCrudActionOverride, SoftDeleteConfig, TenancyConfig, VersioningConfig } from '@objectstack/spec/data';
import type { ApiMethod, Index, Lifecycle, LifecycleClass, Object, ObjectAccessConfig, ObjectCapabilities, ObjectExtension, ObjectExternalBinding, ObjectFieldGroup, ObjectOwnershipEnum, ObjectRequiredPermissions, PerOperationRequiredPermissions, RowCrudActionOverride, SoftDeleteConfig, TenancyConfig, VersioningConfig } from '@objectstack/spec/data';

// Validate data
const result = ApiMethod.parse(data);
Expand Down Expand Up @@ -282,6 +282,21 @@ Type: `string[]`
| **delete** | `string[]` | optional | Capabilities required to delete (delete/purge). |


---

## RowCrudActionOverride

Boolean-or-predicates override for a built-in row CRUD affordance.

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **enabled** | `boolean` | optional | Object-level on/off for the generic affordance; same meaning as the bare boolean form. Omitted → managedBy bucket default. |
| **visibleWhen** | `string \| Object` | optional | Per-record CEL predicate; false → hide the row button for that record. Fail-closed. |
| **disabledWhen** | `string \| Object` | optional | Per-record CEL predicate; true → render the row button disabled for that record. Fail-soft. |


---

## SoftDeleteConfig
Expand Down
14 changes: 7 additions & 7 deletions content/docs/references/integration/connector.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ entries with `enabled: false`. Provider-bound declarative instances that

a generic executor (connector-openapi / connector-mcp) materializes at

boot are tracked in #2977 (ADR-0096).
boot are tracked in #2977 (ADR-0097).

Authentication is now imported from the canonical auth/config.zod.ts.

Expand Down Expand Up @@ -177,9 +177,9 @@ Circuit breaker configuration
| **description** | `string` | optional | Connector description |
| **icon** | `string` | optional | Icon identifier |
| **authentication** | `Object \| Object \| Object \| Object \| Object` | optional | Authentication configuration (runtime shape with inline secrets). Provider-bound declarative instances use `auth.credentialRef` instead. |
| **provider** | `string` | optional | Generic-executor key that materializes this declarative entry at boot (e.g. openapi/mcp/rest). Omit for a catalog-only descriptor. Unknown provider ⇒ hard boot error (ADR-0096). |
| **providerConfig** | `Record<string, any>` | optional | Provider-specific config validated by the provider factory at boot (e.g. `{ spec, baseUrl }` for openapi). Requires `provider`. |
| **auth** | `Object \| Object \| Object \| Object` | optional | Declarative instance auth — references credentials via `credentialRef` (resolved at boot), never inline secrets. Requires `provider` (ADR-0096). |
| **provider** | `string` | optional | Generic-executor key that materializes this declarative entry at boot (e.g. openapi/mcp/rest). Omit for a catalog-only descriptor. Unknown provider ⇒ hard boot error (ADR-0097). |
| **providerConfig** | `Record<string, any>` | optional | Provider-specific config validated by the provider factory at boot (e.g. `{ spec, baseUrl }` for openapi, where spec is an inline document, a package-relative file path like './billing-openapi.json', or an http(s) URL). Requires `provider`. |
| **auth** | `Object \| Object \| Object \| Object` | optional | Declarative instance auth — references credentials via `credentialRef` (resolved at boot), never inline secrets. Requires `provider` (ADR-0097). |
| **actions** | `Object[]` | optional | |
| **triggers** | `Object[]` | optional | |
| **syncConfig** | `Object` | optional | Data sync configuration |
Expand Down Expand Up @@ -303,9 +303,9 @@ Connector type
| **description** | `string` | optional | Connector description |
| **icon** | `string` | optional | Icon identifier |
| **authentication** | `Object \| Object \| Object \| Object \| Object` | optional | Authentication configuration (runtime shape with inline secrets). Provider-bound declarative instances use `auth.credentialRef` instead. |
| **provider** | `string` | optional | Generic-executor key that materializes this declarative entry at boot (e.g. openapi/mcp/rest). Omit for a catalog-only descriptor. Unknown provider ⇒ hard boot error (ADR-0096). |
| **providerConfig** | `Record<string, any>` | optional | Provider-specific config validated by the provider factory at boot (e.g. `{ spec, baseUrl }` for openapi). Requires `provider`. |
| **auth** | `Object \| Object \| Object \| Object` | optional | Declarative instance auth — references credentials via `credentialRef` (resolved at boot), never inline secrets. Requires `provider` (ADR-0096). |
| **provider** | `string` | optional | Generic-executor key that materializes this declarative entry at boot (e.g. openapi/mcp/rest). Omit for a catalog-only descriptor. Unknown provider ⇒ hard boot error (ADR-0097). |
| **providerConfig** | `Record<string, any>` | optional | Provider-specific config validated by the provider factory at boot (e.g. `{ spec, baseUrl }` for openapi, where spec is an inline document, a package-relative file path like './billing-openapi.json', or an http(s) URL). Requires `provider`. |
| **auth** | `Object \| Object \| Object \| Object` | optional | Declarative instance auth — references credentials via `credentialRef` (resolved at boot), never inline secrets. Requires `provider` (ADR-0097). |
| **actions** | `Object[]` | optional | |
| **triggers** | `Object[]` | optional | |
| **syncConfig** | `Object` | optional | Data sync configuration |
Expand Down
Loading