Skip to content

EPBDS-16211: CLAUDE.md says how the engine keeps a field's props stable, and how to find a render's cost - #215

Merged
AlexSamBY merged 4 commits into
masterfrom
stability-docs
Oct 12, 2026
Merged

AlexSamBY merged 4 commits into
masterfrom
stability-docs

Conversation

@AlexSamBY

Copy link
Copy Markdown
Member

What

CLAUDE.md gains a Render stability section, after the engine's mapping. It says:

  • Why props must be kept. Every field's host is memoised, and the engine builds a field's props on every render, in the mapper and in the transforms (which include the cell renderers). A function, an array or an object built there anew renders the field again, and every field like it in the document.
  • What keeps them. keptVerify, boundedValidator, keptHandler, keptMappedOptions, BuiltCell, the meta kept with metaStateReads.ts, and formData.ts's lastMerges and ValidatingForm. A new wrapper, mapping or copy made for a field's prop must be kept the same way.
  • What pins it. The src/library/__tests__/*-budget.test.js contracts count the renders a mount, an edit and a blur cost.
  • How to find a cost. Count calls through jest.mock before naming a cause. A timing with and without a feature names none, as EPBDS-16211: a period table checked by notWithinRange no longer slows down with its rows #209 found.

No code changes.

Base

This is built on #214, since it names metaStateReads.ts, which #214 adds. It therefore targets #214's branch, and its diff is this section alone. Once #214 is merged, GitHub retargets it to master, and I will bring it up to date with master before it is merged.

Gates

Documentation only. Nothing reads CLAUDE.md: in scripts, src and e2e, two comments name it. git diff --check is clean. The seven *-budget.test.js files the section names exist on this branch. The gates of the code under it are those of #214.

🤖 Generated with Claude Code

…r renders every field again

A document built its whole meta anew for every new state (`meta` in rules.tsx, keyed on the state
object). Two writes made that common: a named Select with no onChange of its own writes its pick into
the state (toOpenLConfig gives it `setState,<name>`), and the form layer writes canSave there on the
first edit. Each built the meta anew, so every Select, and every field taking an object or a function,
rendered again, and every table's cells were built anew.

The meta is now kept while the new state holds what the build read from the previous one
(metaStateReads.ts). The build reads the state in three ways only: the `{...}` templates in names,
resolved against the instance ({state.x} reads state.x, {data.x} the data); the currency; and whether
a Select's state is empty yet, to seed it (initSelectStatesFromData). So it is kept while the state
holds the same meta and data slices, the same currency, the same value under each key a template
reads, and the same emptiness under each key a Select seeds. A template reading anything else of the
instance, or the whole state, keeps the old rule. FIELD.FUNC is read only by the build, into each
meta's own copy, so fewer builds bind nothing differently.

rules.meta-cache.test.js pinned "every new state is a new meta"; it now pins that a state the meta
did not read keeps its handlers, and one it read builds anew with new handlers. meta-rebuild-budget
pins it on the selectCascading example: a product pick (read by nothing) builds nothing and renders
one Select (was 1 build, both Selects); a category pick, read by {state.categoryX,0}, builds the meta
and the product list follows. Two budgets record their mount counts halving (one build at mount, not
two) and the first edit rendering no Select. Measured in jsdom on React 18 with a form of 101 Selects:
builds/Select renders at mount 2/202 -> 1/101, first edit 1/101 -> 0/0, a pick 1/101 -> 0/1.
…eta rebuilds reached

Two instances of one class are equal only when they are the same one. Nothing tested that branch
directly: the document's meta, built anew for every new state, reached it, and lodash-lite.ts's
per-file coverage threshold rested on it. With the meta kept across a state it does not read, the
branch went unreached and the threshold failed (95.66 % statements against 96 %).
…le, and how to find a render's cost

A Render stability section, after the engine's mapping: every field's host is memoised, so a
function, array or object the mapper or the transforms build anew for a field's prop renders it, and
every field like it, again. It lists what keeps them (keptVerify, boundedValidator, keptHandler,
keptMappedOptions, BuiltCell, the meta kept with metaStateReads.ts, formData's lastMerges and
ValidatingForm), the *-budget contracts that count the renders, and the rule to count calls before
naming a cost's cause.
Base automatically changed from meta-rebuilds to master October 12, 2026 16:12
@AlexSamBY
AlexSamBY merged commit 5cb6bf6 into master Oct 12, 2026
7 checks passed
@AlexSamBY
AlexSamBY deleted the stability-docs branch October 12, 2026 16:13
AlexSamBY added a commit that referenced this pull request Oct 13, 2026
## What

Each push to master gets a CI run of its own. A push to master now has a
concurrency group named by its commit (`github.sha`). A pull request's
group is still its ref, and its newer push still cancels the run before
it.

## Why #205 was not enough

#205 stopped a push to master from cancelling the run in progress:
`cancel-in-progress` is on for pull requests only. But a group still
holds at most one running run and one pending run. GitHub cancels the
pending one when another is queued, whatever `cancel-in-progress` says.
From [GitHub's
documentation](https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency):
"any existing `pending` job or workflow in the same concurrency group
will be canceled".

It happened on 2026-10-12, when #214, #215 and #216 merged within two
minutes:

| commit | its run was created | what happened to it |
|---|---|---|
| 1b16e4d (#214) | 16:12:35 | ran until 16:18:20, success |
| 5cb6bf6 (#215) | 16:13:29 | pending behind it; cancelled at 16:14:02
with no job started |
| 1710577 (#216) | 16:14:01 | success |

5cb6bf6 is the case #205 set out to prevent: a master commit with no
finished run.

## The change

- **`.github/workflows/ci.yml`:** the group is `ci-${{ github.workflow
}}-${{ github.event_name == 'push' && github.sha || github.ref }}`.
Master's runs no longer share a group, so none waits and none is
cancelled; merges in quick succession run in parallel. The comment says
why one group for the branch was not enough.
- **Why not `queue: max`:** it keeps up to 100 pending runs per group,
but GitHub's documentation says it cannot be combined with
`cancel-in-progress: true`, which pull requests use.
- **The changelog:** its entry said "CI cancels a run only when a newer
push to the same pull request supersedes it". It now says that each
master commit gets a run of its own.

The same fix for release/v0.34's `ci-0.34.yml` is #218.

## Verified

- The workflow parses (js-yaml).
- For a push, the group comes out as `ci-CI-<sha>`; for a pull request,
as `ci-CI-refs/pull/<n>/merge`. `&&` and `||` return one of their
operands, the common way to pick a value in a workflow expression. This
PR's own run evaluates the expression, so a syntax error would have kept
it from starting.
- This PR's own run uses the pull-request group, which is unchanged. The
push groups show on the next merges in quick succession: each commit
should get a finished run.

## Gates

All 22 gates and the manifest contract exit 0 on HEAD, on Node 24.14.0.

- The suite has 2875 tests. All pass on React 18 and 19.
- On React 16.14 and 17, 2871 pass and #207's 4 transition tests are
skipped, as before.
- In the browser, the demo's 62 tests pass in Chromium on React 18 and
19, and so do the packed tarball's on React 16.14 to 19.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant