From d1f1c892d1b7fb7a1813cb6ffe80dbb76b3f7d4a Mon Sep 17 00:00:00 2001 From: "objectstack-fleet[bot]" <332303061+objectstack-fleet[bot]@users.noreply.github.com> Date: Thu, 29 Oct 2026 05:02:14 +0000 Subject: [PATCH 1/3] docs(skills): objectstack-formula eval for a mail amount in each record's own currency (currency:@path), held over the token ceiling for a ruling Case 8 asks for an email_template that mails one overdue notice per contract with the amount in that contract's own currency; its reference answer names the marked form, that only a mail template reads the code from the send's data, that the stored code is three capitals exactly as stored, and the declared-variables rule; the assertions require currency:@ and refuse the bare path, a hard-coded code and the no-argument form. Six reference answers of existing cases are shortened where they restated a point they already make (-160 B, every assertion and fact kept). The file reads 1506 / 1352 tokens (+154): the case cannot be paid by deletion inside the file, so this commit is a measured WIP for the ceiling ruling, not a landing. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg --- .../26940-formula-eval-currency-at-path.md | 12 +++++++++++ .../evals/cel-predicates-formulas.json | 20 ++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 .changeset/26940-formula-eval-currency-at-path.md diff --git a/.changeset/26940-formula-eval-currency-at-path.md b/.changeset/26940-formula-eval-currency-at-path.md new file mode 100644 index 00000000000..4239e444c84 --- /dev/null +++ b/.changeset/26940-formula-eval-currency-at-path.md @@ -0,0 +1,12 @@ +--- +'@objectstack/skills': patch +--- + +docs(skills): objectstack-formula gains an eval for a mail amount shown in each record's own currency (`currency:@path`) + +Clause-②: no +Metadata-protocol: none + +- **Before.** The skill taught `{{ amount | currency:@currency_code }}` for a mail template that shows each record's amount in that record's own currency, but no eval checked that an AI author reaches for it. An author could write a bare path after the colon (refused at authoring), hard-code one currency code for every record, or leave the argument out (the organization's default currency for every record), and nothing scored that wrong. +- **Now.** Case 8 of `evals/cel-predicates-formulas.json` asks for an `email_template` that mails one overdue notice per contract with the amount in that contract's own currency. Its reference answer names the marked form, that only a mail template may read the code from the send's data, that the stored code is three capitals exactly as stored, and that a declared `variables` list must name the path or a root above it; the assertions require `currency:@` and refuse the bare path, a hard-coded code and the no-argument form. Six reference answers of existing cases are shortened where they restated a point they already make, with every assertion and fact kept, to pay for the case inside the file's token ceiling. +- No schema, key or behaviour changes. diff --git a/skills/objectstack-formula/evals/cel-predicates-formulas.json b/skills/objectstack-formula/evals/cel-predicates-formulas.json index e4389a466f8..0b13a4a7b78 100644 --- a/skills/objectstack-formula/evals/cel-predicates-formulas.json +++ b/skills/objectstack-formula/evals/cel-predicates-formulas.json @@ -4,7 +4,7 @@ { "id": 1, "prompt": "Add a `full_name` formula on `contact` built from salutation, first name and last name, and a `margin_pct` formula on `deal` = (revenue - cost) / cost * 100.", - "expected_output": "Two `type: 'formula'` fields whose `expression` is CEL via the `F` tag. The text formula coalesces every nullable operand — `F`coalesce(record.salutation, '') + ' ' + coalesce(record.first_name, '') + ' ' + coalesce(record.last_name, '')`` — because `null + 'foo'` throws. The numeric one guards the divisor: `F`coalesce(record.cost, 0) > 0 ? ((coalesce(record.revenue, 0) - record.cost) * 100) / record.cost : 0.0``.", + "expected_output": "Two `type: 'formula'` fields, `expression` under the `F` tag. The text one coalesces every nullable operand — `F`coalesce(record.salutation, '') + ' ' + coalesce(record.first_name, '') + ' ' + coalesce(record.last_name, '')`` — because `null + 'foo'` throws. The numeric one guards the divisor: `F`coalesce(record.cost, 0) > 0 ? ((coalesce(record.revenue, 0) - record.cost) * 100) / record.cost : 0.0``.", "files": [], "assertions": { "must_contain": ["F`", "coalesce(", "record."], @@ -14,7 +14,7 @@ { "id": 2, "prompt": "Translate this Salesforce validation rule into ObjectStack: AND(ISBLANK(po_number), amount > 10000, NOT(ISPICKVAL(status, 'draft'))).", - "expected_output": "Applies the mechanical legacy → CEL table: `P`isBlank(record.po_number) && record.amount > 10000 && record.status != 'draft'``, noting that a `script` validation is inverted (it FAILS when the expression is true, so keep the failing condition) and that `record.amount > 10000` on a declared-nullable field needs an explicit `record.amount != null &&` guard or the publish gate rejects it.", + "expected_output": "By the legacy → CEL table, `P`isBlank(record.po_number) && record.amount > 10000 && record.status != 'draft'``; a `script` validation is inverted (it FAILS when true, so keep the failing condition), and `record.amount > 10000` on a nullable field needs an explicit `record.amount != null &&` guard or the publish gate rejects it.", "files": [], "assertions": { "must_contain": ["isBlank(", "&&", "record.", "P`"], @@ -34,7 +34,7 @@ { "id": 4, "prompt": "Demo opportunities should close 45 days after the package is installed, and a formula should show how many days remain until close.", - "expected_output": "Seed value as a CEL envelope, `close_date: cel`daysFromNow(45)``, never a JS `Date` / `Date.now()` (the determinism gate: two `os build` runs must be byte-identical). Days remaining is `daysBetween(today(), record.close_date)` — never `record.close_date - today()` (a duration stored as `{}`) and never date arithmetic on `today()` (it faults).", + "expected_output": "Seed value as a CEL envelope, `close_date: cel`daysFromNow(45)``, never a JS `Date` / `Date.now()` (two `os build` runs must be byte-identical). Days remaining is `daysBetween(today(), record.close_date)` — never `record.close_date - today()` (a duration stored as `{}`) and never date arithmetic on `today()` (it faults).", "files": [], "assertions": { "must_contain": ["cel`", "daysFromNow(", "daysBetween("], @@ -44,7 +44,7 @@ { "id": 5, "prompt": "Hide `discount_reason` unless a discount amount was entered, and reject any `end_date` earlier than `start_date`.", - "expected_output": "`visibleWhen: P`record.discount != null`` (or `!isBlank(record.discount)`) on the field — `has(record.discount)` is not a null check: every declared field exists, so it is always true. The date rule is a `script` validation whose predicate dominates the comparison with explicit null tests: `record.start_date != null && record.end_date != null && record.end_date < record.start_date`; a `has()` guard does not satisfy the publish-time gate.", + "expected_output": "`visibleWhen: P`record.discount != null`` (or `!isBlank(record.discount)`) on the field — `has(record.discount)` is not a null check: every declared field exists, so it is always true. The date rule is a `script` validation with explicit null tests ahead of the comparison: `record.start_date != null && record.end_date != null && record.end_date < record.start_date`; a `has()` guard does not satisfy the publish gate.", "files": [], "assertions": { "must_contain": ["visibleWhen", "!= null", "record.end_date"], @@ -64,12 +64,22 @@ { "id": 7, "prompt": "Refuse a `contract_version` save unless its `kind` is the clean copy, and show `signed_at` only once `status` is signed.", - "expected_output": "Both are predicates over the STORED value: a `script` validation `P`record.kind != 'clean'`` (it fails when true) and `visibleWhen: P`record.status == 'signed'``. The `label` macro evaluates only in a formula field; in a validation, `visibleWhen` or hook predicate it faults and aborts every write, so compare the stored value, never its option label.", + "expected_output": "A `script` validation `P`record.kind != 'clean'`` (it fails when true) and `visibleWhen: P`record.status == 'signed'``. The `label` macro evaluates only in a formula field; in a validation, `visibleWhen` or hook predicate it faults and aborts every write, so compare the stored value, never its option label.", "files": [], "assertions": { "must_contain": ["record.kind", "visibleWhen", "P`"], "must_not_contain": ["label(", "{record."] } + }, + { + "id": 8, + "prompt": "An `email_template` that mails one overdue notice per `contract`, the amount shown in that contract's own currency.", + "expected_output": "`{{ amount | currency:@currency_code }}` — only a mail template may read the code from the send's data. The stored code is three capitals (`EUR`), exactly as stored; when the template declares `variables`, the path must be one of them or lie under one. A bare path is refused, a literal code fixes one currency for every record, and no argument means the organization's default currency.", + "files": [], + "assertions": { + "must_contain": ["currency:@"], + "must_not_contain": ["currency:currency_code", "currency:USD", "currency:CNY", "| currency }}"] + } } ] } From ee7516b7a996f974345f099968af3633ed08db37 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 30 Oct 2026 03:37:04 +0000 Subject: [PATCH 2/3] chore(skills): raise the objectstack-formula eval token ceiling 1352 -> 1506 for the currency:@path case The eval case for a mail amount shown in each record's own currency costs 194 tokens; six restatements already cut in the same file paid 40 of them, and the remaining 154 are raised by the maintainer ruling recorded on the card (comment 6512920986, letter A), quoted in the row comment. Pinned AT the measurement, zero headroom. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_016WV4d1BfaajCZsT5GLe2M7 --- scripts/check-skills-token-ratchet.mjs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/scripts/check-skills-token-ratchet.mjs b/scripts/check-skills-token-ratchet.mjs index c223c18ebf8..89fce5c7e35 100644 --- a/scripts/check-skills-token-ratchet.mjs +++ b/scripts/check-skills-token-ratchet.mjs @@ -1038,7 +1038,18 @@ export const CEILINGS = new Map([ // `must_not_contain` assertions, 212 bytes = 53 units (1046 -> 993); the two // cases add 1438 bytes = 359 units (993 -> 1352); pinned AT the measurement, // zero headroom. The raising PR's body carries the same arithmetic. - ['skills/objectstack-formula/evals/cel-predicates-formulas.json', 1352], + // 1352 -> 1506 (card #26940): case 8 asks for an `email_template` that mails + // one overdue notice per contract in that contract's own currency and + // expects `{{ amount | currency:@currency_code }}`; it refuses the bare path, + // a hard-coded `currency:USD` / `currency:CNY` and the no-argument form. + // Maintainer ruling 2026-10-30, decision batch #428 item 1, letter A (the + // ruling comment on the card is 6512920986), verbatim and untranslated, the + // binding first clause: 「26940同意」. PAID DOWN FIRST: six restatements in + // cases 1, 2, 4, 5 and 7 cut, 160 bytes = 40 units, every assertion and fact + // kept (1352 -> 1312); the case adds 774 bytes = 194 units (1312 -> 1506); + // pinned AT the measurement, zero headroom. The raising PR's body carries + // the same arithmetic. + ['skills/objectstack-formula/evals/cel-predicates-formulas.json', 1506], // 1004 -> 1451 (2026-10-27, the objectstack-i18n fix card of the skills gap // audit): two eval prompts for the teaching that card adds — the `reports`, // `positions`, `permissions` and `picklists` groups; a form view `title`, a From 23838ca3e8affb2a84e923e6523e2d60b63bcc46 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 30 Oct 2026 07:14:03 +0000 Subject: [PATCH 3/3] docs(skills): drop the token-ceiling clause from the formula eval changeset Eval fixtures carry no token ceiling since the skills ratchet stopped pricing evals/**, so the clause saying the trims paid for the case inside the file's ceiling no longer holds and would ship in the changelog. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_016WV4d1BfaajCZsT5GLe2M7 --- .changeset/26940-formula-eval-currency-at-path.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/26940-formula-eval-currency-at-path.md b/.changeset/26940-formula-eval-currency-at-path.md index 4239e444c84..45496902aca 100644 --- a/.changeset/26940-formula-eval-currency-at-path.md +++ b/.changeset/26940-formula-eval-currency-at-path.md @@ -8,5 +8,5 @@ Clause-②: no Metadata-protocol: none - **Before.** The skill taught `{{ amount | currency:@currency_code }}` for a mail template that shows each record's amount in that record's own currency, but no eval checked that an AI author reaches for it. An author could write a bare path after the colon (refused at authoring), hard-code one currency code for every record, or leave the argument out (the organization's default currency for every record), and nothing scored that wrong. -- **Now.** Case 8 of `evals/cel-predicates-formulas.json` asks for an `email_template` that mails one overdue notice per contract with the amount in that contract's own currency. Its reference answer names the marked form, that only a mail template may read the code from the send's data, that the stored code is three capitals exactly as stored, and that a declared `variables` list must name the path or a root above it; the assertions require `currency:@` and refuse the bare path, a hard-coded code and the no-argument form. Six reference answers of existing cases are shortened where they restated a point they already make, with every assertion and fact kept, to pay for the case inside the file's token ceiling. +- **Now.** Case 8 of `evals/cel-predicates-formulas.json` asks for an `email_template` that mails one overdue notice per contract with the amount in that contract's own currency. Its reference answer names the marked form, that only a mail template may read the code from the send's data, that the stored code is three capitals exactly as stored, and that a declared `variables` list must name the path or a root above it; the assertions require `currency:@` and refuse the bare path, a hard-coded code and the no-argument form. Six reference answers of existing cases are shortened where they restated a point they already make, with every assertion and fact kept. - No schema, key or behaviour changes.