Skip to content

Commit fe41dc7

Browse files
test(plugin-form): pin object-master-detail-form dataSource, details and fields members (objectui#8071 slice 18) (#11113)
Part of #8071 Clause-②: no — test pins only; no published contract, key, export or flag changes 「lands after objectui#11086: merge main, then re-measure the three pins on 17.5.0」 > Seat amendment (objectui#8071 `5892096260`): the 「lands after objectui#11086」 constraint is relaxed on evidence. `git merge-tree` against #11086's head auto-merges the ledger, no pinned read site is in #11086's file list, and the pins survive 17.5.0 per its tarball. This PR lands on its own green CI; whichever of the two lands second runs these pins on its own tree. ## What this does Slice 18 of objectui#8071. It pins the last three `AWAITING_A_PIN` keys, all on one block: `object-master-detail-form.dataSource`, `.details` and `.fields`. In the same commit it deletes their `MEMBER_PIN_EXEMPTIONS` entries and lowers `MEMBER_PIN_EXEMPTION_CEILING` from **4 to 1**. That closes the block. `AWAITING_A_PIN` had no entries left after this, and `apps/console/tsconfig.json` sets `noUnusedLocals`, so the constant is deleted too. A comment where it stood says why. `record:related_list.actions` (`NO_READ_SITE_TO_PIN`) is **not touched**. It holds the ceiling's last unit. | key | pin file | new or promoted | |---|---|---| | `dataSource` | `packages/plugin-form/src/MasterDetailForm.elementDataSource.test.tsx` | promoted (objectstack#7121 file), read end to end, then **grown by 5 rows** | | `details` | `packages/plugin-form/src/__tests__/masterDetailDetailsMembers-8071.test.tsx` | **new**, 8 rows | | `fields` | `packages/plugin-form/src/__tests__/topLevelFieldsWarnCoverage-8847.test.tsx` | promoted (objectui#8847 file), read end to end, then **grown by 5 rows** | ## Why `Part of`, not a closing keyword The card's own text defines the work as turning **every** listed key into a pin: 「本卡的工作就是把它们一个个变成 pin」. `record:related_list.actions` is on that list. It has no read site, so no pin can meet the card's criterion. Its remedy is the enforce-or-remove decision the spec owns, and until that decision lands the entry stays and the card's population is not closed. ⚠️ There is a second reason, measured today. The `NO_READ_SITE_TO_PIN` constant says the entry "belongs to" objectui#7300, "which is open". **objectui#7300 is closed as `not_planned`** (REST read on 2026-09-29). So #8071 is now the only open card whose ledger text names this last entry, and closing it would leave the entry with no live tracker. This PR does not edit that constant (it is out of scope for this dispatch). The stale sentence is listed under Acceptance notes. ## H2: the real read site for each key ⚠️ The read sites are in **`packages/plugin-form`**. None is in `packages/plugin-grid`, which is what the claim's file surface names. See Deviation below. | key | where the renderer reads it | what reads each member | |---|---|---| | `dataSource` | `MasterDetailFormRenderer` (plugin-form `index.tsx`) wraps `ElementDataSourceGate` with **no `mapping`**, so the default applies and only `object` lands, on `objectName`. The gate lives in `packages/react/src/element-data-source/`. | `object` becomes `objectName`, unconditionally. From there `MasterDetailForm` uses it three ways: the parent `ObjectForm`, `deriveDetail`'s FK lookup, and the batch's parent leg. `view` is resolved: an unresolvable view reports, a resolving one contributes nothing. `filter` is not mapped, but beside a `view` it is **merged**, and a malformed one refuses the block. `sort` and `limit` are not read at all. | | `details` | `MasterDetailForm` itself: `rawDetails = schema.details`, the derive effect, `MasterDetailLines`, `sendBatch`, `readLines`. | `title` becomes the section heading. `columns` go to the grid. `sortField`, `minRows`, `maxRows`, `addLabel` and `amountField` reach the grid **renamed**, as `sort_field`, `min_rows`, `max_rows`, `add_label` and `total_field`. `inlineMode` and `formFields` choose the display mode and the row form. `childObject` and `relationshipField` address the batch creates and the edit-mode read. `totalField` receives the rollup on the parent leg. | | `fields` | **Not read by `MasterDetailForm`.** The `parentSchema` memo copies `fields: schema.fields` onto an `object-form`-shaped node rendered through a directly imported `ObjectForm`, which falls through to `SimpleObjectForm`. That is the real read site. | Members are bare parent field names in authored order, and `{ name }` is tolerated. The `FormFieldSchema` object form resolves to no name and draws the route-1 warning. | ## H3: will the pins survive `@objectstack/spec` 17.5.0? Yes, per the tarball I read the 17.5.0 tarball read-only and diffed it against the installed 17.4.0: - `ObjectMasterDetailFormPropsSchema` is **identical** for `fields` and `details`. Both are `z.array(z.unknown())` with the same descriptions, so every member parses on both versions. No pin row parses them anyway, because the read site is the whole member contract. - `dataSource` (`ElementDataSourceSchema`) **does change**: `filter` moves from `FilterConditionSchema` (a MongoDB-style record) to `z.array(ViewFilterRuleSchema)`. No pin row parses the binding. The new rows spell `filter` in the 17.5.0 rule-array form, and the one row that needs a malformed filter uses a rule the **runtime** lowering refuses: an array value on `equals`, objectui#8557. - PR #11086's file list touches **none** of the read sites these pins exercise: `MasterDetailForm.tsx`, `ObjectForm.tsx`, plugin-form `index.tsx`, `ElementDataSourceGate.tsx`, `element-data-source.ts`, `filter-converter.ts`, `deriveMasterDetail.ts`, `masterDetailTx.ts`. It does touch `GridField.tsx`, but only the currency and percent scale clamp, which these pins do not exercise. - Trial merge with `git merge-tree` of this branch against PR #11086's head `174c4a7`: the ledger **auto-merges**, because #11086's hunks and this branch's hunks do not overlap. The only conflict is `packages/fields/src/index.tsx`, which this branch does not touch. It is between #11086 and `main`. - ⛔ NOT MEASURED: the pins actually **run** on 17.5.0. That happens after #11086 lands, per the line at the top. ## The ledger, re-derived by TypeScript AST on both trees (not taken from the order) | | base `88fbd79` | head | |---|---:|---:| | `MEMBER_PIN_EXEMPTIONS` | 4 | **1** | | `MEMBER_PINS` | 98 | **101** | | `MEMBER_PIN_EXEMPTION_CEILING` | 4 | **1** | | `NEWLY_JUDGED_UNPINNED_MEMBERS` | `[]` | `[]` | | non-property members in either map | `[]` | `[]` | The sets match exactly. The three keys that left the exemptions are the three that entered the pins. Nothing entered the exemptions and nothing left the pins. What remains is `record:related_list.actions = NO_READ_SITE_TO_PIN`, and 1 equals the ceiling. ## Tests (head `ae719c4`) - The three pin files together with the ledger: 4 files, **223 passed**. - `pnpm exec vitest run packages/plugin-form/`: **144 files, 1719 passed, 1 skipped**. - `pnpm exec vitest run apps/console/`: **131 files, 1475 passed**. The ledger file alone had 199 passed. - Type-check. plugin-form: `turbo run type-check` gave 13/13 successful. `--listFiles` shows the `tsconfig.test.json` leg lists all three pin files, while the main leg lists 0 of them (it excludes tests). Console: `tsc --noEmit && tsc -b tsconfig.node.json` exit 0, and `--listFiles` includes the ledger. - Lint, narrowed to the changed files with the narrowing measured. `eslint --no-inline-config --format json` over the 4 changed test files reports **4 files, 0 errors, 20 warnings**, all `no-explicit-any`. `eslint.config.js` sets no `parserOptions.project` or `projectService`, so the lint is not type-aware and this diff cannot change the verdict on any untouched file. Repo-wide `pnpm lint` is left to CI. - Gates, each exit 0: `check:control-bytes`, `check:new-line-citations` (0 new), `check:test-path-roots`, `check:vi-mock-specifiers`, `check:vi-mock-inherit`, `check:vi-mock-override-shape`, `check:changeset-claims`, `check:pending-changeset-literals`, `check:unreferenced-sources`, `check-changeset-presence` (reads the empty frontmatter as the declaration), `check-changeset-no-major`, `check-changeset-fixed`, `check-changeset-overwrite`. - `check:changeset-claims` names `.changeset/8067-component-input-member-kind.md` because it mentions the ledger file. I re-read the paragraph: it is a past-tense account of the `page:header.actions` drift, and this diff does not falsify it. No correction is owed. ## Ablations Each leg ran on the committed tree through `ablation-replace.mjs`. Before any result was read, the tool proved the mutation on disk (anchor count, blob hash). Each leg restored under the tool's trap, with the restore proven by blob equal to the HEAD blob and an empty `git diff HEAD`. The vitest config aliases every `@object-ui/*` import to `src`, so the mutated source is what ran and no dist preflight applies. - **A, the gate mechanism.** I re-added `object-master-detail-form.details` as an exemption and left the ceiling untouched. Blob `e591038c` became `335d11ab`. Result: **3 failed, 196 passed**. The three are the census partition row, `carries no stale member-pin exemption`, and `only ratchets DOWN` with "expected 2 to be less than or equal to 1", which proves the ceiling moved with the list. - **B, `details`.** I deleted `min_rows: d.minRows,` from the grid object. Result: **2 failed**, rows 2 and 2b, and the other two pin files stayed green in the same run. The first run of this leg reddened row 2 only, because `toEqual` treats a key holding `undefined` as absent. Row 2b now asserts the key set too, and the leg was re-run on `ae719c4`. - **B, `dataSource`.** I changed the gate's `next[objectKey] = composed.object` to the `??=` spelling. Result: **2 failed**, the OUTRANKS row and the detail-half row, with neighbours green. The detail-half row fails at its first wait: the parent form is the old object `order`, whose only field is `code`, so the bound object's field never appears. - **B, `fields`.** I deleted `fields: schema.fields,` from the `parentSchema` memo. Result: **5 failed**, every row in the file that authors a non-default `fields`, including the pre-existing route-1 warning row. The bare-name control and the no-`fields` control stayed green, as did both neighbour files. ## Acceptance notes (not changed here) - ⚠️ **Handed back as a finding (class b): the `fields` description versus the parent leg.** The `object-master-detail-form` `fields` registration calls the key the parent pool for "values, create defaults and the submitted set". Measured: a seeded parent field that `fields` does not list is **still written** on the batch's parent leg. With `fields: ['status']` and `initialValues: { status, memo }`, the parent leg carries both. The pin records this as behaviour. Fixing it means changing either the renderer or the declaration, and this card writes pins only. - The `NO_READ_SITE_TO_PIN` text ("objectui#7300 … is open") is stale: #7300 is closed as `not_planned`. Out of scope for this dispatch. - All four `fields` registration descriptions (`object-form`, `form`, `embeddable-form`, `object-master-detail-form`) still say the `FormFieldSchema` object member "is silently skipped". Since objectui#8738 route 1 it draws a named warning. Wording only; no carrier. - The spec's `details` description names five of the twelve members the renderer reads (`title`, `childObject`, `addLabel`, `columns`, `relationshipField`). That is this card's own subject, now pinned. - `masterDetailFormTypeVocabulary.test.tsx` still carries six cross-file `ObjectForm.tsx` line addresses, which AGENTS.md #11 bans. This diff does not touch that file, so the opportunistic-repair rule does not apply. With this block closed, no later slice of this card will touch it either. No carrier. - From reading the sources only, not measured at any entry point: the registration declares `details` as `required: true`, the spec declares it `.optional()`, and the renderer tolerates it missing (`schema.details || []`). - I also corrected a stale reason in the promoted `topLevelFieldsWarnCoverage-8847.test.tsx` docblock ("`fields` is documented as ignored once `sections` is given"), which objectui#9884 had already falsified. The change is limited to that docblock paragraph. ## Deviation: the claim's file surface The claim lists `packages/plugin-grid/src/__tests__/` "only if a pin needs a renderer-side fixture there". The dispatch's H2 flagged that entry as inherited and possibly wrong. Measured: this block renders from `packages/plugin-form`, so the three pin files live there. Nothing under `packages/plugin-grid` is touched. The claim's file surface needs amending; that belongs to the seat, and this PR posts no claim. --- _Generated by [Claude Code](https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent b24f93a commit fe41dc7

5 files changed

Lines changed: 880 additions & 39 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
---
3+
4+
Test-only change: the last three `object-master-detail-form` registry inputs,
5+
`dataSource`, `details` and `fields`, become per-block member pins. Their
6+
`MEMBER_PIN_EXEMPTIONS` entries are deleted in the same change, and
7+
`MEMBER_PIN_EXEMPTION_CEILING` follows down to the new count (objectui#8071
8+
slice 18, criterion from objectui#8068). This closes the block. The generic
9+
`AWAITING_A_PIN` reason is deleted with its last entries. The one exemption
10+
left, `record:related_list.actions`, carries a reason of its own and is not
11+
touched.
12+
13+
The pins constrain what the RENDERER reads:
14+
15+
- `details`: a member is a detail-collection object. Five of its members reach
16+
the line grid through one hand-written object, four of them renamed, and that
17+
object is pinned as an exact key set. The members also address the atomic
18+
batch (each line a create on `childObject`, linked by `relationshipField`,
19+
with `totalField` summing `amountField` onto the parent leg) and the edit-mode
20+
line read. With only `childObject` authored, the FK and the columns are derived
21+
from the child object.
22+
- `dataSource`: the binding's `object` outranks a flat `objectName` and reaches
23+
the detail half as well: the derived child FK and the batch's parent leg both
24+
read it. Its `filter`, `sort` and `limit` never reach the child read. Beside a
25+
named view, a malformed binding `filter` withholds the whole form, so an
26+
unmapped member is still read.
27+
- `fields`: a member is a bare parent field name drawn in authored order. A name
28+
the parent does not declare is dropped, including a detail column name, and
29+
`{ name }` is the same member. One member fact is recorded as behaviour and
30+
handed back as a finding: the key bounds what is drawn, not what the parent
31+
leg writes.
32+
33+
No published behaviour changes. Every changed file is a test file or the
34+
console's test ledger, and no package entry imports any of them. No package is
35+
released by this change.
36+
37+
Refs objectui#8071, objectui#8068.

‎apps/console/src/__tests__/registry-inputs-spec-parity.test.ts‎

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2545,6 +2545,18 @@ const MEMBER_PINS: Record<string, MemberPin> = {
25452545
file: 'packages/plugin-form/src/MasterDetailForm.i18nLabels.test.tsx',
25462546
pins: 'The I18nLabel object arm, resolved at the read site: through the real `SchemaRenderer`, a `properties.cancelText` map under a `zh` UI renders the map\'s `zh-CN` entry on the Cancel button, with `en` listed FIRST so an `en` or first-entry fallback fails the row. Before objectui#10935 the map was a raw Button child and threw "Objects are not valid as a React child". The button renders only when the host supplies `onCancel`, so the row adds one beside the document and asserts the host handler still runs. A plain-string control and the nothing-authored control (`Cancel`) stay green, and the first row asserts the mounted documents pass `safeValidateSchema`. The manifest half (a map is not a `type-mismatch`) is `masterDetailFormI18nLabelManifest.test.ts` in this directory (objectui#10935).',
25472547
},
2548+
'object-master-detail-form.dataSource': {
2549+
file: 'packages/plugin-form/src/MasterDetailForm.elementDataSource.test.tsx',
2550+
pins: 'The per-element binding\'s members on the block that writes a parent AND its children in one batch. `object` is the only key mapped (the gate\'s default mapping), asserted at the fetch it causes, and it OUTRANKS a flat `objectName` on the same node: `next[objectKey] = composed.object` is unconditional, and the `??=` spelling would keep a rebound form on the old parent with no diagnostic. ⭐ The row only this block can make: the bound object reaches the DETAIL half. `deriveDetail` picks a child\'s FK by the parent object\'s name and the atomic batch names the parent on its first leg, measured on one child object that carries a lookup to BOTH candidate parents, so the derived FK and the parent leg together say which name won. `view` / `filter` / `sort` / `limit` are NOT mapped: a resolving view adds no parent collection query, an unresolvable one REPORTS, and in `edit` mode each child line read stays `{ $filter: { FK: recordId }, $top: 500 }` whatever the binding carries. ⚠️ Unmapped is not unread: beside a named `view` the gate merges the binding\'s `filter` with the view\'s, so a malformed one WITHHOLDS the whole form behind the malformed-filter notice, with a well-formed one as that row\'s control. A binding-free form is the control for the negatives. `dataSource` is absent from `ComponentPropsMap[\'object-master-detail-form\']` (it is the injected `ELEMENT_DATA_SOURCE_INPUT`), so nothing declared constrains the precedence. Pre-existing file (objectstack#7121), promoted after being read end to end and GROWN by five rows, four facts and a control (objectui#8071 slice 18).',
2551+
},
2552+
'object-master-detail-form.details': {
2553+
file: 'packages/plugin-form/src/__tests__/masterDetailDetailsMembers-8071.test.tsx',
2554+
pins: 'Members are detail-collection OBJECTS (`MasterDetailDetailConfig`), and unlike this block\'s parent keys the read site is THIS block: `MasterDetailForm` is the key\'s only reader. Each member renders as its own section in AUTHORED order, headed by `title` (fallback `Line Items`), with `columns` reaching the line grid in authored order. ⭐ The sharp row: five members reach the grid through ONE hand-written object and four are RENAMED on the way (`sortField` to `sort_field`, `minRows` to `min_rows`, `maxRows` to `max_rows`, `addLabel` to `add_label`, `amountField` to `total_field`). That object is pinned as a SORTED KEY SET of exactly six, with two off-list members (one in the grid\'s own snake_case spelling) asserted not forwarded and an all-empty control beside it. `inlineMode: \'form\'` makes a list whose Add opens the full form (labelled `Add` when unset), and in grid mode `formFields` wider than `columns` offers the row form; each arm is the other\'s control. `childObject` and `relationshipField` address the atomic batch (each line a create on the child, linked by `{ $ref: 0 }`) and the edit-mode read (`{ $filter: { FK: recordId }, $top: 500 }` per collection). `totalField` receives the sum of the lines\' `amountField` on the PARENT leg, and a collection without one puts nothing there. With `{ childObject }` alone the FK and the columns are DERIVED from the child object, the one member behaviour the spec\'s description promises, and the batch writes the derived FK. `details: []` is the non-vacuity control: no section, no grid, a one-leg batch. The line grid is the REAL `LineItemsField`, wrapped only to record the props it is handed. The spec row is `z.array(z.unknown())`, whose description names five of the twelve members the renderer reads, and the registration declares no `of`, so the read site is the whole member contract. New file (objectui#8071 slice 18).',
2555+
},
2556+
'object-master-detail-form.fields': {
2557+
file: 'packages/plugin-form/src/__tests__/topLevelFieldsWarnCoverage-8847.test.tsx',
2558+
pins: 'Members are bare PARENT field names drawn in AUTHORED order, against a no-`fields` control whose order is the object\'s. A name the parent does not declare is dropped rather than drawn as an untyped stub, measured with a DETAIL column name because one node here declares two field vocabularies, while the line grid keeps that column. `{ name }` is the same member as the bare name. The two pre-existing rows carry the sharpest member fact: the spec `FormFieldSchema` object that is legal in `sections[].fields` resolves to no name here, draws the named route-1 warning (objectui#8738, objectui#8847) and does not render, with a bare name as the firing control. ⚠️ Pinned as BEHAVIOUR and handed back: the key bounds what is DRAWN, not what the parent leg WRITES. A seeded field it does not list is still written, while the registration calls this key the parent pool for "the submitted set". The intersection with `sections` belongs to the `object-master-detail-form.sections` pin and is not re-asserted. `MasterDetailForm` reads none of this itself: the `parentSchema` memo copies the key onto an `object-form`-shaped node rendered through a DIRECTLY imported `<ObjectForm>`, the hand-written carrier that is why the pin is taken at this block. The spec row is `z.array(z.unknown())` and the registration declares no `of`, so the read site is the whole member contract. Pre-existing file (objectui#8847), promoted after being read end to end and GROWN by five rows (objectui#8071 slice 18).',
2559+
},
25482560
'object-master-detail-form.initialData': {
25492561
file: 'packages/plugin-form/src/__tests__/masterDetailInitialMembers-8071.test.tsx',
25502562
pins: 'Members are PARENT FIELD NAMES and each member value is that control\'s opening value \u2014 pinned in ONE file with `initialValues`, because this block\'s own registration declares them a pair ("Alternate spelling of `initialValues` the renderer also reads", with new schemas told to prefer `initialValues`) and a pair pinned apart leaves the PRECEDENCE stated nowhere. The precedence is PER MEMBER since objectui#9760: one shared `resolveInitialRecord(schema)` merges `{ ...initialValues, ...initialData }`, so with both authored a member `initialData` says nothing about keeps its `initialValues` value, and an EMPTY `initialData` contributes nothing rather than blanking the parent form. It used to be whole-object (`schema.initialData || schema.initialValues`, where `||` tests the object and `{}` is truthy); objectui#8071 wrote pins only, so it recorded that and handed the defect back, and the maintainer ruling on objectui#9760 repaired it \u2014 the two rows were FLIPPED there, \u26d4 not deleted. \u2b50 The two rows no sibling pin can make are the block\'s own: the seed reaches the PARENT LEG of the atomic batch and nothing else (a member naming a detail column seeds no child row and the posted batch still carries exactly one operation), and in `edit` mode with a `recordId` the fetched record REPLACES both keys wholesale, so a member the record omits opens empty rather than falling back to the seed. Neither key has a read site in this block at all: `MasterDetailForm`\'s `parentSchema` memo copies them key by key onto an `object-form`-shaped object rendered through a DIRECTLY imported `<ObjectForm>` \u2014 no `SchemaRenderer`, no registry lookup \u2014 which is exactly why the pin is taken here, since that hand-written map can drop a key while every declaration still reads correct (the failure `object-form.sections` records twice, objectui#9779 / objectui#9834). A no-keys row renders the same controls empty as the non-vacuity control. Both keys are registered `type: \'object\'` and typed `Record<string, any>`, so every object parses on both declared sides and the read site is the whole member contract. New file (objectui#8071 slice 15).',
@@ -2707,28 +2719,17 @@ const MEMBER_PINS: Record<string, MemberPin> = {
27072719
},
27082720
};
27092721

2710-
/**
2711-
* The reason every entry in `MEMBER_PIN_EXEMPTIONS` carries today.
2712-
*
2713-
* One shared constant rather than one near-copy per entry, because the reason
2714-
* really is uniform and a copy is what drifts: none of these keys has a pin, and
2715-
* writing one string per key is not the dispatched scope of the card that built
2716-
* this direction. (The measured population size lives in the string below, where
2717-
* it is attributed to the card that measured it and dated by it.)
2718-
* objectui#8068 prescribes the transition itself for a population this size, and
2719-
* objectui#8071 owns the work — key by key, deleting an entry here in the same
2720-
* change that registers its pin.
2721-
*
2722-
* A key that needs a DIFFERENT reason — a shape a pin genuinely cannot express,
2723-
* a contract question upstream owns — gets its own string. The map is
2724-
* `Record<string, string>` precisely so that stays possible without a second
2725-
* mechanism.
2726-
*/
2727-
const AWAITING_A_PIN =
2728-
'No per-block member pin today. Measured with objectui#8068: 58 of the 77 array/object-armed ' +
2729-
'inputs on covered blocks had none, which is the population size that card prescribes a ' +
2730-
'self-deleting transition for rather than a same-PR sweep. objectui#8071 owns writing the pin; ' +
2731-
'delete this entry in the same change that registers it.';
2722+
// `AWAITING_A_PIN` — the one shared reason every key in objectui#8068's
2723+
// measured population carried ("objectui#8071 owns writing the pin; delete this
2724+
// entry in the same change that registers it") — was DELETED by objectui#8071
2725+
// slice 18 together with its last three entries (`object-master-detail-form`'s
2726+
// `dataSource`, `details` and `fields`). With no entry carrying it, it was a
2727+
// reason for nothing, and a constant nothing reads is a claim nothing checks.
2728+
// The two docblocks below still contrast against it by name; read those
2729+
// mentions as the retired constant. The rule it stated survives as the
2730+
// discipline of this map: a key that needs a reason other than "no pin yet" —
2731+
// a shape a pin genuinely cannot express, a contract question upstream owns —
2732+
// gets its own string, which is why the map stays `Record<string, string>`.
27322733

27332734
/**
27342735
* The reason the four keys objectui#8176 brought INTO this population carry.
@@ -2839,12 +2840,10 @@ const MEMBER_PIN_EXEMPTIONS: Record<string, string> = {
28392840

28402841
// object-master-detail-form — objectui#8071 slice 15 pinned the two PARENT
28412842
// SEED keys (`initialData`, `initialValues`, an alternate-spelling pair the
2842-
// registration declares as one) and `sections`; the three below are what that
2843-
// first bite left, and they are the block's other half — where the parent
2844-
// record comes from and what the child collections are.
2845-
'object-master-detail-form.dataSource': AWAITING_A_PIN,
2846-
'object-master-detail-form.details': AWAITING_A_PIN,
2847-
'object-master-detail-form.fields': AWAITING_A_PIN,
2843+
// registration declares as one) and `sections`, and slice 18 the block's
2844+
// other half (`dataSource`, `details`, `fields`); the block is now fully
2845+
// pinned, and this header stays only as a note for the next reader who
2846+
// greps for it.
28482847

28492848
// object-metric — objectui#8071 slice 8 pinned the four QUERY members
28502849
// (`dataSource`, `aggregate`, `filter`, `compareTo`) and slice 9 the two
@@ -3624,11 +3623,48 @@ const NEWLY_JUDGED_UNPINNED_MEMBERS: string[] = [];
36243623
* `NO_READ_SITE_TO_PIN` reading was not re-measured here either — slice 7's
36253624
* measurement still stands.
36263625
*
3626+
* ## 4 -> 1 (objectui#8071 slice 18) — `object-master-detail-form` CLOSES,
3627+
* and `AWAITING_A_PIN` goes with its last three entries
3628+
*
3629+
* `dataSource`, `details` and `fields` are pinned, which takes the last block
3630+
* carrying the generic reason to zero. Set arithmetic: -3 exemptions,
3631+
* -3 ceiling, +3 pins, and the remaining count equals this constant, so the
3632+
* enumeration is the whole population: one entry,
3633+
* `record:related_list.actions`.
3634+
*
3635+
* ⚠️ Two of the three are PROMOTED pre-existing files and one is new, and the
3636+
* split follows what each file already stated. `MasterDetailForm.elementDataSource`
3637+
* already fixed WHICH binding members this block honours, and was grown by
3638+
* where the honoured one goes: it OUTRANKS the flat `objectName`, and it
3639+
* reaches the DETAIL half (the derived child FK and the batch's parent leg).
3640+
* It was also grown by the finding that "unmapped" is not "unread": a
3641+
* malformed binding `filter` beside a named view withholds the whole form.
3642+
* `topLevelFieldsWarnCoverage-8847` already pinned the `FormFieldSchema`
3643+
* trap, and was grown by what a legal member IS. `details` is this block's
3644+
* own half, and nothing stated its twelve members member by member, so its
3645+
* pin is new.
3646+
*
3647+
* ⚠️ One member fact is pinned as BEHAVIOUR and handed back rather than
3648+
* repaired here, the same choice slice 9 made and for the same reason:
3649+
* `fields` bounds what the parent form DRAWS and not what its batch leg
3650+
* WRITES, while the registration calls the key the parent pool for "the
3651+
* submitted set".
3652+
*
3653+
* ⚠️ `AWAITING_A_PIN` was deleted in this commit. Its last entries were these
3654+
* three, and a reason no entry carries says nothing (see the note where it
3655+
* stood). The constant beside it, `AWAITING_A_PIN_NEWLY_JUDGED`, is kept,
3656+
* because the objectui#8176 audit test still reads it.
3657+
*
3658+
* ⚠️ Unchanged by this slice: `record:related_list.actions`, whose
3659+
* `NO_READ_SITE_TO_PIN` reading was not re-measured here. The one unit left
3660+
* in this constant is that key's, and it moves only with the enforce-or-remove
3661+
* decision the constant names, which the spec owns.
3662+
*
36273663
* ⇒ The rule for every future slice of objectui#8071: delete the entry, register
36283664
* the pin, and set this constant to the new count. Not to the new count plus
36293665
* room.
36303666
*/
3631-
const MEMBER_PIN_EXEMPTION_CEILING = 4;
3667+
const MEMBER_PIN_EXEMPTION_CEILING = 1;
36323668

36333669
/**
36343670
* Every test file a member pin can live in, as LAZY `?raw` loaders.

0 commit comments

Comments
 (0)