Repository navigation
Add AI workflow automation and predictive analytics protocols - #83
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…alytics Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
|
This PR is very large. Consider breaking it into smaller PRs for easier review. |
There was a problem hiding this comment.
Pull request overview
This PR introduces AI workflow automation and predictive analytics protocol schemas to the ObjectStack spec, with matching JSON Schemas, documentation, and tests. It extends the AI surface area to support intelligent workflows (triggers, tasks, post-actions) and ML model definitions (features, training, evaluation, drift, prediction IO).
Changes:
- Added
AIWorkflowAutomationprotocol schemas, batch/execution result types, re-exports, and comprehensive scenario-based tests. - Added
PredictiveModel/ predictive analytics schemas (features, hyperparameters, training, metrics, prediction request/response, drift), plus exhaustive tests. - Generated and wired corresponding JSON Schema definitions and MDX reference documentation for all new AI protocol components.
Reviewed changes
Copilot reviewed 41 out of 41 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
packages/spec/src/ai/workflow-automation.zod.ts |
Defines Zod-first schemas and inferred types for AI workflow triggers, tasks, schedules, post-actions, workflow definition, batch execution, and execution results. |
packages/spec/src/ai/workflow-automation.test.ts |
Adds extensive Vitest coverage for workflow automation schemas, including edge cases and real-world workflows. |
packages/spec/src/ai/predictive.zod.ts |
Defines Zod schemas and inferred types for predictive model types, features, hyperparameters, training config, evaluation metrics, model definition, prediction IO, and drift detection. |
packages/spec/src/ai/predictive.test.ts |
Provides broad test coverage for predictive schemas across model types and realistic ML use cases. |
packages/spec/src/ai/index.ts |
Re-exports new workflow automation and predictive analytics schemas from the AI module entrypoint. |
packages/spec/json-schema/WorkflowSchedule.json |
JSON Schema for WorkflowSchedule, mirroring the schedule Zod schema used by AI workflows. |
packages/spec/json-schema/WorkflowFieldCondition.json |
JSON Schema for WorkflowFieldCondition, used to constrain field_changed workflow triggers. |
packages/spec/json-schema/TrainingConfig.json |
JSON Schema for TrainingConfig, representing training data splits, strategy, and resource limits. |
packages/spec/json-schema/PredictiveModelType.json |
JSON Schema enum for predictive model types (classification, regression, etc.). |
packages/spec/json-schema/PredictiveModel.json |
JSON Schema for PredictiveModel, aligning with the predictive model Zod schema. |
packages/spec/json-schema/PredictionResult.json |
JSON Schema for PredictionResult; currently treats prediction as optional, unlike the Zod schema (see comment). |
packages/spec/json-schema/PredictionRequest.json |
JSON Schema for PredictionRequest, describing model name, record IDs, direct input data, and return flags. |
packages/spec/json-schema/PostProcessingAction.json |
JSON Schema for PostProcessingAction, used in AI workflow post-action definitions. |
packages/spec/json-schema/ModelFeature.json |
JSON Schema for ModelFeature, capturing feature identity, source, type, and transformation. |
packages/spec/json-schema/ModelDrift.json |
JSON Schema for ModelDrift, mirroring drift detection configuration/result schema. |
packages/spec/json-schema/Hyperparameters.json |
JSON Schema for Hyperparameters, covering general, tree-based, NN, clustering, and time-series params. |
packages/spec/json-schema/EvaluationMetrics.json |
JSON Schema for EvaluationMetrics, including classification, regression, clustering, and time-series metrics. |
packages/spec/json-schema/BatchAIWorkflowExecution.json |
JSON Schema for BatchAIWorkflowExecution, specifying workflow name, record IDs, and batch options. |
packages/spec/json-schema/AIWorkflowTrigger.json |
JSON Schema enum for AIWorkflowTrigger values. |
packages/spec/json-schema/AIWorkflowExecutionResult.json |
JSON Schema for AIWorkflowExecutionResult, describing workflow run outcome and per-task results. |
packages/spec/json-schema/AIWorkflowAutomation.json |
JSON Schema for AIWorkflowAutomation, matching the workflow automation Zod schema. |
packages/spec/json-schema/AITaskType.json |
JSON Schema enum for AITaskType values. |
packages/spec/json-schema/AITask.json |
JSON Schema for AITask, reflecting the AI task Zod schema structure. |
content/docs/references/ai/WorkflowSchedule.mdx |
MDX reference doc for WorkflowSchedule, detailing schedule fields used by AI workflows. |
content/docs/references/ai/WorkflowFieldCondition.mdx |
MDX reference doc for WorkflowFieldCondition, describing field-based trigger conditions. |
content/docs/references/ai/TrainingConfig.mdx |
MDX reference doc for TrainingConfig, documenting training strategy and split fields. |
content/docs/references/ai/PredictiveModelType.mdx |
MDX listing the allowed PredictiveModelType enum values. |
content/docs/references/ai/PredictiveModel.mdx |
MDX reference for PredictiveModel, summarizing all model configuration properties. |
content/docs/references/ai/PredictionResult.mdx |
MDX doc for PredictionResult; currently documents prediction as optional, diverging from the Zod schema. |
content/docs/references/ai/PredictionRequest.mdx |
MDX reference for PredictionRequest, documenting request fields and semantics. |
content/docs/references/ai/PostProcessingAction.mdx |
MDX reference for PostProcessingAction, describing action types and configuration. |
content/docs/references/ai/ModelFeature.mdx |
MDX reference for ModelFeature, documenting feature identity, type, and transformation. |
content/docs/references/ai/ModelDrift.mdx |
MDX reference for ModelDrift, explaining drift types, severity, and metrics. |
content/docs/references/ai/Hyperparameters.mdx |
MDX reference for Hyperparameters, covering standard ML hyperparameter fields. |
content/docs/references/ai/EvaluationMetrics.mdx |
MDX reference for EvaluationMetrics, listing supported metric fields. |
content/docs/references/ai/BatchAIWorkflowExecution.mdx |
MDX reference for BatchAIWorkflowExecution, documenting batch execution options. |
content/docs/references/ai/AIWorkflowTrigger.mdx |
MDX listing allowed AIWorkflowTrigger values. |
content/docs/references/ai/AIWorkflowExecutionResult.mdx |
MDX reference for AIWorkflowExecutionResult, summarizing workflow run result properties. |
content/docs/references/ai/AIWorkflowAutomation.mdx |
MDX reference for AIWorkflowAutomation, documenting workflow identity, triggers, tasks, and options. |
content/docs/references/ai/AITaskType.mdx |
MDX listing allowed AITaskType values. |
content/docs/references/ai/AITask.mdx |
MDX reference for AITask, describing task configuration fields. |
content/docs/references/ai/WorkflowSchedule.mdx |
MDX reference for WorkflowSchedule, used by AIWorkflow docs. |
content/docs/references/ai/WorkflowFieldCondition.mdx |
MDX reference for workflow field conditions (used in AI workflows). |
| export const PredictionResultSchema = z.object({ | ||
| modelName: z.string(), | ||
| modelVersion: z.string(), | ||
| recordId: z.string().optional(), | ||
| prediction: z.any().describe('The predicted value'), | ||
| confidence: z.number().optional().describe('Confidence score (0-1)'), |
There was a problem hiding this comment.
PredictionResultSchema currently requires prediction (z.any()) at the Zod level, but the generated JSON Schema (PredictionResult.json) and MDX docs (PredictionResult.mdx) treat prediction as optional (not listed in required and documented without the required checkmark). This discrepancy means CLI/runtime validation and JSON-schema–driven tooling/IDE hints will disagree on whether prediction must be present. Consider aligning these by either making prediction optional in the Zod schema or marking it as required in the JSON schema/docs, depending on the intended contract for prediction results.
…psMap rows, derived from the renderers' read points (objectui#8348 Q1-C) (objectstack-ai#18403) Fixes objectstack-ai#18305 Executes the objectui#8348 ruling 「8348 以协议为准」 (decision batch objectstack-ai#83, 2026-09-08) and batch objectstack-ai#136 item 3 (Q1-C, maintainer 「同意」): `ComponentPropsMap` gains `object-map`, `object-gantt` and `object-tree`, each row's key set derived the **objectstack-ai#7751 way — from the objectui renderer's own read points**, at the `.objectui-sha` pin `53ded82b`. Not derived from `@object-ui/types`' mirror. That mirror standing in as the authority for map and gantt is the defect this card closes, and for `object-tree` the mirror is measurably wrong about four keys (below). ## The objectstack-ai#7751 derivation table, extended by three rows Every citation is a line in the objectui checkout at `53ded82b`. The shared record-source ladder is `resolveRecordSourceConfig` in `core/src/utils/record-source.ts` — rung 1 `data` (`:151`, returned VERBATIM as a `ViewData`), rung 2 `staticData` (`:155`, wrapped into `{ provider: 'value' }`), rung 3 `objectName` (`:162`). ### `object-map` — `plugin-map/src/ObjectMap.tsx`, registry shell `plugin-map/src/index.tsx` | key | read point | declared as | |---|---|---| | `objectName` | ladder rung 3; `:793` (fetch dep), `:890` (navigation) | `z.string().optional()` | | `data` | `:169` array-shorthand head, then `resolveRecordSourceConfig` at `:174` | `ViewDataSchema` | | `staticData` | ladder rung 2 | `z.array(z.unknown())` | | `filter` | `:742` — `$filter: schema.filter` | `z.array(ViewFilterRuleSchema)` | | `sort` | `:743` — `$orderby: convertSortToQueryParams(schema.sort)` | `z.array(SortItemSchema)` | | `map` | `:370` — `getMapConfig` branch 1, the author face; the registration's declared `{ name: 'map', type: 'object' }` input | `z.unknown()` (see note) | | `mapStyle` | `:365` — `schema.mapStyle \|\| schema.map?.style` | `z.string()` | | `navigation` | `:889` — `useNavigationOverlay` | `z.unknown()` | | `enableClustering` | `:905` — `enableClustering ?? (schema.enableClustering \|\| …)` | `z.boolean()` | Measured and deliberately NOT declared: the flat `map`-config spellings `getMapConfig` branch 2 reads at `:382-396` (`locationField`, `latitudeField`, `longitudeField`, `titleField`, `descriptionField`, `zoom`, `center`) — the ObjectView/ListView flatten product, ruled an internal transport form, not a second authoring surface (maintainer ruling objectui#5018, 2026-08-17); `style`, which `:283` reads ONLY to say it is not consumed as a map style (objectui#5017) and which is the node's own inline CSS record; and the React props of `ObjectMapProps` (`clusterRadius`, the `data` ARRAY prop, the four callbacks, `className`, `dataSource`). ### `object-gantt` — `plugin-gantt/src/ObjectGantt.tsx`, registry shell `plugin-gantt/src/index.tsx` | key | read point | declared as | |---|---|---| | `objectName` | ladder rung 3; `:1359` (layout key), `:1491` (navigation), `:1873` (export name) | `z.string().optional()` | | `data` | `resolveRecordSourceConfig` at `:593` | `ViewDataSchema` | | `staticData` | ladder rung 2 | `z.array(z.unknown())` | | `filter` | `:738` — `$filter: schema.filter` | `z.array(ViewFilterRuleSchema)` | | `sort` | `:739` — `$orderby: convertSortToQueryParams(schema.sort)` | `z.array(SortItemSchema)` | | `gantt` | `:499-501` — `getGanttConfig` branch 1; the registration's `{ name: 'gantt', type: 'object' }` input, and `:501` validates it against this repo's OWN `GanttConfigSchema` | `GanttConfigSchema` | | `navigation` | `:1487` — `schema.navigation ?? { mode: 'drawer' }` | `z.unknown()` | | `label` | `:1871` — `resolveInlineI18nLabel(schema.label, displayLocale)` in the export-file-name chain (`:1849` is the comment ABOVE that chain, not a read) | `I18nLabelSchema` | | `skipWeekends` | `:1205` — `!!sw` into the working calendar | `z.boolean()` | | `holidays` | `:1206` — `new Set(hol)` for the duration math | `z.array(z.string())` | | `persistLayout` | `:1357` — `schema.persistLayout === false` | `z.boolean()` | | `viewName` | `:1359` — the `objectName:viewName` storage key | `z.string()` | | `markers` | `:1826` — `markers={schema.markers}` | `z.array(z.unknown())` | | `criticalPath` | `:1829` — `criticalPathDefault={!!schema.criticalPath}` | `z.boolean()` | | `showBaselines` | `:1832` — `schema.showBaselines !== false` | `z.boolean()` | | `readOnly` | `:1833`, `:1916` — `!!schema.readOnly` | `z.boolean()` | | `mobileReadOnly` | `:1834` — `schema.mobileReadOnly !== false` | `z.boolean()` | Measured and deliberately NOT declared: the 30 flat `GanttConfig` spellings branch 2 reads at `:513-543` (objectui#6469 inherited the objectui#5018 ruling for this block); `title`, which this renderer never reads; and a row cap — the reload's `$top` is the platform ceiling `NON_GRID_ROW_CEILING_TOP`, which the renderer's own comment marks 「⛔ Not authorable」. ### `object-tree` — `plugin-tree/src/ObjectTree.tsx`, registry shell `plugin-tree/src/index.tsx` | key | read point | declared as | |---|---|---| | `objectName` | ladder rung 3; `:534`, `:570`, `:605` | `z.string().optional()` | | `data` | `resolveRecordSourceConfig` at `:359` — rung 1, returned VERBATIM as a `ViewData`. That ONE site is the whole support for this arm | `ViewDataSchema` | | `staticData` | ladder rung 2 | `z.array(z.unknown())` | | `filter` | `:474` — `$filter: schema.filter` | `z.array(ViewFilterRuleSchema)` | | `tree` | `:108` — the nested block `getTreeConfig` reads; the registration's `{ name: 'tree', type: 'object' }` input | `TreeConfigSchema` | | `navigation` | `:591` — `useNavigationOverlay({ navigation: (schema as any).navigation })` | `z.unknown()` | **`data` IS on the row, and that is the measurement, not family symmetry.** The card left this conditional — 「if the renderer's authored surface has no `data`, the row declares none」 — and the condition is FALSE here: the renderer reaches `schema.data` through the shared ladder at `:359`, which returns it verbatim as a `ViewData`. ⛔ `:496` is **not** a second site for that arm, and an earlier revision of this body over-claimed it as one: `(rest as any).data ?? (schema as any).data` is gated by `Array.isArray(passed)` on the very next line, so it honours only the bare-ARRAY shorthand this row **refuses** — the same shorthand `object-map` measures and declines above. One ladder site is sufficient and `:359` is it, so the row stands on a corrected citation. What is absent is the DECLARATION, on both published faces: `ObjectTreeSchema` on `@object-ui/types` at this pin declares no `data`, no `staticData`, no `filter` and no `navigation` at all, and makes `objectName` required — four keys the renderer reads. That is precisely the mirror-is-wrong shape the ruling puts the protocol in front of, so the row follows the read points and the mirror is the face that has to follow. **No `sort`.** `ObjectTree.tsx:473-484` issues `$filter`, `$top` and `$expand` and no `$orderby`, and nothing else in the file reads an order. A `sort` door here would publish a key with no read site — the exact defect objectstack-ai#7751 exists to remove, in the other direction. ## Three derivation decisions, each pinned rather than left to review 1. **The flat config spellings stay unauthorable on all three.** `ObjectView` / `ListView` build these nodes by spreading `options.map` / `options.gantt` / `options.tree`'s CONTENTS at the top level, carrying no block key at all; that is an internal transport form (objectui#5018, inherited by objectui#6469), and the map and gantt renderers name every flat key beside a present block as IGNORED in a dev warning. Writing one now gets a wrong-layer prescription naming the config block — the channel `object-calendar` already uses. Each set is held equal to the config block it points at (`ListMapConfigSchema.shape`; `GanttConfigSchema.shape` plus the legacy singular `dependencyField`; `TreeConfigSchema.shape` plus `titleField`, which `getTreeConfig:117` reads only as `labelField`'s last fallback), so it cannot drift silently. 2. **`filter` and `sort` are the family's one orthography from birth** — `ViewFilterRule[]` (ui#6206-B reaching the family: objectstack-ai#15449, batch objectstack-ai#55 option A) and `SortItem[]` (objectui#8221, batch objectstack-ai#77 option B). A new door on a family-wide ruling has no "measured before the ruling" arm. The whole-map census pins in `component.test.ts` already demanded it: every `filter` door must refuse the record form, and only `record:related_list` may take a string `sort`. 3. **Config-block VALUES follow the read point.** `gantt` takes `GanttConfigSchema` because `ObjectGantt.tsx:501` literally validates the authored block against that schema, imported from `@objectstack/spec/ui`. `tree` takes `TreeConfigSchema`, which objectstack-ai#15469 shut against unknown keys on this very measurement, re-measured unchanged here. `map` stays `z.unknown()` — and that is measured, not conservative-by-default: the spec's own `ListMapConfigSchema` is strict and declares no `style`, the key `getMapConfig:365` reads at `schema.map?.style`, so pointing this door at it would refuse a value the renderer honours today. The pin records the reason so the later value ratchet has one. ## Measured file face 13 files, all additive to a published surface; nothing retired, nothing narrowed. ``` .changeset/18305-component-props-map-map-gantt-tree.md new packages/spec/src/ui/component.zod.ts +3 schemas, +3 rows, +3 guidance sets packages/spec/src/ui/component.test.ts +1 describe, 4 door lists extended packages/spec/src/ui/filter-rule-array-guidance.test.ts +3 doors (seven -> ten) packages/spec/src/ui/component-type-vocabulary.test.ts +1 pin packages/spec/authorable-surface/ui.json +32 rows (generated) packages/spec/json-schema.manifest/ui.json +3 entries (generated) packages/spec/api-surface/ui.json +9 exports (generated) packages/spec/export-origins/ui.json generated packages/spec/declaration-map/ui.json generated content/docs/references/ui/component.mdx generated content/docs/references/index.mdx generated docs/audits/2026-07-unknown-key-strictness-ledger.counts.md generated (444 -> 447 sites) ``` No corpus moves: a whole-tree grep for an authored `object-map` / `object-gantt` / `object-tree` page node finds zero outside `sdui.manifest.json` (a registry dump, not authored metadata), against a positive control that finds `object-grid` authored twice in `examples/app-showcase`. `PageComponentSchema.type` already accepted all three through its open string arm and still does; what changes is that an authored props bag on one of them is judged instead of skipped. ## Verification All readings below are at `e7967ce543` (the branch tip) unless a row says otherwise, exit codes captured by redirect-then-`$?`, never through a pipe. The spec suite, typecheck and the generated-artifact checks were re-run after the patch round below. | run | verdict | |---|---| | `pnpm --filter @objectstack/spec build` | exit 0 | | `pnpm --filter @objectstack/spec test` | exit 0 — 482 files, 13727 tests passed | | `pnpm --filter @objectstack/spec typecheck` | exit 0 (`tsc --noEmit` + `check:scripts-typecheck` + `check:test-typecheck`) | | `pnpm --filter @objectstack/lint test` | exit 0 — 103 files, 3821 passed, 5 skipped (the `ComponentPropsMap` consumer; its closure built first) | | `pnpm --filter @objectstack/lint typecheck` | exit 0 | | `pnpm --filter @objectstack/spec check:generated` | exit 0 after `--fix` regenerated the 5 it proved stale | | `MANIFEST=… check:react-declaration-parity --baseline … --strict` | exit 0 — `object-map` 2 both / 7 spec-only / **0 registry-only**, `object-gantt` 2 both / 14 spec-only / **0 registry-only**; baseline ratchet: no new divergence | | `pnpm exec eslint . --no-inline-config --format json` | exit 0 — **6786 files, 0 errors, 0 warnings**, repo-wide, no narrowing | | 47 further derived gates (`scripts/pm/dispatch-gates.mjs --commands`) | all exit 0 — see the acceptance notes for the one that did not | `packages/spec` has no workspace dependencies, so the dependency-closure build is an empty run by construction. CI on this head is **not enumerated here** and is not claimed green — the verdicts above are local runs only. ## Patch round — the at-tier review's two text findings, and the citations The contract review returned PASS; these are the three things its scope did not settle, all of them text or citation, with **no key, no value shape and no row membership moved**. Verified after the round rather than asserted: the three key sets read back identical, `ComponentPropsMap` still carries 48 rows, `object-tree` still accepts the `ViewData` object arm and still refuses the bare array, the gantt block is still strict, `object-chart` is still absent. 1. **`object-gantt`'s describes said "chart".** Every sibling names its own noun, and `object-chart` is the row this section deliberately leaves absent, so "chart" pointed an author at the one block with no row. Three describes now name gantt (`objectName`, `label`, `readOnly`). **Proof on the shipping artifact** — `content/docs/references/ui/component.mdx`, regenerated with the repo's own `gen:docs`, never by hand: `Object this chart binds to` → **0**; `Object this gantt binds to` → **1**, at `:398`; control `Object this NOUN binds to` → **6**, unchanged, so all six blocks name their own noun exactly once. 2. **`object-tree`'s `objectName` gave a reason that is false for tree.** It said the component-level `dataSource` binding can supply the object. Measured at the pin `53ded82b`: `plugin-tree/src/index.tsx` has **0** hits for the `ElementDataSource*` wiring, against **7** each in `plugin-map`, `plugin-gantt`, `plugin-grid` and `plugin-calendar` — four controls, so the zero discriminates. The key stays optional; the reason is now the true one, the ladder's first two rungs (`data` can name the object itself, `staticData` needs none), plus the explicit negative. In the generated doc the false reason reads **0** hits and the new line **1**, at `:768`. 3. **Two citations corrected**, in the tables above: gantt `label` at `:1871` (`:1849` is the comment), and tree `data` on `:359` alone. `check:docs` was red on `e3a256daa3` — source fixed, shipping artifact not yet regenerated — and is green on `e7967ce543`: `check:docs` exit 0, "223 generated files in sync with packages/spec"; `check:generated` exit 0, "All 15 generated artifacts are up to date"; `check:authorable-surface` exit 0, which is why `gen:schema` had nothing to write and `gen:docs` was the only stale artifact. `pnpm --filter @objectstack/spec test` exit 0, 482 files / 13727 tests, and `typecheck` exit 0, both re-run after the round. `git diff --name-only origin/main...HEAD -- content/docs/releases/` → **0** paths, against a control of **2** under `content/docs/references/`. On this head `Type Check · source gates` is **completed / success** — the job the red had truncated now reaches its end. The rest of CI is still running and is not claimed green. ## Patch round 2 — the at-tier review's FAIL: F1 blocking, F2 and F3 in passing Review of record: comment `5696414075` on this PR, `VERDICT: FAIL` at head `e7967ce543`. That review re-derived the accept set, the public surface, all three key sets, every value posture and the `minor` level and judged them RIGHT — nothing in this round moves a key, a value shape, a row membership or the level. Text, plus the one regeneration it forces. **F1 (blocking) — six describes named a function that does not exist in the code they describe.** The `objectName` / `data` / `staticData` describes on `object-gantt` and `object-tree` said the record source is resolved by, or "read FIRST/SECOND by", `getDataConfig`. Re-measured at the `.objectui-sha` pin `53ded82b`, each file fetched with `git show` in the same command block as its count: | file at `53ded82b` | `getDataConfig` | `resolveRecordSourceConfig` | |:---|---:|---:| | `plugin-gantt/src/ObjectGantt.tsx` | **0** | 2 — import `:64`, call `:593` | | `plugin-tree/src/ObjectTree.tsx` | **0** | 3 — import `:41`, call `:359`, comment `:422` | | `plugin-map/src/ObjectMap.tsx` (control) | **8** | 3 | The control discriminates, and it also settles what the map keeps: `ObjectMap.tsx` really does hold a local `getDataConfig` at `:135` — the array-shorthand head, delegating to the shared ladder at `:174` — so the three map describes are TRUE and keep the name. Gantt and tree reach the shared three-rung ladder directly: `resolveRecordSourceConfig` in `@object-ui/core` `utils/record-source.ts:146`, rungs at `:151` (`data`, returned verbatim), `:155` (`staticData`, wrapped as `{ provider: 'value', items }`) and `:162` (`objectName`, folded to `{ provider: 'object', object }`). So the ladder ORDER those six sentences describe was already true and only the name was wrong; the six now name `resolveRecordSourceConfig`. Neither renderer has any reader above its ladder call, so "read FIRST" is literal for both — unlike the map, whose array head runs first, which is the second reason its describes keep the wrapper's name. **Both sides counted, because this trap already bit this PR once.** An earlier round fixed a false describe in the source alone and `check:docs` caught the generated doc still carrying the old text. After `gen:docs`, at `c431c22e05`: | | `getDataConfig` | `resolveRecordSourceConfig` | |:---|---:|---:| | `packages/spec/src/ui/component.zod.ts` | **3** — map only, `:3226` `:3242` `:3244` | 11 | | `content/docs/references/ui/component.mdx` | **3** — map rows only, `:638-640` | **6** — gantt `:398-400`, tree `:768-770` | Both sides read 9 and 0 before the round. The generated diff is exactly six table rows; no map row moved. **F2** — `component.zod.ts:3545`, the tree `data` field JSDoc, cited two read points where the schema header at `:3487` already rules that `:496` is not a second site for the object arm. It cites `:359` alone now. Source comment; no artifact follows it. **F3** — two citations off by one line, both re-read at the pin: `getGanttConfig`'s `safeParse` is `ObjectGantt.tsx:500` (`:501` is the `if (!result.success)` beneath it), and the tree's "Not authorable" marker is `ObjectTree.tsx:482`, not `:477`. ### Verification for this round - `pnpm --filter @objectstack/spec build`, then `check:generated` — exit 0, all 15 artifacts up to date including `check:docs`, with `content/docs/references/ui/component.mdx` actually regenerated by `gen:docs` rather than hand-edited. Before the regeneration the same gate named that one artifact, and only it, stale. - `pnpm --filter @objectstack/spec test` — exit 0, 482 files / 13727 tests. `pnpm --filter @objectstack/spec typecheck` — exit 0. - `check:objectui-pin-citations` — exit 0, 26 asserting pin citations match `53ded82bf`. It verifies the sha, not the line numbers, and reports 0 anchor content assertions here: F3 was measured by hand at the pin, not caught by this gate. - 34 of the 109 gate families `dispatch-gates` derives for this change set were run locally, every one exit 0; the remaining 75 are the repo-wide farm CI owns. A declared narrowing, not a silent skip. - `pnpm lint` narrowed to the diff, with the three readings that make the narrowing a measurement: the population is eslint's own `**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` and eslint itself answers the regenerated `.mdx` with "File ignored because no matching configuration was supplied", so the linted population of this diff is one file; `--format json` reports 1 file, 0 errors, 0 warnings; and no type-aware linting is configured anywhere in `eslint.config.mjs` (no `projectService`, no `parserOptions.project`), so a comment-and-describe edit in one file cannot move any untouched file's verdict. ## Acceptance notes ### Docs Drift Check, re-verified rather than waved through The drift check flagged 6 hand-written pages. Each was re-read; none needs an edit, and the readings are here rather than implied by their absence from the diff. - **`content/docs/protocol/objectui/layout-dsl.mdx` does not ride this PR — measured, not assumed.** It is flagged via the symbol `ComponentPropsMap`, which it names exactly once, at `:721`, and as a MECHANISM: *"For the platform's own types the authoring rules dispatch `ComponentPropsMap` and reject a misspelled prop; a `custom.*` type has no entry there."* That sentence is still exactly true after this change — three more types now have entries, and it enumerates none. The page carries no register of the object-block family at all: no table, no list, and its only numeric completeness claim is about breakpoints (`:579`). **The discriminating control:** `object-calendar` and `object-form` each get **0** hits on that page, and both have carried `ComponentPropsMap` rows since objectstack-ai#7751 landed on 2026-08-12 — so the page already omits 2 of the 6 pre-existing rows, against a positive control of `object-metric` (3), `object-master-detail-form` (3), `object-kanban` (2), `object-grid` (1). A page that has never tracked the row set is not a list this change makes incomplete; its `### Metric Widget` / `### Master-Detail Forms` / `### Kanban Board` sections are chosen worked examples. Writing three new hand-written renderer sections would be new documentation of three renderers, which is a card, not a rider on "add three rows". - **The other five pages are literal-name collisions, not semantic links.** `content/docs/ui/views.mdx`, `concepts/architecture.mdx`, `getting-started/common-patterns.mdx`, `protocol/kernel/i18n-standard.mdx` and `ui/field-grouping-and-order.mdx` each get **0** hits for `ComponentPropsMap` and **0** for `object-map` / `object-gantt` / `object-tree`. They are reached only through string literals that this PR's new `OBJECT_GANTT_FLAT_CONFIG_GUIDANCE` key array happens to contain — `groupByField`, `timeZone`, `colorField`, `startDateField` and friends. `views.mdx` documents the LIST-VIEW `gantt` block (`type: 'gantt'` with `gantt: { startDateField, … }`, `:340-352`), whose schema is `ListViewSchema.gantt` / `GanttConfigSchema` in `view.zod.ts` — a file this diff does not touch at all, so that page's vocabulary is unmoved. - **Release-owned pages: untouched, with a control.** The drift check names 7 pages under `content/docs/releases/`; they are read-only. `git diff --name-only origin/main...HEAD -- content/docs/releases/` returns **0** paths, against a control of **2** paths under `content/docs/references/` in the same diff, so the zero is a reading and not an empty filter. ### `component-reference-rail.test.ts` needs no change — the reading, not the omission The card names it among "the vocabulary tests that pin the row set". Measured on this branch: the file is 164 lines, mentions `ComponentPropsMap` 4 times, and **every one of them is the single subscript `['record:reference_rail']` or prose about that one row** (`:3`, `:21`, `:33`, `:35`). `Object.keys(ComponentPropsMap)` → **0** hits; any `object-` type literal → **0** hits. Control, the same two probes on `component-type-vocabulary.test.ts`, which genuinely does pin the row set: **6** and **8** ⇒ the zeros discriminate. All twelve of its `describe`/`it` subjects are `record:reference_rail` behaviour. It reads no property of the row SET, so nothing in it can move when three `object-*` rows land — and it passed unchanged inside the 482 green spec files above. The row-set pins that DO move with the rows are `component.test.ts` (six ruled blocks to nine, plus the filter / sort / plural-`filters` / optional-`objectName` door lists, plus a new describe) and `component-type-vocabulary.test.ts`. The *reference rail* that regenerates is the docs one, `content/docs/references/ui/component.mdx`. ### Out of scope for this PR, noted rather than fixed Each with the measurement that found it: - **`ListMapConfigSchema` declares no map style at all.** The spec's list-view map block is strict and has neither `style` nor `mapStyle`, while `ObjectMap.tsx:365` reads `schema.map?.style` and objectui's `ObjectMapConfigSchema` declares `style`. `ListMapConfigSchema.safeParse({ style: '…' })` is refused — pinned as a negative in this PR's own tests, because it is the reason `object-map.map` stays `z.unknown()`. An author cannot declare a map style through the spec's list-view face today. Carrier: this PR's `map` value ratchet, whenever it is taken. - **`object-tree` is absent from the tracked `sdui.manifest.json`.** `check:react-declaration-parity` prints `object-tree: NO component in the manifest — not registered or not public`, although `plugin-tree/src/index.tsx` registers it under both `object-tree` and `tree`. The baseline ratchet is unaffected (a block with no baseline row cannot regress), so this PR is green either way, but the new row gets no parity comparison. Carrier: the next `sdui:manifest` regeneration. - **`pnpm check:cross-package-test-inputs` is sensitive to local build state, not to this diff.** It exits 1 in a worktree where `packages/spec/dist/` exists and 0 where it does not — measured here as a one-variable control, with the diff held constant and the directory restored byte-identically (216 files, `dist/ui/index.mjs` hash unchanged). It names `packages/cli/test/init-created-files-summary.e2e.test.ts`, which symlinks `packages/spec/dist` at `:115`; this PR touches neither `packages/cli` nor `turbo.json`. The `Lint & Repo Gates` job runs it on an unbuilt tree, which is why CI is green on it. - **`objectui#9239` has moved since the card was written.** The card describes it as an open, separate calendar-mirror contradiction; it is `closed completed`. Nothing in this PR depends on it, and `object-calendar` is untouched. - **objectui's own gantt face carries the same false attribution F1 removed from ours, and it is pre-existing.** At the same pin `53ded82b`, `packages/types/src/zod/objectql.zod.ts` docblock `:726` states `getDataConfig` is in `plugin-gantt/src/ObjectGantt.tsx`, and three `ObjectGanttSchema` describes repeat the name (`:738`, `:739`, `:838`) — while that file has 0 occurrences of it (control: `ObjectMap.tsx` 8, and the map's own mirror describes at `:641-643` are correspondingly TRUE). Separately, `core/src/utils/record-source.ts` quotes our describes as the ruled contract at `:28`, `:92`, `:95` and `:97` and attributes the text to the map and gantt zod twins, so after this PR that quotation matches the map twin and no longer matches the gantt one. Both sites are objectui's, in objectui's repo; nothing here can fix them and the pin does not move. Handed to the seat to file against objectui rather than fixed in passing. Not touched, deliberately: the `object-chart` deliberate-absence note in `component.zod.ts`, pinned as still absent in the new tests. objectui#8348 carries `pm:blocked` on this card; its remaining slice judges map / gantt / tree against these rows once this lands. Nothing here changes any objectui file. Authored by the `domain:spec` execution seat, session `session_01KB5PFtxuy1x3dcR5gxudx6`. --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Adds two AI protocol specifications to enable intelligent automation and ML-powered decision making in ObjectStack applications.
AI Workflow Automation (
ai/workflow-automation.zod.ts)Core capabilities:
Example usage:
Predictive Analytics (
ai/predictive.zod.ts)Model types: classification, regression, clustering, forecasting, anomaly_detection, recommendation, ranking
Features:
Example usage:
Technical notes
Featureschema by usingModelFeatureOriginal prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.