skills(formula): optimization flight — cut the cron/template quickrefs and section-5 narration, teach virtual formula fields and fault-open form predicates (net −844 tokens) - #14563
Merged
Conversation
…RM-F-06 / A-01 / G-05 / B-04 / F-07)
The frontmatter routes cron away ("Do not use for ... cron schedules") while the
body taught it across four sections; the corpus authors zero of it (`cron` 0,
`tmpl` 0, any formatter 0). Both are live surfaces, so each keeps ONE
schema-pointing row with its carriers; the two quick-reference sections and the
formatter table go.
- FRM-F-06 / A-01: `### Cron surfaces`, `### Template surfaces`, `## Cron quick
reference` and `## Template quick reference` collapse into one
`### Cron and template surfaces` table, two rows, pointing at
`shared/expression.zod.ts` for the full carrier list.
- FRM-G-05: the deprecated `visibleOn` / `visibility` alias row leaves the CEL
surfaces table; the two `Object.titleFormat` notes dedupe to one carrier cell.
- FRM-B-04: the third "no js dialect" restatement goes; the canonical one in
Core contract stays.
- FRM-F-07: the view-list-filter prohibition now names the alternative it was
withholding — date macro tokens, with the pointer to objectstack-query
`rules/filters.md`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…history (FRM-D-05 / D-04 / B-03) Section 5 was 20.7% of the file (1,241 tok). The rules in it are load-bearing; the narration around them was not. - FRM-D-04: the pre-protocol-17 `logger.warn` history goes. Two rules were buried in it and survive in one sentence: `before*` and `after*` abort alike with no `onError` escape, and a condition that does not compile aborts the same way. - FRM-D-05: the `before*` exception restated the table row above it at 165 tokens — it is now one clause pointing back at that row. The per-row bulk semantics keep their ADR-0058 statement; the query-count and fetch-cost narration goes. - FRM-B-03: the `has()`-on-a-declared-field rule was stated once for `record` and again for `previous`. It is now one statement covering both, in the `has()` section where the rule lives. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…RM-D-01 / B-07 / B-06 / D-09 / D-07 / D-02 / B-05 / D-03 / G-03 / G-01 / G-02 / G-04 / E-03) - FRM-D-01 / B-07: "Strategic context" — why CEL was picked over the deleted Salesforce-flavor engine — was the first content after the H1. An author emitting a predicate does not need the engine-selection history. - FRM-B-06: ADR-0032 was stated three times, and "Verify your work" cross-refs the top-of-file note that carried it. One statement now, in "Verify your work", and it keeps both halves the callout had: build fails with a located did-you-mean, and the runtime throws. - FRM-D-09: the `type Expression` block transcribed `shared/expression.zod.ts`. One sentence and the pointer replace it. - FRM-D-07 / D-02: "you do not learn three languages" and the roadmap for emitting `ast` go; the envelope rule they both carried survives once. The auto-wrap fact moved to the cron/template row in the previous commit. - FRM-B-05: "Determinism contract" restated the seed pattern's own note. The three numbered conditions fold into that note. - FRM-D-03 / G-03: "Open questions" cited `CelExprSchema` twice; the symbol exists nowhere in the repo (0 hits outside this file). - FRM-G-01: the CLI is `os`, not `objectstack` — 5 mentions, now 0. - FRM-G-02: the frontmatter pins 17.x; the retired milestone numbers are gone. - FRM-G-04: a CEL string literal takes either quote; the corpus writes double quotes inside P` ` more than ten times, so "(always)" was false. - FRM-E-03: `* 100.0` was a cel-js workaround. `registerNumericCoercions` (`packages/formula/src/stdlib.ts`) registers the mixed `double op int` / `int op double` overloads, so `* 100` is correct now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…FRM-F-01 / F-03 / F-02 / F-04 / B-01 / B-02 / E-01 / D-08) The corpus measurement behind these: `type: 'formula'` fields have zero live usages and the only hits are a removal test; flow/trigger conditions are authored five times and the scope was taught wrong; field conditional rules are the second-commonest task and two of their roots were missing. - FRM-F-01: a `formula` field is virtual — `where` / `orderBy` / `searchableFields` on one are refused `400 INVALID_FIELD` at both doors (`packages/spec/src/migrations/entries/semantic/17.engine-find-formula-filter-refused.ts`; `packages/objectql/src/filter-comparand-shape.ts`). The filter and sort axes were taught nowhere in the catalog. Remedy is the stored column, in the same words the sort and search axes already use. - FRM-F-03: a form-view `visibleWhen` is client-side only and faults OPEN (`packages/spec/src/ui/view.zod.ts` `FormFieldSchema` / `FormSectionSchema`; `packages/metadata-core/src/form-predicate-root-policy.ts`). The package taught fault-closed globally, so the note says where each holds. - FRM-F-02: the `parent` root (line-item cell `readonlyWhen` / `requiredWhen`, `packages/spec/src/data/field.zod.ts`) and the `data` root (metadata-editing forms, `BOUND_FORM_VIEW_PREDICATE_ROOTS`) join the cheat-sheet. - FRM-F-04: flow conditions bind bare identifiers — the automation engine spreads the record's variables to top level (`packages/services/service-automation/src/engine.ts`). The translation table's first row implied the opposite universally. - FRM-B-02 / B-01: the `node_modules/@objectstack/formula/` pointer named a package that ships `dist` only, which this file already says is unreadable. Its slot goes to `references/_index.md`, which every other domain package links and this one orphaned. - FRM-E-01: the predicate pattern block is now type-checked. Both previously marked blocks were the cron and template quick references, so no "Mandatory pattern" was checked at all. - FRM-D-08: the no-date-arithmetic rule carried five prescriptions as prose. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…cron away (FRM-A-02 / A-01) Four host skills end their description with "load objectstack-formula alongside" (objectstack-data, -ui, -api, -automation); this one never said it was that companion, so an author reading it cold had no way to know why it had been loaded. FRM-A-01: the description also said "Do not use for ... cron schedules" while the body taught cron across four sections. The body now carries one schema-pointing cron row, so the exclusion goes and the Skill Boundaries table keeps the routing to objectstack-automation. `skills/README.md` and `content/docs/ai/skills-reference.mdx` are the generator output of `pnpm --filter @objectstack/spec gen:skill-docs` — both move, and nothing in them is hand-edited. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ills-formula-optimization # Conflicts: # content/docs/ai/skills-reference.mdx # skills/README.md
Generator output only (`pnpm --filter @objectstack/spec gen:skill-docs`), re-run after the merge so it derives from all 11 SKILL.md files at the merged tree rather than at the pre-merge one. The whole diff is the FRM-A-02 description edit; nothing here is hand-written. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…pointer (contract review round 1) Two spans failed in-seat contract review; both were verified at source before editing and both are paid in-file — the file lands back on 5,158 tokens exactly, no growth. FAIL 1 (FRM-F-01, the query-axis note). The three axes do not share one error code. `where` and `searchableFields` answer `400 INVALID_FIELD`, but `orderBy` answers `400 INVALID_SORT` — `assertOrderByIsMaterializable` sets `err.code = 'INVALID_SORT'` at the engine seam (`packages/objectql/src/engine.ts`), and `assertSortFieldsExist` does the same at the ingress. An agent branching on the code would have branched wrong on the sort refusal. Both codes are now named. FAIL 2 (the cron/template pointer). The paragraph sent readers to `shared/expression.zod.ts` for "the formatter whitelist and the cron alias set", and that file carries neither. Worse, the sets used to live in the quick references this flight deleted, so the pointer was the last mention of both — and this same file correctly says `@objectstack/formula` ships `dist` only, so "go read it" resolves to nothing. Now: the seven aliases are inlined in the cron row from `packages/formula/src/cron-engine.ts`, the template row names `TEMPLATE_FORMATTERS` (really exported from `@objectstack/formula`, derived from the formatter table in `packages/formula/src/template-engine.ts`) as the introspectable whitelist, and the `expression.zod.ts` pointer is narrowed to the carrier lists it does declare. Paid by cutting narration of the same class this flight exists to remove: the `asc`/`desc` anecdote from the new note, and the Skill Boundaries cron parenthetical now that the surfaces table names `Job.schedule.expression` and the dialect. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ills-formula-optimization
os-zhuang
approved these changes
Sep 2, 2026
os-zhuang
marked this pull request as ready for review
September 2, 2026 13:07
os-zhuang
enabled auto-merge
September 2, 2026 13:07
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #14316
Member of the skills catalog optimization program #14292 (maintainer mandate 2026-09-02: 「审核所有的 skills,进行全面的优化。」). Verdict DIET. The audit record is on the card as the
audit-recordcomment.Transliteration key — the GitHub body sanitizer eats angle-bracket-shaped fragments, so this body spells them out:
LT= opening angle bracket,GT= closing angle bracket, and a name in CAPS inside a path (record.FIELD,vars.STEP.KEY) stands for a placeholder the file writes in angle brackets.Governed face — three of the changes below are contract-semantics claims, so this PR stays draft and carries
needs:contract-review; the card carries it too.Review round 1 — both FAILs fixed, paid in-file
In-seat contract review at
d3a32664returned FAIL on two spans, PASS on everything else. Both were re-verified at source before editing, both are fixed, and both are paid insideSKILL.md: the file lands back on 5,158 tokens exactly, the same figure it carried at review time. Nothing grew.400 INVALID_FIELDwhereandsearchableFieldsanswer400 INVALID_FIELD;orderByanswers400 INVALID_SORTpackages/objectql/src/engine.ts—assertOrderByIsMaterializablesetserr.code = 'INVALID_SORT';assertSortFieldsExistis the same code at the ingress. An agent branching on the code would have branched wrong on the sort refusal.shared/expression.zod.tsfor "the formatter whitelist and the cron alias set"TEMPLATE_FORMATTERS, exported from@objectstack/formula, as the introspectable whitelist; theexpression.zod.tspointer is narrowed to the carrier lists it really declarespackages/formula/src/cron-engine.ts(@yearly @annually @monthly @weekly @daily @hourly @reboot);packages/formula/src/template-engine.tsexportsTEMPLATE_FORMATTERSviapackages/formula/src/index.ts. That file carried neither set, and the sets had lived in the quick references this flight deleted — so the pointer was the last mention of both, resolving to nothing.Paid by cutting narration of the same class this flight exists to remove: the
asc/descanecdote from the new note, and the Skill Boundaries cron parenthetical now that the surfaces table namesJob.schedule.expressionand the dialect. Two new identifiers were added and both are live —check:skill-identifier-livenessLeg 1 went 476 → 477 citations and stayed OK.Token delta
skills/objectstack-formula/SKILL.mdNet −844 tokens (−14.1%), 551 → 374 lines. Convention is the ratchet's own:
ceil(utf8 bytes / 4), soceil($(wc -c < file) / 4)reproduces it. Ceiling untouched (FRM-I-01 re-lock is deferred). The generatedreferences/_index.md(302 tok) is untouched.Gap against the audit's −1,265, itemised per cluster — not smoothed
onErrorclause, "a condition that does not even compile aborts the same way", the ADR-0058 per-row statementThe gap is one thing four times: a MERGE-INTO finding cannot pay what a DELETE would. Where the audit measured a span's bytes, I measured what was left after the rule inside it was preserved. I did not re-wrap, pad, or cut anything the audit did not name to close the difference.
Per-item 落点 | before | after
Deletions
### Cron surfaces,### Template surfaces,## Cron quick reference,## Template quick reference### Cron and template surfacestable, one row each, carriers named, the seven cron aliases inlined and the formatter whitelist named. Both are live surfaces with 0 corpus usages, so neither loses its last mention; the frontmatter no longer says "Do not use for cron schedules" while the body teaches cronbefore*table row, plus query-count and fetch-cost narrationlogger.warnhistory and its two opposite-failure examplesbefore*andafter*alike, noonErrorescape, non-compiling conditions the same## Determinism contractastprevioustotalityhas()-on-a-declared-field rule stated once forrecord, again forprevioushas()section## Open questionsCelExprSchemavisibleOn/visibilitydeprecated-alias row,titleFormatnoted twicetitleFormatdeduped to one carrier cell## See alsonode_modules/@objectstack/formula/packages/formula/package.jsonships["dist","README.md","CHANGELOG.md"], and this file already says sotype Expressionblock transcribingshared/expression.zod.tsobjectstack build5×;7.6/M9.5/M9.7/M9.8; "single quotes (always)";* 100.0os build; no milestone numbers; both quote styles;* 100Additions (each paid by deletions in the same file)
formulafield is virtual — no driver materialises a column, so all three query axes refuse one at both doors:where/searchableFieldswith400 INVALID_FIELD,orderBywith400 INVALID_SORT. Remedy is the stored column, in the same words the sort and search axes already usepackages/spec/src/migrations/entries/semantic/17.engine-find-formula-filter-refused.ts;packages/objectql/src/filter-comparand-shape.ts;packages/objectql/src/engine.tsvisibleWhenis CLIENT-SIDE only and faults open (true), so it is never access control — and where the opposite holds:Hook/SharingRuleconditionand validation predicates abort the writepackages/spec/src/ui/view.zod.ts(FormFieldSchema/FormSectionSchemavisibleWhen);packages/metadata-core/src/form-predicate-root-policy.tsparent(line-item cellreadonlyWhen/requiredWhen) anddata(metadata-editing forms, repeater rows included)packages/spec/src/data/field.zod.ts;BOUND_FORM_VIEW_PREDICATE_ROOTSrecord.prefixing was universalpackages/services/service-automation/src/engine.ts— "bare identifiers likestatus/previous.statusresolve — the natural authoring style for record-change start conditions"## See alsoreferences/_index.md— formula was the only domain package that orphaned its own generated index (8 of 9 link theirs)rules/filters.mdskills/objectstack-query/rules/filters.mdAnchor rulings honoured
This package is the CEL anchor; the automation flight (#14467) and the query flight (#14443) deleted their CEL restatements on that ruling. All three anchored constructs survive with their headings addressable:
## ObjectStack CEL standard library— the 35-function table, untouched## Mechanical translation table (legacy → CEL)— including theOLD/NEWrows and the warning below it (the warning gained the flow-condition exception; nothing was removed)## Surfaces that take an Expression— the CEL surfaces table, minus the one deprecated alias rowThe
packages/formulapin tests both pass, quoted below.Premise adjustments — no finding was forced
:548-551as pointing at an unreadable package. That holds for the@objectstack/formulaline. The second pointer,node_modules/@objectstack/spec/src/shared/expression.zod.ts, is readable in an installed app —packages/spec/package.jsonshipssrc/**/*.zod.ts. Kept, and the additions lean on it.:81-83,:476-478and:51. The first two restate "there is no js dialect" and one was deleted.:51is the Skill Boundaries row routing L2 hook bodies to objectstack-data — a routing row that never states the claim. Realized 2 → 1.skills/objectstack-formula/**is byte-identical between the audit refa59f78d, the dispatch base18d816a5and my branch point93940d49, so everyskills/span was valid unchanged;packages/**citations are re-cited above at the tree in this PR.Follow-ups (⛔ deliberately not touched here)
origin/mainand on the delivered branchclaude/issue-14308-skills-automation-optimization(PR skills(automation): optimization flight — the authored control-flow trio taught and the BPMN trio demoted, notify given its fence, the eval deduplicated, hooks and ETL pointed away (net −1,278 tokens) #14467), recording only what is still there. Thefull CEL contract, stdlib (...)callout at:41-43is gone on that branch. The numbered rule "Conditions are bare CEL — only the stdlib is callable" survives, still enumerating ~14 stdlib names beside a pointer to this package. That enumeration is what remains to cut. (Neither cut has landed onmainyet — both restatements are present at the tree in this PR.)now(),today(),daysFromNow(n),daysAgo(n),isBlank(v),coalesce(v, fallback)) is still present beside its pointer here.OLD/NEWevaluate tonullsince M9.5" is stale twice over: the current truth is a build failure (packages/formula/src/validate.ts, and this package's own stdlib note), andM9.5is a retired milestone. Generated-region prose, so not editable from this PR.evals/. This is the package's sole unexcused omission (8 of 8 other domain packages ship one), and it cannot be added here:scripts/check-skills-token-ratchet.mjsreds a discovered authored file with no ceiling, and no same-file deletion can pay for a new file. Deferred to [Decision] Skills optimization program — batch 1 (4 items): new-file ceilings for splits · planned-eval stubs · react-blocks double rendering · published pm-dispatch scope #14296 item 2, as ruled.objectstack-formulareference index advertisesdata/date-macros.zod.ts, a surface the SKILL.md routes to objectstack-query —SKILL_MAPinbuild-skill-references.tsmaps it to both #14562 (unassigned, spec lane) after a dedupe search that returned a known-present control. Nearest neighbours named on it: [finding] Theobjectstack-aigenerated reference index advertises 6 schemas the SKILL.md never teaches and omits the one it names —SKILL_MAPinbuild-skill-references.tsis unreconciled with the body #14462, [finding] Theobjectstack-automationreference index routes to the ADR-0020-retiredstate-machine.zod.tsand omits both per-nodeconfigschemas —SKILL_MAPinbuild-skill-references.ts#14466, [finding] The published skill reference index labelsdata/query.zod.ts— the whole QueryAST — "Sort Node", because its first JSDoc block isSortNode's #14441 — the same "SKILL_MAP is unreconciled with the body" class in three other packages.Gates — head sha
291f7bd7Re-derived by
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsafter the round-1 edit and after mergingorigin/main: 36 commands, whole list run, list identical across all three derivations. Two derivations were refused as STALE and each was answered by mergingorigin/mainrather than by reading the old copies — the first (33 commits behind, 148 family files changed) conflicted only in the two generated files, which were resolved to main's side, committed, and only then regenerated, never in MERGE state; the second (5 commits behind, changing.github/workflows/lint.ymlandscripts/role-word-baseline.json, both of which the families read) merged cleanly. The@objectstack/spec+@objectstack/client-reactclosure was rebuilt after each merge —check:skill-examplesandcheck:docsboth refuse a stale dist and said so, by name, rather than answering falsely green.35 / 36 green. The one non-zero is
node scripts/check-test-completeness.mjs→ exit 3, which that script defines as a MISSING or UNREADABLE LOG — NOT MEASURED, not red. Exit codes were captured by redirecting each command to its own file before any pipe.Card-named gates, quoting each gate's own verdict line:
node scripts/check-skills-token-ratchet.mjs—✓ check-skills-token-ratchet: skills/objectstack-formula/SKILL.md is 5158 tokens (ceiling 6002; headroom 844).and✓ check-skills-token-ratchet: 37 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.pnpm --filter @objectstack/spec check:skill-examples—✅ 260 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of thempnpm check:skill-compatibility—✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 78 workspace packagespnpm check:skill-identifier-liveness—check-skill-identifier-liveness OK — Leg 1: 477 citation(s) over 47 published file(s) checked against 93135 implementation word tokens (3 ledgered exemption(s)); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).Leg 2 was checked before the first deletion: none of the 8 registered exhaustive sections is in this file.pnpm --filter @objectstack/spec check:skill-docs—✅ Skill docs in syncpackages/formulapin tests —pnpm --filter @objectstack/formula exec vitest run --maxWorkers=2 src/cel-stdlib-drift.test.ts src/skill-catalog-sync.test.ts→Test Files 2 passed (2)/Tests 5 passed (5). The stdlib anchor is intact.pnpm check:role-word— green, no ratchet-DOWN, soscripts/role-word-baseline.jsonis untouched.Heavy steps ran under
scripts/pm/os-verify-lock.shwithOS_VERIFY_LOCK_SLOT=issue-14316. The pushed remote head equals the tree the gates read (291f7bd7, verified withgit ls-remote).FRM-E-01 ablation — the marker is not a phantom green
The new marker was added only after the gate passed with it, then ablated once. Mutation and restore were both proven on disk rather than by an editor's exit code:
21ee6b50; a semantic-only mutation was injected (assigning thePtagged template'sExpressionresult tonumber), leaving the block syntactically valid so the failure could only come from the semantic pass.grep -c= 1, anchor still 1,git hash-object=79936c1f≠ HEAD blob.skills/objectstack-formula/SKILL.md:243:7 error TS2322: Type '{ dialect: ... }' is not assignable to type 'number'.The right file, the right line, my block.git checkout HEAD --with an absolute path (never the bare form, which restores from a polluted index), inside atrap ... EXIT INT TERMusing absolute paths. Proven bygit hash-object=21ee6b50(equal to the HEAD blob, non-empty) andgit diff HEADempty — not by the restore's exit code.dist.Labels
skip-changeset— this PR releases nothing. Verified against the gate's own enumeration rather than assumed:scripts/check-empty-changeset.mjscarries a "GREEN 3: a skills/**-only PR carrying NO changeset (route 2)" case asserting exactly this shape must be green, and nopackages/*/package.jsonshipsskills/in itsfiles[]. Plusneeds:contract-review, because FRM-F-01, F-03 and F-04 are contract-semantics claims.🤖 Generated with Claude Code
https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1