Repository navigation
Commit 8cfe1f9
fix(lint): os validate, os build and the object save door refuse a validations[] predicate that reads parent (#24763)
Closes #24720
Clause-②: no
## What changes
A `validations[]` rule's predicates are evaluated by ObjectQL in
`ruleScope(record, previous)`
(`packages/objectql/src/validation/rule-validator.ts`, `checkPredicate`
and `checkConditional`). That scope binds `record` and `previous`, and
no master-detail header. The header is bound as `parent` only for a
field's own `readonlyWhen` / `requiredWhen`, on an object with exactly
one `master_detail`. A rule that read `parent` (for example
`parent.status == 'paid'`) passed `os validate` silently, because
`parent` is a `SCOPE_ROOTS` member and the bare-reference check stays
silent on it. At runtime every write the rule judged was refused as
unevaluable (`Unknown variable: parent`), the seed's rows at boot
included.
- **A root verdict on the rule's own predicates, at `warning` in 18.x.**
`validationRuleParentIssue` in
`packages/lint/src/validate-expressions.ts` reports a predicate that
reads `parent`. It covers a `script` / `cross_field` rule's `condition`,
a `conditional` rule's `when`, and the same two slots on every rule
nested in its `then` / `otherwise`. "Reads `parent`" is decided from the
parsed AST (`readsParentRoot`, the same reader the field-rule verdict
uses), so `record.parent_code` and the string `'parent'` are not judged.
- **Location and prescription.** The finding is located where `check()`
locates the same predicate (`object 'NAME' · validation 'RULE'`, its
`when`, or the nested rule's label). The message names the slot, the
rule and the root, and points at the field-rule slots where `parent` IS
bound (`readonlyWhen` / `requiredWhen`), with the
denormalise-onto-the-detail remedy for any other check. Its rule id is
the existing `expression-invalid`.
- **Both doors, neither refuses.** `os validate` / `os build` and the
object save door run the same pass (`runStackExpressionPasses`). A
warning fails no command, and the door admits the write and lists the
finding among its `advisories`.
- **The refusal is deferred to 19.0, per the standing post-GA ruling
「甲」.** A narrowing that no ADR-0087 conversion can carry ships only its
loud half in 18.x, and no conversion can rewrite a predicate that reads
`parent`. The same finding at `error` is planned for 19.0. The docblock
and the message say so, and the ⛔ withdrawal note stays: the verdict
goes in the same change that binds `parent` for a `validations[]` rule.
- **The runtime is unchanged.**
## Evidence
All readings at the PR head on top of `origin/main` `a7199ef974`.
- **New suite**
`packages/lint/src/validate-expressions.validation-rule-parent.test.ts`:
12 tests pass. It asserts `warning` on the `script`, `cross_field` and
`conditional` (`when` and nested `then`) slots, and on objects with 0, 1
and 2 masters. For `os validate` and `os build` it asserts one `warning`
finding and no `error` finding. At the object door it asserts that
`errors` is empty, that the finding is in `advisories`, and that the
door finding equals the build finding. Controls: `parent` on a field's
own `readonlyWhen` / `requiredWhen` gives no finding at the build or at
the door, and `record` / `previous` reads, `record.parent_code` and the
literal `'parent'` stay clean.
- **Ablations** through `scripts/ablation-replace.mjs`; each landed on
disk and was restored to blob == HEAD with `git diff HEAD` empty. Making
the verdict return `null` turned 9 of 12 tests red, and the 3 CONTROL
tests stayed green. Setting the severity back to `error` turned 7 of 12
red: the warning assertions bite.
- **Public door, measured.** On the stock showcase, with a temporary
rule `{ type: 'script', name: 'probe_paid_lock', events: ['insert'],
condition: "parent.status == 'paid'" }` on `showcase_invoice_line`,
`pnpm --filter @objectstack/example-showcase validate` exits 0
(`Validation passed`). It prints the finding as a warning: `rule:
expression-invalid at object 'showcase_invoice_line' · validation
'probe_paid_lock'`. The probe was applied and restored by
`ablation-replace.mjs`.
- **Whole package:** `pnpm --filter @objectstack/lint test` passed 151
files and 7000 tests. `pnpm --filter @objectstack/lint typecheck`
passed.
- **Changeset gates:** `check-adr-0087-registration` reports "this PR
adds no declared-breaking changeset". `check-changeset-no-major`,
`check-empty-changeset`, `check:changeset-gate-self-tests` and
`check:spec-changes` are green.
- **Corpus:** `git grep` over `examples/**` and the package objects
finds `parent.` only in field `readonlyWhen` slots and in no
`validations[]` rule.
## Acceptance notes
- The verdict judges a rule as written, the way the rest of the
expression rule does. So the warning also covers a rule with `active:
false`, a rule on `events` a stack never fires, and a `conditional`
branch whose `when` is never true. The 19.0 refusal will cover them too,
which is why it waits for a major.
- The changeset is a `patch` with `Clause-②: no`: a warning narrows
nothing. The earlier breaking-`minor` declaration was withdrawn under
「甲」.
- The object door is pinned in-package through
`runRuntimeAuthoringRules({ type: 'object' })`. There is no
`saveMetaItem` pin in `packages/metadata-protocol`, because that package
is outside this card's file surface.
- No rule registry or docs row is needed: the verdict uses the existing
`expression-invalid` rule, and `content/docs/data-modeling/fields.mdx`
already states that `parent` is bound for the field-rule slots only.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: objectstack-fleet[bot] <332303061+objectstack-fleet[bot]@users.noreply.github.com>1 parent 3d8960c commit 8cfe1f9
3 files changed
Lines changed: 315 additions & 2 deletions
File tree
- .changeset
- packages/lint/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
552 | 552 | | |
553 | 553 | | |
554 | 554 | | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
555 | 559 | | |
556 | 560 | | |
557 | 561 | | |
| |||
572 | 576 | | |
573 | 577 | | |
574 | 578 | | |
575 | | - | |
| 579 | + | |
576 | 580 | | |
577 | | - | |
| 581 | + | |
578 | 582 | | |
579 | 583 | | |
580 | 584 | | |
| |||
1710 | 1714 | | |
1711 | 1715 | | |
1712 | 1716 | | |
| 1717 | + | |
| 1718 | + | |
| 1719 | + | |
| 1720 | + | |
| 1721 | + | |
| 1722 | + | |
| 1723 | + | |
| 1724 | + | |
| 1725 | + | |
| 1726 | + | |
| 1727 | + | |
| 1728 | + | |
| 1729 | + | |
| 1730 | + | |
| 1731 | + | |
| 1732 | + | |
| 1733 | + | |
| 1734 | + | |
| 1735 | + | |
| 1736 | + | |
| 1737 | + | |
| 1738 | + | |
| 1739 | + | |
| 1740 | + | |
| 1741 | + | |
| 1742 | + | |
| 1743 | + | |
| 1744 | + | |
| 1745 | + | |
| 1746 | + | |
| 1747 | + | |
| 1748 | + | |
| 1749 | + | |
| 1750 | + | |
| 1751 | + | |
| 1752 | + | |
| 1753 | + | |
| 1754 | + | |
| 1755 | + | |
| 1756 | + | |
| 1757 | + | |
| 1758 | + | |
| 1759 | + | |
| 1760 | + | |
| 1761 | + | |
| 1762 | + | |
| 1763 | + | |
| 1764 | + | |
| 1765 | + | |
| 1766 | + | |
| 1767 | + | |
| 1768 | + | |
| 1769 | + | |
| 1770 | + | |
| 1771 | + | |
| 1772 | + | |
| 1773 | + | |
| 1774 | + | |
| 1775 | + | |
| 1776 | + | |
| 1777 | + | |
| 1778 | + | |
| 1779 | + | |
| 1780 | + | |
| 1781 | + | |
| 1782 | + | |
| 1783 | + | |
1713 | 1784 | | |
1714 | 1785 | | |
1715 | 1786 | | |
| |||
2848 | 2919 | | |
2849 | 2920 | | |
2850 | 2921 | | |
| 2922 | + | |
| 2923 | + | |
| 2924 | + | |
| 2925 | + | |
| 2926 | + | |
| 2927 | + | |
| 2928 | + | |
| 2929 | + | |
| 2930 | + | |
| 2931 | + | |
| 2932 | + | |
| 2933 | + | |
| 2934 | + | |
| 2935 | + | |
| 2936 | + | |
2851 | 2937 | | |
2852 | 2938 | | |
2853 | 2939 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
0 commit comments