From 968c182445518f59480157c59333c3e96fc789b1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Aug 2026 13:07:51 +0000 Subject: [PATCH] docs(skills): an unevaluable hook condition aborts the write (#4775) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit skills/objectstack-data/references/data-hooks.md had been updated for #4770 and #4784 but still closed with the pre-17 failure mode — an undeclared key leaves the condition "logged at WARN and treated as false". #4775 replaced that: an unevaluable condition now aborts the operation. This is the AI-authoring reference for hooks, so the stale sentence pointed the wrong way on the axis that matters — it told an author a typo is a soft failure, and it downgraded the two bullets above it (previous is unbound on inserts and multi:true writes; has() is not a null guard) from "this breaks your write" to "this quietly disables your hook". Swept the rest of skills/ and .claude/skills/ against the same rc.2 window; nothing else was stale. Docs-only; releases nothing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AaegKY1Y7GqTb8CKMm5GLC --- .changeset/skill-hook-condition-aborts.md | 31 +++++++++++++++++++ .../objectstack-data/references/data-hooks.md | 17 ++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 .changeset/skill-hook-condition-aborts.md diff --git a/.changeset/skill-hook-condition-aborts.md b/.changeset/skill-hook-condition-aborts.md new file mode 100644 index 0000000000..f7cbcd79c6 --- /dev/null +++ b/.changeset/skill-hook-condition-aborts.md @@ -0,0 +1,31 @@ +--- +--- + +Docs-only: the `objectstack-data` skill's hook reference taught the pre-17 +failure mode for an unevaluable `condition`. + +`skills/objectstack-data/references/data-hooks.md` had been updated for #4770 +(the condition reads the record, not the payload) and #4784 (`previous` is +bound), but its closing bullet still said an undeclared key leaves the condition +"logged at WARN and treated as false" — the behaviour #4775 replaced. An +unevaluable condition now **aborts the operation**. + +This is the AI-authoring reference for hooks, so the stale sentence pointed the +wrong way on the one axis that matters: it told an author a typo is a soft +failure. It also silently downgraded the two bullets above it — `previous` is +unbound on inserts and on `multi: true` writes, and `has()` is not a null guard — +from "this breaks your write" to "this quietly disables your hook". + +Replaced with a callout carrying the #4775 rule and the reason the two outcomes +had to split (a `before*` guard swallowed into `false` lets writes through; an +audit hook swallowed into `false` drops records — opposite failures), plus the +practical authoring consequence. + +Swept the rest of `skills/` and `.claude/skills/` against the same rc.2 window; +nothing else was stale. `objectstack-automation` already documents #4343's +`script`-node retirements, `objectstack-query` already carries the #4286 +`cursor` / `joins` / `windowFunctions` prescriptions, and +`objectstack-formula` already documents #4649 fail-closed predicates and #4763's +build-time `has()` rejection. + +Releases nothing. diff --git a/skills/objectstack-data/references/data-hooks.md b/skills/objectstack-data/references/data-hooks.md index 3d1b19d1d6..9a0bacfdb8 100644 --- a/skills/objectstack-data/references/data-hooks.md +++ b/skills/objectstack-data/references/data-hooks.md @@ -260,8 +260,21 @@ in neither). So: `has(record.spent) && record.spent > record.budget` still faults on `null > null`. `has()` answers "is this key declared at all", which is a question about your spelling, not about your data. -- An **undeclared** key (a typo) stays unevaluable: the condition is logged at - WARN and treated as false. + +⚠️ **An unevaluable condition ABORTS the operation (#4775).** A typo'd key +(`record.stauts`), a `previous` reference on an insert, or a comparison CEL has +no overload for does **not** degrade to "the hook did not fire" — it **fails the +write**. Until protocol 17 the gate emitted a `logger.warn` and returned `false`, +which is why the two bullets above are load-bearing rather than stylistic: a +`before*` guard swallowed into `false` silently let writes through, and an audit +hook swallowed into `false` silently dropped records. Those are opposite +failures, so "the condition said no" and "the platform could not work out what +the condition says" are now different outcomes and the second one is loud. + +Practical consequence when authoring: spell keys against the object's **declared** +fields, and never reach for `previous` in a hook that can fire on insert or on a +`multi: true` write — that mistake used to cost you a hook that quietly never +ran, and now costs you every write the hook is attached to. #### `onError` — Error Handling