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
fix(lint): three write rules ask anchor provenance before exempting a system column (#8996)
* fix(lint): three write rules ask anchor provenance before exempting a system column (#8663)
`IMPLICIT_FIELDS` is object-independent, so on an ADR-0015 `external` object it
exempted injected anchors the platform never provisioned storage for. Measured
end to end: the engine's write-path validator PASSES the anchor (it is in the
registered schema) while refusing an undeclared name outright with
INVALID_FIELD, so the anchor is the only payload key that reaches the remote
database raw — where SQLite answers an untyped `no such column` that aborts the
whole statement.
Each of the three consumers now emits a separate advisory finding on that path,
at `warning`, reusing the read-axis anchor wording. Author-declared columns of
the same name stay untouched.
* docs(automation): record the unprovisioned-anchor write findings on the hook-bodies page (#8663)
* docs(automation): point the federation reference at the page that owns it (#8663)
The added line linked ADR-0015 to /docs/protocol/federation, which does not
exist — Check Documentation Links caught it. The ADR id now reads as bare text
(matching all eight existing ADR-0015 citations in content/docs) and the link
moves onto the concept, targeting the page that actually owns federation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Three write-surface lint rules now ask provenance, not just membership, before exempting a system column (#8663).
6
+
7
+
`validate-hook-body-writes`, `validate-action-body-writes` and `validate-flow-node-writes` share one `IMPLICIT_FIELDS` set, which is object-INDEPENDENT: it answers "could this name be implicitly writable somewhere", never "did the platform provision a column for it on THIS object". On an ADR-0015 `external` object those diverge — the registry injects `owner_id` / `organization_id` / the audit family onto a federated object exactly as onto a local one, but the remote database owns the schema and no column exists behind them.
8
+
9
+
Each rule now emits a new advisory finding on that path instead of staying silent — `hook-body-write-unprovisioned-anchor`, `action-body-write-unprovisioned-anchor`, `flow-node-write-unprovisioned-anchor` — sharing the `unprovisionedAnchorCause` / `unprovisionedAnchorHint` wording the read-axis rules already use. All three are `warning`: the flow-node rule's existence finding still gates at `error`, and its provenance finding deliberately does not, because the claim is about a remote schema this repo cannot see.
10
+
11
+
An author-DECLARED column of the same name is untouched — on a federated object it maps a remote column the author vouches for. `FlowNodeWriteSeverity` widens from `'error'` to `'error' | 'warning'` accordingly.
Copy file name to clipboardExpand all lines: content/docs/automation/hook-bodies.mdx
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -141,6 +141,7 @@ Static validation around a hook is asymmetric, and it is worth knowing exactly w
141
141
-**Checked — read side.**`hook.condition` is validated at build time against the target object's fields by the expression validator (`@objectstack/lint`), including array-valued `hook.object` targets. A condition referencing a nonexistent field fails the lint.
142
142
-**Checked — capability side.**`body.capabilities` gates which `ctx` APIs the body may call at all; the sandbox throws on an undeclared call.
143
143
-**Checked — write side, advisory and literal-only.** Since [#4271](https://github.com/objectstack-ai/objectstack/issues/4271), `body.source` is **parsed** (never executed, never type-checked) and the field names it writes are resolved against the target object's declarations. An unknown field raises `hook-body-write-unknown-field` — a **warning** carrying a did-you-mean suggestion, which never blocks a build. Action bodies get the same check on their `ctx.api` writes (`action-body-write-unknown-field`). Both run under `os validate`, `os lint` and `os compile`.
144
+
- **Checked — writes to a system column the object has no storage for.** Since [#8663](https://github.com/objectstack-ai/objectstack/issues/8663), a write to an injected system column is no longer exempted on the strength of its NAME alone. The registry injects `owner_id` / `organization_id` / the audit family onto an ADR-0015 [`external` object](/docs/data-modeling/external-datasources) exactly as onto a local one, but the remote database owns that schema and no column exists behind them. Writing one raises `hook-body-write-unprovisioned-anchor` (or `action-body-write-unprovisioned-anchor` / `flow-node-write-unprovisioned-anchor` on the other two surfaces) — a **warning** on all three, including the flow-node rule that otherwise gates, because the claim is about a remote schema the build cannot see. A column you **declare** yourself is untouched: on a federated object a declared `owner_id` maps a remote column you vouch for. Why it matters more than an ordinary typo: an undeclared name is refused upstream by the engine's own write-path validator (`INVALID_FIELD`), whereas the injected anchor is in the registered schema and passes it — so it is the one payload key that reaches the remote database raw, where a SQL remote aborts the **whole statement** with an untyped `no such column` and takes the correctly named fields of the same payload with it.
144
145
-**Checked — writes that reach nothing at all.** Since [#4345](https://github.com/objectstack-ai/objectstack/issues/4345), an action body assigning to `ctx.record` raises `action-record-write-discarded`, also a warning. This one is **not** a field-resolution question: an action's `ctx.record` is a snapshot the runtime never writes back, so the assignment is discarded whether or not the field is declared — see [Signature conventions](#signature-conventions) below.
145
146
146
147
Four literal write shapes are recognized, and only these:
0 commit comments