You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: v17 docs sweep run 5 — rc.2 catch-up (#4881)
Three hand-written pages had drifted behind changes that landed in the
2bafe62..a2ebea2 window.
- data-modeling/validation.mdx contradicted itself: the has(x) callout says
an unevaluable predicate is rejected fail-closed, while the condition
paragraph twelve lines later still taught the pre-17 "logged and skipped
rather than blocking the write". #4649 reversed that. Rewritten to the
shipped contract, including the total stored-or-payload record that makes
the has() callout true in the first place.
- automation/hooks.mdx had no coverage of the declarative condition gate
while three changes landed on it, one breaking. Adds a section for #4775
(unevaluable condition aborts the operation), #4770 (evaluates against
stored + payload, not the payload alone) and #4784 (previous is bound).
- concepts/metadata-lifecycle.mdx did not list job, which #4509 closed to
runtime creation and org override. Adds the row with its reasoning, and
notes the retired standalone validation kind under ADR-0088.
Filed #4880 for the areas[] documentation gap rather than guessing at the
section shape.
Docs-only; releases nothing.
Claude-Session: https://claude.ai/code/session_01AaegKY1Y7GqTb8CKMm5GLC
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: content/docs/concepts/metadata-lifecycle.mdx
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -113,8 +113,9 @@ In shared-database multi-tenancy, **most metadata types must not be per-org cust
113
113
|`permission`, `position`| ✅ | Per-org overlays are allowed; tenant-level controls layer on top. |
114
114
|`object`, `field`| ❌ | Defines the table schema. Overriding a packaged object/field would break existing data — but both set `allowRuntimeCreate: true`, so tenants *can* author brand-new objects and fields. |
115
115
|`datasource`| ❌ | Connection strings; multi-tenant isolation is enforced at a higher layer. (`allowRuntimeCreate: true` — the datasource wizard persists `origin: 'runtime'` rows.) |
116
+
|`job`| ❌ |**Also `allowRuntimeCreate: false` since protocol 17** (#4509). `JobSchema.handler` names a function in the compiled bundle's function table, which a runtime writer has no way to reach — so a job created in Studio or through `PUT /meta` parsed, saved, reported success and was never scheduled. The door is closed rather than bridged: `job` stays first-class through `*.job.ts` / `defineStack({ jobs, functions })`, where every schedule shape, `retryPolicy` and `timeout` does reach the scheduler. Existing rows are untouched — they were never scheduled — and `migrateStoredMetadata` reports them `skipped`. |
116
117
117
-
There is no `workflow` metadata type (per [ADR-0020](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0020-state-machine-converge-and-enforce.md), record state machines are a `state_machine` validation). The runtime gate is implemented in `OVERLAY_ALLOWED_TYPES` (derived from the registry) and enforced by `SysMetadataRepository.put()`.
118
+
There is no `workflow` metadata type (per [ADR-0020](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0020-state-machine-converge-and-enforce.md), record state machines are a `state_machine` validation). Nor is there a standalone `validation` type any more — it was retired in protocol 17 under [ADR-0088](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0088-metadata-kind-admission-and-retirement.md) because `ValidationRuleSchema` carries no object-binding key, so a rule authored through that door could never say what it protected; author rules in the object's own `validations[]` instead. The runtime gate is implemented in `OVERLAY_ALLOWED_TYPES` (derived from the registry) and enforced by `SysMetadataRepository.put()`.
118
119
119
120
The gate is **two-tier** — `allowOrgOverride: false` is not the same as "no runtime writes":
Copy file name to clipboardExpand all lines: content/docs/data-modeling/validation.mdx
+16-1Lines changed: 16 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -104,7 +104,22 @@ Formula-based validation using expressions:
104
104
}
105
105
```
106
106
107
-
The `condition` is a **CEL** predicate and should evaluate to `true` when the data is **invalid**. A predicate that cannot be evaluated (parse error, unbound variable) is treated as a broken rule — it is logged and skipped rather than blocking the write.
107
+
The `condition` is a **CEL** predicate and should evaluate to `true` when the data is **invalid**.
108
+
109
+
A predicate that cannot be evaluated (parse error, unbound variable, a comparison
110
+
CEL has no overload for) **rejects the write** with `VALIDATION_FAILED`, naming the
111
+
rule and — when the fault is a missing key — the key the predicate read and how to
112
+
fix it. Until protocol 17 such a rule was logged at WARN and *skipped*, so the write
113
+
went through while the rule stayed declared and enforced nothing; a validation exists
114
+
to reject a write, and "the rule could not be checked" must never resolve to
115
+
"allowed" (#4649). `severity` still governs blocking — an unevaluable `warning` /
116
+
`info` rule is logged and does not throw.
117
+
118
+
The record a predicate reads is the stored row overlaid with this write's payload,
119
+
**total over the object's declared fields** (`null` for a declared field present in
120
+
neither), on update as well as insert — so a driver that stores only the columns it
121
+
wrote no longer decides whether an expression is evaluable. That totality is also why
122
+
`has()` is not a null guard: see the callout above.
108
123
109
124
### Uniqueness (use an index, not a validation rule)
0 commit comments