Repository navigation
Commit d753744
Fixes #20400
Clause-②: no
## What was wrong
`validateComponentProps` does not report the props schema's required
`object` when the component carries a `dataSource.object` binding. Two
docblocks scope that waiver to absence: `DATASOURCE_SUPPLIED_PROP` ("the
one prop whose absence this rule does NOT report") and
`suppliedByDataSource` ("is this issue 'the required `object` prop is
missing'"). The code matched on the issue's path alone. Any issue at
exactly `['object']` was waived whenever `dataSource.object` was a
non-empty string, without looking at `properties.object` or at the
issue. So a present but wrong `object` beside a binding was silenced,
and the same value without a binding was reported.
## Before and after (measured)
Each row is one component in a one-page stack, run through
`validateComponentProps` from source with `tsx`. Before is `origin/main`
`6e3e5462c6`. After is this branch at `e994035362`.
| component | before | after |
|---|---|---|
| `element:number`, `dataSource: { object: 'contact' }`, `properties: {
object: 7, aggregate: 'count' }` | **0 findings** | 1
`component-props-invalid` at `properties.object` |
| the same, with no `dataSource` (control) | 1 `component-props-invalid`
at `properties.object` | the same, unchanged |
| `object: null` beside the binding | **0 findings** | 1
`component-props-invalid` at `properties.object` |
| no `object` key beside the binding | 0 | 0 (still waived) |
| `object: undefined` beside the binding | 0 | 0 (still waived) |
| the existing picker pin (`element:record_picker`, binding,
`labelField` only) | 0 | 0 |
## The change
The landing site is the expected one: the body of `suppliedByDataSource`
in `packages/lint/src/validate-component-props.ts`, plus its test file
and a changeset. After the existing path and binding checks, the
function reads absence off the component. It waives only when
`properties.object` is not present or is `undefined`, which is triage's
definition of missing. Any other issue at that path passes through as
the props row raised it. Both docblocks are unchanged: their contract
sentences already say this, and the renderer sentence is out of scope
(see Acceptance notes). The call site is unchanged.
## Pins (`packages/lint/src/validate-component-props.test.ts`)
- The existing `does not report the required object prop when dataSource
supplies it` is unchanged and green.
- New, run for `object: 7` and for `object: null`: the same
`element:number` bag is judged beside `dataSource: { object: 'contact'
}` and without it. Each is reported as `[COMPONENT_PROPS_INVALID,
'...properties.object']`, and the two finding lists are deep-equal. The
assertions are on rule id, path and equality, never on message text.
- New: `object: undefined` beside the binding reports nothing. The same
bag without the binding reports `properties.object`, so the silence
comes from the waiver and not from the row accepting `undefined`.
## Reverse verification
The fix was committed first (`e994035362`). The one predicate line was
then mutated back to the old behaviour through
`scripts/ablation-replace.mjs` in WRAP mode, with an absolute-path
`trap` restore around it. The line `return
props?.[DATASOURCE_SUPPLIED_PROP] === undefined;` became `return true;`,
which equals the old `strName(dataSource?.object) !== undefined` once
the new early return has run.
- On-disk proof: anchor count 1 → 0, mutant count 0 → 1, blob
`f4793c5782` → `52df0d1033`.
- The run: `Tests 2 failed | 46 passed (48)`. The two failures are the
`object: 7` and `object: null` pins (`expected [] to deeply equal [ [
'component-props-invalid', …(1) ] ]`). The `undefined` pin and the
existing pin stayed green, as expected, because the old code waived
both.
- The restore: blob after restore `f4793c5782` equals the HEAD blob,
`git diff HEAD` is 0 bytes, and `git status --porcelain` is empty.
- The test imports the rule by relative path
(`./validate-component-props.js`, resolved to `src/`), so no `dist/` leg
applies.
## Verification (all at `e994035362`)
- Build: `pnpm --filter '@objectstack/lint^...' build` through
`os-verify-lock.sh`, `VERDICT command-exit 0`.
- `pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2
src/validate-component-props.test.ts`: `Tests 48 passed (48)`.
- `pnpm --filter @objectstack/lint test`: `Test Files 115 passed (115)`,
`Tests 5329 passed | 5 skipped (5334)`.
- `pnpm --filter @objectstack/lint typecheck`: exit 0. `tsc --noEmit` is
clean. `check:test-typecheck` is OK with the ledger unchanged at 2
files, 6 errors and 2 signatures. `--listFiles` shows the edited test
file is in the `tsconfig.test.json` program (1 hit) and is not a
ledgered file.
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` derived 60 commands. Each was run with its exit code
captured before any pipe, and the results were reconciled with `--ran`:
`60 derived famil(ies) accounted for — 58 run, 2 NOT-MEASURED (2 DERIVED
from a recorded exit 3)`.
- 58 exited 0. That count includes `check:doc-authoring`,
`check:nul-bytes`, `check:adr-0087-registration --base origin/main`,
`check:changeset-no-major --base origin/main`, `check:empty-changeset`
and `check:changeset-gate-self-tests`.
- `check:docs-transcript-drift` and `check:lean-entry-closure` first
answered `PREREQUISITE NOT MET`. They exited 0 after `pnpm --filter
@objectstack/lint build` and `turbo run build
--filter=@objectstack/objectql`.
- NOT MEASURED: `check:dual-build-cjs-loads` and
`check:type-check-debt`. Reason: both refuse without the whole workspace
built (84 and 28 packages have no `dist/`). That build is CI's (`Build
Core`, `Lint & Repo Gates`), not a local targeted run.
- The four roster gates the derivation flags for directories this diff
touches all exited 0: `check-changeset-fixed`, `check:authz-resolver`,
`check:error-code-casing` and `check:filter-alias-parity`.
- Published surface: `@objectstack/lint` ships `files: ["dist",
"README.md", "CHANGELOG.md"]`. After a build, the new line (built
spelling `return props?.[DATASOURCE_SUPPLIED_PROP] === void 0;`) appears
once in each of `dist/index.js`, `dist/index.cjs`, `dist/runtime.js` and
`dist/runtime.cjs`. The positive control `function
suppliedByDataSource(issue, component)` also appears once in each. So
this publishes, and it takes a `patch` changeset.
- Diff size: +84 / -1 across 3 files.
## Acceptance notes
- **`null` is reported.** Triage defined missing as "no key, or
`undefined`". `object: null` is a present value that `z.string()`
rejects, so it now reaches the author, like `object: 7`. It is pinned
beside `7` so the boundary stays explicit.
- **The renderer sentence is untouched.** `DATASOURCE_SUPPLIED_PROP`'s
docblock says objectui's element renderers "read it FIRST". That is
still false for `element:number` until objectui#10909 lands.
objectui#10909 is not addressed here: its fix is that renderer, per
triage's out-of-scope list.
- **No per-type table.** Restricting the waiver to types whose renderer
honours the binding would be a new per-type table on this rule. Triage
ruled it out of this card.
- **The two readers now agree on this input.** objectui's mirror of this
waiver (PR objectui#10908) already refuses `object: 7` beside a binding,
following the docblock. After this change, this gate refuses it as well.
- **`main` moved after the base.** It gained `7fa3e3e07c` and
`8cdbe0c6e5`, which touch `packages/rest` and
`packages/metadata-protocol` only. Neither touches `packages/lint` or
`packages/spec`, so the branch was not merged forward and the merge
queue arbitrates.
- **PR #20420 is text-disjoint.** It adds `ComponentPropsMap` rows that
this rule reads, but it does not touch this file.
---
_Generated by [Claude
Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent bea6d2e commit d753744
3 files changed
Lines changed: 86 additions & 1 deletion
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 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
231 | 231 | | |
232 | 232 | | |
233 | 233 | | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
234 | 286 | | |
235 | 287 | | |
236 | 288 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
175 | 175 | | |
176 | 176 | | |
177 | 177 | | |
178 | | - | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
179 | 185 | | |
180 | 186 | | |
181 | 187 | | |
| |||
0 commit comments