From d7411d9031e89b25abfd9a5b7a9e3e2bd9925101 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 31 Oct 2026 23:03:31 +0000 Subject: [PATCH 1/3] docs(skills): the percent row of the data skill's field-type table teaches percentDisplay The `percent` row of `skills/objectstack-data/rules/field-types.md` now lists `percentDisplay`: unset or `'bar'` draws the progress bar, `'plain'` prints the number alone, for a rate or a probability. The addition is paid inside the file's token ceiling by deleting the `Select with Default` example, which restated the Selection section's own option example (label, value, color, default). Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01L9qibvc9vjAiGhyhnLTp34 --- skills/objectstack-data/rules/field-types.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/skills/objectstack-data/rules/field-types.md b/skills/objectstack-data/rules/field-types.md index 2f353791bac..3e77079bc52 100644 --- a/skills/objectstack-data/rules/field-types.md +++ b/skills/objectstack-data/rules/field-types.md @@ -51,7 +51,7 @@ fields: { |:-----|:------------|:-------| | `number` | Generic numeric value | `min`, `max`, `precision`, `scale` | | `currency` | Monetary amounts | `currencyConfig` (currencyMode, defaultCurrency, currencyField, homeAmount) | -| `percent` | Percentage values (0-100) | `min`, `max`, `precision` | +| `percent` | Percentage values (0-100) | `min`, `max`, `precision`, `percentDisplay` — the read-only face: unset or `'bar'` draws a progress bar, `'plain'` the number alone, for a rate or a probability | ## Date & Time @@ -182,20 +182,6 @@ home-currency amount in `field` (a `'dynamic'` `currency`) at the `rateDate` (`date`/`datetime`) rate, and that rate in `rateField` (`number`), both `readonly: true`. Cross-currency totals sum `field`. -### Select with Default - -```typescript -{ - type: 'select', - required: true, - options: [ - { label: 'Low', value: 'low' }, - { label: 'Medium', value: 'medium', default: true }, - { label: 'High', value: 'high', color: '#e74c3c' }, - ], -} -``` - ### Lookup (One-to-Many) ```typescript From 0984e3e91e582d31c6beeafec3703f4debe2f756 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 31 Oct 2026 23:05:36 +0000 Subject: [PATCH 2/3] docs(skills,docs): the data skill and the fields page teach lookupAutofill on a lookup pick `skills/objectstack-data/rules/field-types.md` lists `lookupAutofill` on the `lookup` and `master_detail` rows and teaches it under the Relational table: the pair shape, which targets a pick fills, which sources it copies, the overwrite rule, that the fill is client-side only, and that a declared target leaves a line-item grid column's same-named `autofill` copy. Paid inside the file's token ceiling by deleting the `Email with Uniqueness` example (the unique scope lives in `rules/indexing.md`), the `Lookup (Many-to-Many)` example (the multi-value lookup lives in `SKILL.md`, and its `max: 10` is the numeric bound, not a selection cap) and the vector paragraph, whose `vectorConfig` trap folds into the invented-key list at the top of the file. `content/docs/data-modeling/fields.mdx` gains the key in the relationship property table, a product-lookup example and a `Lookup-pick autofill` section with the same rules in full, pointing at the generated Field reference. Changeset: `@objectstack/skills` patch. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01L9qibvc9vjAiGhyhnLTp34 --- ...a-skill-percent-display-lookup-autofill.md | 12 +++++ content/docs/data-modeling/fields.mdx | 40 +++++++++++++++++ skills/objectstack-data/rules/field-types.md | 44 ++++++------------- 3 files changed, 65 insertions(+), 31 deletions(-) create mode 100644 .changeset/28256-data-skill-percent-display-lookup-autofill.md diff --git a/.changeset/28256-data-skill-percent-display-lookup-autofill.md b/.changeset/28256-data-skill-percent-display-lookup-autofill.md new file mode 100644 index 00000000000..109d265cd4a --- /dev/null +++ b/.changeset/28256-data-skill-percent-display-lookup-autofill.md @@ -0,0 +1,12 @@ +--- +'@objectstack/skills': patch +--- + +docs(skills): the objectstack-data field-types rule teaches `percentDisplay` and `lookupAutofill` + +Clause-②: no +Metadata-protocol: none + +- The `percent` row of the field-type table lists `percentDisplay`: unset or `'bar'` draws the progress bar, `'plain'` prints the number alone, for a rate or a probability. +- The Relational section teaches `lookupAutofill: [{ target, source, overwrite? }]` on a single-value `lookup` / `master_detail`: which targets a pick may fill, which sources it may copy, the overwrite rule, that the fill is client-side only, and how it relates to a line-item grid column's `autofill`. +- Paid inside the file's token ceiling: the `Select with Default`, `Email with Uniqueness` and `Lookup (Many-to-Many)` examples go (each restated a rule the skill carries elsewhere), and the `vectorConfig` trap folds into the invented-key list at the top of the file. diff --git a/content/docs/data-modeling/fields.mdx b/content/docs/data-modeling/fields.mdx index 9294d6e9554..1c9d073ec1c 100644 --- a/content/docs/data-modeling/fields.mdx +++ b/content/docs/data-modeling/fields.mdx @@ -212,6 +212,13 @@ primary_contact: Field.lookup('contact', { ], }), +// A pick fills sibling fields: the picked product's list_price lands in unit_price +product: Field.lookup('product', { + label: 'Product', + required: true, + lookupAutofill: [{ target: 'unit_price', source: 'list_price' }], +}), + // Master-detail (cascade delete) project: Field.masterDetail('project', { label: 'Project', @@ -236,6 +243,39 @@ order: Field.masterDetail('order', { | `inlineEdit` | `boolean \| 'grid' \| 'form'` | Render child records inline on the parent create/edit form (`true` = auto-pick, `'grid'`, or `'form'`) | | `inlineColumns` | `array` | Optional explicit columns for the inline grid | | `inlineAmountField` | `string` | Optional numeric child field for the inline running total | +| `lookupAutofill` | `{ target, source, overwrite? }[]` | Fields of this object a pick fills from the picked record (single-value `lookup` / `master_detail` only) — see below | + +#### Lookup-pick autofill: `lookupAutofill` + +A single-value `lookup` / `master_detail` can declare `lookupAutofill: [{ target, +source, overwrite? }]`. When a user picks a record in that field, each pair copies +the picked record's `source` field (a field of the `reference` object) into this +object's `target` field — in the record form, and in a line-item grid column over +the field. The example above fills a quote line's `unit_price` from the picked +product's `list_price`. + +- **`target`** is a field of this object that an author writes: not the lookup + itself, not `readonly`, and not computed (`formula`, `summary`, `autonumber`, or + a stored field with an `expression`). Each target is named once per field. The + object's parse refuses anything else. +- **`source`** is a field the referenced object declares and the record carries + (not `internal`), and its value must be one the target can hold: the same type, + a string-valued field into a text target, `number` and `currency` into each + other, a relationship only into a relationship over the same object. `os + validate` / `os build` judge the source against the referenced object. +- **Overwrite rule.** By default a pick never replaces a value somebody wrote: the + target is filled only while it is empty or still holds the value an earlier pick + filled in. A pair with `overwrite: true` replaces the target on every pick. +- **Client-side only.** The fill edits the form, and the filled value is saved as an + ordinary write. Nothing on the write path reads the key: an import or an API + write that sets the lookup fills nothing. +- **In a line-item grid**, the column key `autofill` still governs only the grid's + same-named copy; a declared pair applies under its own overwrite rule and takes + its target out of that copy. A master-detail collection whose relationship and + every column are authored loads no child fields, so it sees no pairs and keeps + only the same-named copy. + +The key's full contract is in the [Field reference](/docs/references/data/field). #### Referential integrity diff --git a/skills/objectstack-data/rules/field-types.md b/skills/objectstack-data/rules/field-types.md index 3e77079bc52..ee9aff8cec5 100644 --- a/skills/objectstack-data/rules/field-types.md +++ b/skills/objectstack-data/rules/field-types.md @@ -26,8 +26,8 @@ fields: { > **Config columns list only real `FieldSchema` keys.** Per-type display knobs > beyond these do **not** exist — an unknown field key is REFUSED at parse -> (`unrecognized_keys`), so don't invent `theme`, `rows`, or -> `fileAttachmentConfig`. Source of truth: +> (`unrecognized_keys`), so don't invent `theme`, `rows`, +> `vectorConfig` or `fileAttachmentConfig`. Source of truth: > `node_modules/@objectstack/spec/src/data/field.zod.ts`. ## Text & Content @@ -90,11 +90,20 @@ options: [ | Type | When to Use | Key Config | |:-----|:------------|:-----------| -| `lookup` | Reference another object (independent) | `reference`, `lookupFilters`, `multiple`, `deleteBehavior` | -| `master_detail` | Parent–child with lifecycle control | `reference`, `deleteBehavior` (`cascade`/`restrict` — `set_null` is refused) | +| `lookup` | Reference another object (independent) | `reference`, `lookupFilters`, `multiple`, `deleteBehavior`, `lookupAutofill` | +| `master_detail` | Parent–child with lifecycle control | `reference`, `deleteBehavior` (`cascade`/`restrict` — `set_null` is refused), `lookupAutofill` | | `tree` | Hierarchical self-reference | `reference` — optional; when written, this object's own name (any other object is refused at parse) | | `user` | Person picker — a lookup specialized to `sys_user` (assignee, watchers). Stored identically to `lookup` | `multiple` (collaborators), `defaultValue: 'current_user'` | +**`lookupAutofill: [{ target, source, overwrite? }]`** (single-value `lookup` / +`master_detail`): a pick copies the picked record's `source` into this object's +`target` (`[{ target: 'unit_price', source: 'list_price' }]` on a product lookup), +in the form and the line-item grid. `target`: a field of this object an author +writes (not the lookup, `readonly` or computed), named once. `source`: a declared, +non-`internal` field of `reference` the target can hold. A pick fills an empty +target or an earlier pick's fill; `overwrite: true` always replaces. Client-side +only; in a grid a declared target leaves the same-named `autofill` copy. + ## Media | Type | When to Use | Config | @@ -142,19 +151,6 @@ Stored as JSON on the parent row — no separate table / FK: ## Common Field Configurations -### Email with Uniqueness - -```typescript -{ - type: 'email', - required: true, - // State the scope (ADR-0120): 'organization' = one holder per organization - // (NULL-safe), 'global' = one across the whole installation. Bare `true` at - // field level still means 'organization' and stays valid. - unique: 'organization', -} -``` - ### Currency ```typescript @@ -195,17 +191,6 @@ home-currency amount in `field` (a `'dynamic'` `currency`) at the `rateDate` } ``` -### Lookup (Many-to-Many) - -```typescript -{ - type: 'lookup', - reference: 'tag', - multiple: true, - max: 10, -} -``` - ### Formula ```typescript @@ -267,9 +252,6 @@ The `format` is literal text interleaved with `{...}` tokens: } ``` -There is **no** `vectorConfig` block — an authored `vectorConfig` is refused at -parse. `dimensions` is the flat field-level key. - ## Incorrect vs Correct ### ❌ Incorrect — Autonumber interpolating an optional / adjacent field From 153207eec637c67a3405ac550adc4b428a36d182 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 31 Oct 2026 23:45:25 +0000 Subject: [PATCH 3/3] docs(fields): the lookupAutofill source rule names multiplicity, and the master-detail bound names its two conditions Two should-fixes from the contract review of the fields page's Lookup-pick autofill section: the source type rule now says "the same type and multiplicity (a list only into a list)", the first refusal of the stack's type rule; and the bound reads "whose relationshipField is set and whose every column carries its type", the two conditions under which the master-detail renderer skips the child-schema fetch. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01L9qibvc9vjAiGhyhnLTp34 --- content/docs/data-modeling/fields.mdx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/content/docs/data-modeling/fields.mdx b/content/docs/data-modeling/fields.mdx index 1c9d073ec1c..b40a6f61a5d 100644 --- a/content/docs/data-modeling/fields.mdx +++ b/content/docs/data-modeling/fields.mdx @@ -259,7 +259,8 @@ product's `list_price`. a stored field with an `expression`). Each target is named once per field. The object's parse refuses anything else. - **`source`** is a field the referenced object declares and the record carries - (not `internal`), and its value must be one the target can hold: the same type, + (not `internal`), and its value must be one the target can hold: the same type + and multiplicity (a list only into a list), a string-valued field into a text target, `number` and `currency` into each other, a relationship only into a relationship over the same object. `os validate` / `os build` judge the source against the referenced object. @@ -271,9 +272,9 @@ product's `list_price`. write that sets the lookup fills nothing. - **In a line-item grid**, the column key `autofill` still governs only the grid's same-named copy; a declared pair applies under its own overwrite rule and takes - its target out of that copy. A master-detail collection whose relationship and - every column are authored loads no child fields, so it sees no pairs and keeps - only the same-named copy. + its target out of that copy. A master-detail collection whose `relationshipField` + is set and whose every column carries its `type` loads no child fields, so it + sees no pairs and keeps only the same-named copy. The key's full contract is in the [Field reference](/docs/references/data/field).