Skip to content

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation - #13674

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep
Aug 31, 2026
Merged

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation#13674
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13660

Program anchor: #13658 — flight ① of the published-skills factual sweep, the calibration flight. That anchor stays open; this PR carries only its member ①, and the roster and re-sizing live on it.

Session, for durable attribution: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de

⛔ Governed surface — human merge, deliberately parked

skills/** is the published, customer-facing surface. This PR is a draft on purpose: auto-merge is not armed, it is not marked ready, no review is requested, and the dispatching seat will not arm it. It waits for a maintainer to merge by hand. A draft awaiting the maintainer's own merge is the correct terminal state here, not a stuck PR.

Method — PR #13577 is the spec

Every behavioral claim in skills/objectstack-formula/** (2 files, 579 lines) was inventoried FIRST, then verified in inventory order against the implementation@objectstack/formula, @objectstack/lint, @objectstack/spec — never against another document. Behavior-bearing claims were verified by reading plus an executed probe; probes were written in the worktree, run, and deleted (they are evidence, not commits — none earned a place as a real pin, and the corpus already has its drift-guard in packages/formula/src/skill-catalog-sync.test.ts).

Non-vacuity control — a claim proven true by execution. SKILL.md's "Null + string throws" section asserts a verbatim runtime message. Probed against celEngine.evaluate:

NULL+STRING :: runtime :: no such overload: dyn<null> + string

The skill's own sentence names that same overload string verbatim, byte for byte, as what null + 'foo' throws — VERIFIED against the line quoted above. Same run also pinned, by execution: floor(-1.2) == -2 and ceil(-1.2) == -1; addMonths(date('2026-01-31'), 1) clamping to Feb 28; today() and daysFromNow(45) landing on UTC midnight; all 8 advertised cel-js built-ins resolving; all three Salesforce-flavor sources failing CEL compile; unknown functions (PRIOR(), ISBLANK(), isBlnk()) failing with the "no matching overload" type error the skill promises.

The six FALSE claims, and what measured them

落点beforeafter
L175 "No date arithmetic""CEL has no numeric arithmetic on dates, so these fault and the field silently nulls (the build now rejects them).""a date mixed with a number faults and the field silently nulls (the build rejects it); end - start does not fault, it yields a duration stored as {}."
L419 CEL surfaces`defaultValue` (M9.9b)`defaultValue` (envelope only; bare string = literal)
L428 CEL surfaces`Dataset.records[*]``Seed.records[*]`
L441 cron surfaces`connector.schedule`, `etl.schedule`, `sync.schedule` — pipelines`connector.schedule` — scheduled connector sync
L475 template surfaces`system/notification` — email subject + body, SMS message, push body + message (5 fields)row replaced by `system/email-template`subject, bodyHtml, bodyText (plain strings, the email pipeline renders the holes)
L477 template surfaces`integration/connector/github` — titleTemplate, bodyTemplate (PR + release)row deleted
L155 stdlib pointer"Source: node_modules/@objectstack/formula/src/stdlib.ts" / "…/src/validate.ts""ships dist only — there is no src/ to read in an installed app. Its exported CEL_STDLIB_FUNCTIONS is the canonical list, pinned by two tests"

1. end - start does not fault. The sentence grouped three shapes under "these fault". Measured side by side (build verdict via validateExpression, runtime via celEngine.evaluate):

end - start (both dates) | build=ACCEPT | runtime=OK value={} (object)
start + 30 (literal int) | build=REJECT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + int
start + record.n (num fld) | build=ACCEPT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + dyn<double>
today() + 30 | build=REJECT | runtime=FAULT no such overload: google.protobuf.Timestamp + int

timestamp - timestamp is a valid CEL overload yielding a google.protobuf.Duration; coerce() (cel-engine.ts:1161) hands any non-Date object back key-by-key, so the field receives {}. int() and string() both refuse a Duration, so the value is unusable rather than absent — which is why the "do NOT write it" advice stands while the stated reason did not. The implementation agrees the boundary is date against a number: validate.ts:116 reads "Dates — ARITHMETIC against a number always nulls".

2. Field.defaultValue is not a string | Expression slot. The table's own heading says "All of these spec fields accept string | Expression". Executed against FieldSchema from @objectstack/spec/data:

REJECT | defaultValue: bare string "today()" | Field "d" (date): the default "today()" cannot satisfy this
field's own stored value contract … The engine stores a
literal default VERBATIM
ACCEPT | defaultValue: cel envelope | {"dialect":"cel","source":"today()"}
ACCEPT | visibleWhen: bare string (control) | {"dialect":"cel","source":"record.x == 'a'"}

The control row is the point: a real CEL slot auto-wraps, defaultValue does not. field.zod.ts:894 states the three legal shapes — envelope, runtime TOKEN, or LITERAL. On a text field the bad spelling is silent (it stores the characters today()), which is exactly the AI-authoring trap the sweep exists to close.

3. Dataset.records[*] is the wrong subject.DatasetSchema (ui/dataset.zod.ts:248) is the analytics semantic layer — dimensions / measures — and has no records key at all. The seed-records surface is SeedSchema.records (data/seed.zod.ts:96, z.array(z.record(z.string(), z.unknown()))), registered as the seed metadata kind. The rest of the skill already says "Seed" — the Boundaries table and pattern §4 both do — so the row was the outlier.

4. Two of three named cron surfaces are retired. Measured by enumerating every CronExpressionInputSchema use in the spec: six sites, none of them an etl or a sync schema. retry-policy.zod.ts:86 records that "#6414 retired the whole L2 ETL layer", and connector.zod.ts:21 records that automation/sync.zod.ts was folded into the connector in #4738. connector.schedule — the surviving one — is described in-schema as "Cron expression for scheduled sync", so the row now names it that way.

5 and 6. Two phantom template surfaces.titleTemplate / bodyTemplate return zero hits across packages/**/*.ts — the github-connector row names fields that do not exist anywhere in the repo. The system/notification row names five fields whose schemas were removed: notification.zod.ts:40 records that EmailTemplateSchema, SMSTemplateSchema, PushNotificationSchema and InAppNotificationSchema "were removed from this module too (ADR-0049 enforce-or-remove, v17 window)", left "reachable from no parent schema" by #4610. The same note records that push / in-app have no delivery implementation at all (#3197) — so that half of the row advertised a channel nothing delivers. The replacement row names what does carry holes today: EmailTemplateDefinitionSchema (system/email-template.zod.ts), whose subject / bodyHtml / bodyText are plain strings the email pipeline renders — deliberately spelled "plain strings", because a tmpl envelope would fail their z.string().

7. The stdlib pointer names paths that never ship.packages/formula/package.json declares files: ["dist", "README.md", "CHANGELOG.md"], and npm pack --dry-run --json reports 0src/ entries in the tarball. Both node_modules/@objectstack/formula/src/... paths are unreachable in an installed app. (@objectstack/specdoes publish src/**/*.zod.ts, so the two spec pointers in "See also" and references/_index.md are correct and were left alone.) The replacement also states the sync guarantee precisely: two tests, not one — cel-engine.test.ts:500 asserts every catalog entry resolves at runtime, and skill-catalog-sync.test.ts asserts this table documents every entry.

Written to the token ratchet, not around it

This file sits at zero headroom, so every correction is paid for inside the same file. No ceiling was raised — that is a maintainer's call, not a dev's.

readingbeforeafter
SKILL.md lines (whole file)554551
objectstack-formula/** package lines (all .md)579576
SKILL.md tokens (ceiling 6002)60026002
ratcheted bundle, tokens186903186903
published bundle total, tokens186903186903

The ratchet's own verdict line at head cb9a3cd44:

✓ check-skills-token-ratchet: skills/objectstack-formula/SKILL.md is 6002 tokens (ceiling 6002; headroom 0).
✓ check-skills-token-ratchet: 38 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.

Two nuances were not stuffed into ratcheted text and are recorded for a content/docs/** follow-up instead: (a) the build rejects date-plus-number only when the numeric operand is statically typed — a number-typed field stays dyn and slips through to a runtime fault (measured above, row 3); (b) the null-guard publish gate covers three surfaces (object validation rules, lifecycle hook condition, field requiredWhenvalidate-expressions.ts:820), while the skill names two.

Gates — derived, not recalled, run locally at head cb9a3cd44

Family derived from the real diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack. 13 families; 12 green, 1 NOT MEASURED:

EXIT=0 :: node scripts/check-ci-filter-parity.mjs
EXIT=0 :: node scripts/check-cross-package-test-inputs.mjs
EXIT=0 :: node scripts/check-shard-attestation.mjs
EXIT=0 :: node scripts/check-skills-token-ratchet.mjs
EXIT=3 :: node scripts/check-test-completeness.mjs ← PREREQUISITE NOT MET
EXIT=0 :: pnpm --filter @objectstack/lint run check:doc-formula-expressions
EXIT=0 :: pnpm check:agent-test-spelling
EXIT=0 :: pnpm check:cross-package-test-inputs
EXIT=0 :: pnpm check:doc-authoring
EXIT=0 :: pnpm check:pm-governed-merges
EXIT=0 :: pnpm check:role-word
EXIT=0 :: pnpm check:skill-compatibility
EXIT=0 :: pnpm check:skill-frame-sync

check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET by its own text (it grades a saved turbo run test log and none was named): recorded as NOT MEASURED, not as a red.

check:doc-formula-expressions needed @objectstack/formula and @objectstack/lint built first — its first run was a prerequisite miss, not a finding. Built, re-run, and it quotes itself:

✓ check:doc-formula-expressions self-test: 58 cases passed
✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1451 TS blocks judged clean by @objectstack/formula.
✓ check:doc-formula-expressions (spec TSDoc, #6763): 9 @example(s) judged clean across 1092 packages/spec/src files
✓ check:doc-formula-expressions (field-level `*When`, #11407): 14 predicate(s) judged clean; 6 skipped as undeterminable.

Plus two runs beyond the derived family, because they read this very corpus:

✅ 260 prose examples type-check across 3 surface(s) (pnpm --filter @objectstack/spec check:skill-examples)
Test Files 25 passed (25) · Tests 647 passed (647) (@objectstack/formula, including skill-catalog-sync)

The 647-test run is at parity with the pre-edit baseline (same 25/647), and it contains skill-catalog-sync.test.ts, the drift-guard that pins this table against CEL_STDLIB_FUNCTIONS — so the stdlib edits did not break the catalog contract.

check-nul-bytes clean: OK (scanned 7551 text file(s) … no raw ASCII control bytes).

ESLint was not run repo-wide, and the narrowing is measured, not assumed. Three readings: (1) the population read from eslint.config.mjs itself is **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} plus .ts/.tsx/.mts/.cts blocks — no config block names .md; (2) --format json over the diff's only file returns 1 result with 0 messages and no block applied; (3) type-aware linting is not enabled anywhere — projectService / parserOptions.project occur 0 times in the config, which the file's own line 328 states — so this diff cannot move any verdict on an untouched file. Zero of the changed files are in that gate's population in either direction.

No changeset

This PR releases nothing from any package — a pure skills/** documentation correction, the repo's live convention for which is no changeset. skip-changeset is applied.

Calibration numbers (this flight's purpose)

Full figures are in the report on #13660. Headline: 197 behavioral claims inventoried across 13 classes (of 579 lines, ~118 are non-behavioral — front-matter, positioning, the Boundaries routing table, Open questions, See also — and are out of the inventory by the anchor's rule). 6 FALSE (3.0%), 26 NOT MEASURABLE, 165 VERIFIED.

The density is not uniform, and this is the finding that should size flights ②–⑫: 4 of the 6 falsehoods are in the surfaces/mapping tables — 4/38 = 10.5% there against 2/159 = 1.3% everywhere else, an ~8× concentration. That matches PR #13577's pattern exactly (a mapping column asserting engine behavior), and it is the highest-yield place to point the remaining flights.

What is not here, on purpose

  • No implementation was touched. Verification found no implementation defect worth filing: every disagreement was the document being wrong about settled code, not code contradicting itself.
  • The references/_index.md file is generator-owned (pnpm --filter @objectstack/spec gen:skill-refs) and correct as measured — its @objectstack/spec pointers resolve, because that package does publish src/**/*.zod.ts. Not touched.
  • The 26 NOT MEASURABLE claims are concentrated in §5's previous-binding table and the bulk-write dispatch semantics, whose implementation lives in objectql/runtime hook dispatch rather than @objectstack/formula, plus the determinism contract (needs two full objectstack build runs). They are recorded on the card, not silently skipped.
  • The skill's "always single quotes" style rule is prescriptive, not descriptive — double-quoted CEL literals do evaluate (measured). Left alone: it is a house style, and changing it is not a factual correction.

Generated by Claude Code


Generated by Claude Code

…aim verified against the implementation
Six measured falsehoods corrected, token-neutral under the skills ratchet
(6002/6002, no ceiling raise). Verified against `@objectstack/formula`,
`@objectstack/lint` and `@objectstack/spec` by reading plus executed probes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de
@os-zhuang
os-zhuang marked this pull request as ready for review August 31, 2026 07:15
@os-zhuang
os-zhuang enabled auto-merge August 31, 2026 07:15
@os-zhuang
os-zhuang added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit a7b7a57Aug 31, 2026
34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-13660-formula-skill-sweep branch August 31, 2026 07:34
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills-sweep ①: objectstack-formula (579 lines) — behavioral-claim verification against @objectstack/formula, calibration flight

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation by claude[bot] · Pull Request #13674 · objectstack-ai/objectstack · GitHub
Skip to content

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation - #13674

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep
Aug 31, 2026
Merged

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation#13674
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13660

Program anchor: #13658 — flight ① of the published-skills factual sweep, the calibration flight. That anchor stays open; this PR carries only its member ①, and the roster and re-sizing live on it.

Session, for durable attribution: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de

⛔ Governed surface — human merge, deliberately parked

skills/** is the published, customer-facing surface. This PR is a draft on purpose: auto-merge is not armed, it is not marked ready, no review is requested, and the dispatching seat will not arm it. It waits for a maintainer to merge by hand. A draft awaiting the maintainer's own merge is the correct terminal state here, not a stuck PR.

Method — PR #13577 is the spec

Every behavioral claim in skills/objectstack-formula/** (2 files, 579 lines) was inventoried FIRST, then verified in inventory order against the implementation@objectstack/formula, @objectstack/lint, @objectstack/spec — never against another document. Behavior-bearing claims were verified by reading plus an executed probe; probes were written in the worktree, run, and deleted (they are evidence, not commits — none earned a place as a real pin, and the corpus already has its drift-guard in packages/formula/src/skill-catalog-sync.test.ts).

Non-vacuity control — a claim proven true by execution. SKILL.md's "Null + string throws" section asserts a verbatim runtime message. Probed against celEngine.evaluate:

NULL+STRING :: runtime :: no such overload: dyn<null> + string

The skill's own sentence names that same overload string verbatim, byte for byte, as what null + 'foo' throws — VERIFIED against the line quoted above. Same run also pinned, by execution: floor(-1.2) == -2 and ceil(-1.2) == -1; addMonths(date('2026-01-31'), 1) clamping to Feb 28; today() and daysFromNow(45) landing on UTC midnight; all 8 advertised cel-js built-ins resolving; all three Salesforce-flavor sources failing CEL compile; unknown functions (PRIOR(), ISBLANK(), isBlnk()) failing with the "no matching overload" type error the skill promises.

The six FALSE claims, and what measured them

落点beforeafter
L175 "No date arithmetic""CEL has no numeric arithmetic on dates, so these fault and the field silently nulls (the build now rejects them).""a date mixed with a number faults and the field silently nulls (the build rejects it); end - start does not fault, it yields a duration stored as {}."
L419 CEL surfaces`defaultValue` (M9.9b)`defaultValue` (envelope only; bare string = literal)
L428 CEL surfaces`Dataset.records[*]``Seed.records[*]`
L441 cron surfaces`connector.schedule`, `etl.schedule`, `sync.schedule` — pipelines`connector.schedule` — scheduled connector sync
L475 template surfaces`system/notification` — email subject + body, SMS message, push body + message (5 fields)row replaced by `system/email-template`subject, bodyHtml, bodyText (plain strings, the email pipeline renders the holes)
L477 template surfaces`integration/connector/github` — titleTemplate, bodyTemplate (PR + release)row deleted
L155 stdlib pointer"Source: node_modules/@objectstack/formula/src/stdlib.ts" / "…/src/validate.ts""ships dist only — there is no src/ to read in an installed app. Its exported CEL_STDLIB_FUNCTIONS is the canonical list, pinned by two tests"

1. end - start does not fault. The sentence grouped three shapes under "these fault". Measured side by side (build verdict via validateExpression, runtime via celEngine.evaluate):

end - start (both dates) | build=ACCEPT | runtime=OK value={} (object)
start + 30 (literal int) | build=REJECT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + int
start + record.n (num fld) | build=ACCEPT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + dyn<double>
today() + 30 | build=REJECT | runtime=FAULT no such overload: google.protobuf.Timestamp + int

timestamp - timestamp is a valid CEL overload yielding a google.protobuf.Duration; coerce() (cel-engine.ts:1161) hands any non-Date object back key-by-key, so the field receives {}. int() and string() both refuse a Duration, so the value is unusable rather than absent — which is why the "do NOT write it" advice stands while the stated reason did not. The implementation agrees the boundary is date against a number: validate.ts:116 reads "Dates — ARITHMETIC against a number always nulls".

2. Field.defaultValue is not a string | Expression slot. The table's own heading says "All of these spec fields accept string | Expression". Executed against FieldSchema from @objectstack/spec/data:

REJECT | defaultValue: bare string "today()" | Field "d" (date): the default "today()" cannot satisfy this
field's own stored value contract … The engine stores a
literal default VERBATIM
ACCEPT | defaultValue: cel envelope | {"dialect":"cel","source":"today()"}
ACCEPT | visibleWhen: bare string (control) | {"dialect":"cel","source":"record.x == 'a'"}

The control row is the point: a real CEL slot auto-wraps, defaultValue does not. field.zod.ts:894 states the three legal shapes — envelope, runtime TOKEN, or LITERAL. On a text field the bad spelling is silent (it stores the characters today()), which is exactly the AI-authoring trap the sweep exists to close.

3. Dataset.records[*] is the wrong subject.DatasetSchema (ui/dataset.zod.ts:248) is the analytics semantic layer — dimensions / measures — and has no records key at all. The seed-records surface is SeedSchema.records (data/seed.zod.ts:96, z.array(z.record(z.string(), z.unknown()))), registered as the seed metadata kind. The rest of the skill already says "Seed" — the Boundaries table and pattern §4 both do — so the row was the outlier.

4. Two of three named cron surfaces are retired. Measured by enumerating every CronExpressionInputSchema use in the spec: six sites, none of them an etl or a sync schema. retry-policy.zod.ts:86 records that "#6414 retired the whole L2 ETL layer", and connector.zod.ts:21 records that automation/sync.zod.ts was folded into the connector in #4738. connector.schedule — the surviving one — is described in-schema as "Cron expression for scheduled sync", so the row now names it that way.

5 and 6. Two phantom template surfaces.titleTemplate / bodyTemplate return zero hits across packages/**/*.ts — the github-connector row names fields that do not exist anywhere in the repo. The system/notification row names five fields whose schemas were removed: notification.zod.ts:40 records that EmailTemplateSchema, SMSTemplateSchema, PushNotificationSchema and InAppNotificationSchema "were removed from this module too (ADR-0049 enforce-or-remove, v17 window)", left "reachable from no parent schema" by #4610. The same note records that push / in-app have no delivery implementation at all (#3197) — so that half of the row advertised a channel nothing delivers. The replacement row names what does carry holes today: EmailTemplateDefinitionSchema (system/email-template.zod.ts), whose subject / bodyHtml / bodyText are plain strings the email pipeline renders — deliberately spelled "plain strings", because a tmpl envelope would fail their z.string().

7. The stdlib pointer names paths that never ship.packages/formula/package.json declares files: ["dist", "README.md", "CHANGELOG.md"], and npm pack --dry-run --json reports 0src/ entries in the tarball. Both node_modules/@objectstack/formula/src/... paths are unreachable in an installed app. (@objectstack/specdoes publish src/**/*.zod.ts, so the two spec pointers in "See also" and references/_index.md are correct and were left alone.) The replacement also states the sync guarantee precisely: two tests, not one — cel-engine.test.ts:500 asserts every catalog entry resolves at runtime, and skill-catalog-sync.test.ts asserts this table documents every entry.

Written to the token ratchet, not around it

This file sits at zero headroom, so every correction is paid for inside the same file. No ceiling was raised — that is a maintainer's call, not a dev's.

readingbeforeafter
SKILL.md lines (whole file)554551
objectstack-formula/** package lines (all .md)579576
SKILL.md tokens (ceiling 6002)60026002
ratcheted bundle, tokens186903186903
published bundle total, tokens186903186903

The ratchet's own verdict line at head cb9a3cd44:

✓ check-skills-token-ratchet: skills/objectstack-formula/SKILL.md is 6002 tokens (ceiling 6002; headroom 0).
✓ check-skills-token-ratchet: 38 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.

Two nuances were not stuffed into ratcheted text and are recorded for a content/docs/** follow-up instead: (a) the build rejects date-plus-number only when the numeric operand is statically typed — a number-typed field stays dyn and slips through to a runtime fault (measured above, row 3); (b) the null-guard publish gate covers three surfaces (object validation rules, lifecycle hook condition, field requiredWhenvalidate-expressions.ts:820), while the skill names two.

Gates — derived, not recalled, run locally at head cb9a3cd44

Family derived from the real diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack. 13 families; 12 green, 1 NOT MEASURED:

EXIT=0 :: node scripts/check-ci-filter-parity.mjs
EXIT=0 :: node scripts/check-cross-package-test-inputs.mjs
EXIT=0 :: node scripts/check-shard-attestation.mjs
EXIT=0 :: node scripts/check-skills-token-ratchet.mjs
EXIT=3 :: node scripts/check-test-completeness.mjs ← PREREQUISITE NOT MET
EXIT=0 :: pnpm --filter @objectstack/lint run check:doc-formula-expressions
EXIT=0 :: pnpm check:agent-test-spelling
EXIT=0 :: pnpm check:cross-package-test-inputs
EXIT=0 :: pnpm check:doc-authoring
EXIT=0 :: pnpm check:pm-governed-merges
EXIT=0 :: pnpm check:role-word
EXIT=0 :: pnpm check:skill-compatibility
EXIT=0 :: pnpm check:skill-frame-sync

check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET by its own text (it grades a saved turbo run test log and none was named): recorded as NOT MEASURED, not as a red.

check:doc-formula-expressions needed @objectstack/formula and @objectstack/lint built first — its first run was a prerequisite miss, not a finding. Built, re-run, and it quotes itself:

✓ check:doc-formula-expressions self-test: 58 cases passed
✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1451 TS blocks judged clean by @objectstack/formula.
✓ check:doc-formula-expressions (spec TSDoc, #6763): 9 @example(s) judged clean across 1092 packages/spec/src files
✓ check:doc-formula-expressions (field-level `*When`, #11407): 14 predicate(s) judged clean; 6 skipped as undeterminable.

Plus two runs beyond the derived family, because they read this very corpus:

✅ 260 prose examples type-check across 3 surface(s) (pnpm --filter @objectstack/spec check:skill-examples)
Test Files 25 passed (25) · Tests 647 passed (647) (@objectstack/formula, including skill-catalog-sync)

The 647-test run is at parity with the pre-edit baseline (same 25/647), and it contains skill-catalog-sync.test.ts, the drift-guard that pins this table against CEL_STDLIB_FUNCTIONS — so the stdlib edits did not break the catalog contract.

check-nul-bytes clean: OK (scanned 7551 text file(s) … no raw ASCII control bytes).

ESLint was not run repo-wide, and the narrowing is measured, not assumed. Three readings: (1) the population read from eslint.config.mjs itself is **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} plus .ts/.tsx/.mts/.cts blocks — no config block names .md; (2) --format json over the diff's only file returns 1 result with 0 messages and no block applied; (3) type-aware linting is not enabled anywhere — projectService / parserOptions.project occur 0 times in the config, which the file's own line 328 states — so this diff cannot move any verdict on an untouched file. Zero of the changed files are in that gate's population in either direction.

No changeset

This PR releases nothing from any package — a pure skills/** documentation correction, the repo's live convention for which is no changeset. skip-changeset is applied.

Calibration numbers (this flight's purpose)

Full figures are in the report on #13660. Headline: 197 behavioral claims inventoried across 13 classes (of 579 lines, ~118 are non-behavioral — front-matter, positioning, the Boundaries routing table, Open questions, See also — and are out of the inventory by the anchor's rule). 6 FALSE (3.0%), 26 NOT MEASURABLE, 165 VERIFIED.

The density is not uniform, and this is the finding that should size flights ②–⑫: 4 of the 6 falsehoods are in the surfaces/mapping tables — 4/38 = 10.5% there against 2/159 = 1.3% everywhere else, an ~8× concentration. That matches PR #13577's pattern exactly (a mapping column asserting engine behavior), and it is the highest-yield place to point the remaining flights.

What is not here, on purpose

  • No implementation was touched. Verification found no implementation defect worth filing: every disagreement was the document being wrong about settled code, not code contradicting itself.
  • The references/_index.md file is generator-owned (pnpm --filter @objectstack/spec gen:skill-refs) and correct as measured — its @objectstack/spec pointers resolve, because that package does publish src/**/*.zod.ts. Not touched.
  • The 26 NOT MEASURABLE claims are concentrated in §5's previous-binding table and the bulk-write dispatch semantics, whose implementation lives in objectql/runtime hook dispatch rather than @objectstack/formula, plus the determinism contract (needs two full objectstack build runs). They are recorded on the card, not silently skipped.
  • The skill's "always single quotes" style rule is prescriptive, not descriptive — double-quoted CEL literals do evaluate (measured). Left alone: it is a house style, and changing it is not a factual correction.

Generated by Claude Code


Generated by Claude Code

…aim verified against the implementation
Six measured falsehoods corrected, token-neutral under the skills ratchet
(6002/6002, no ceiling raise). Verified against `@objectstack/formula`,
`@objectstack/lint` and `@objectstack/spec` by reading plus executed probes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de
@os-zhuang
os-zhuang marked this pull request as ready for review August 31, 2026 07:15
@os-zhuang
os-zhuang enabled auto-merge August 31, 2026 07:15
@os-zhuang
os-zhuang added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit a7b7a57Aug 31, 2026
34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-13660-formula-skill-sweep branch August 31, 2026 07:34
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills-sweep ①: objectstack-formula (579 lines) — behavioral-claim verification against @objectstack/formula, calibration flight

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation by claude[bot] · Pull Request #13674 · objectstack-ai/objectstack · GitHub
Skip to content

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation - #13674

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep
Aug 31, 2026
Merged

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation#13674
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13660

Program anchor: #13658 — flight ① of the published-skills factual sweep, the calibration flight. That anchor stays open; this PR carries only its member ①, and the roster and re-sizing live on it.

Session, for durable attribution: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de

⛔ Governed surface — human merge, deliberately parked

skills/** is the published, customer-facing surface. This PR is a draft on purpose: auto-merge is not armed, it is not marked ready, no review is requested, and the dispatching seat will not arm it. It waits for a maintainer to merge by hand. A draft awaiting the maintainer's own merge is the correct terminal state here, not a stuck PR.

Method — PR #13577 is the spec

Every behavioral claim in skills/objectstack-formula/** (2 files, 579 lines) was inventoried FIRST, then verified in inventory order against the implementation@objectstack/formula, @objectstack/lint, @objectstack/spec — never against another document. Behavior-bearing claims were verified by reading plus an executed probe; probes were written in the worktree, run, and deleted (they are evidence, not commits — none earned a place as a real pin, and the corpus already has its drift-guard in packages/formula/src/skill-catalog-sync.test.ts).

Non-vacuity control — a claim proven true by execution. SKILL.md's "Null + string throws" section asserts a verbatim runtime message. Probed against celEngine.evaluate:

NULL+STRING :: runtime :: no such overload: dyn<null> + string

The skill's own sentence names that same overload string verbatim, byte for byte, as what null + 'foo' throws — VERIFIED against the line quoted above. Same run also pinned, by execution: floor(-1.2) == -2 and ceil(-1.2) == -1; addMonths(date('2026-01-31'), 1) clamping to Feb 28; today() and daysFromNow(45) landing on UTC midnight; all 8 advertised cel-js built-ins resolving; all three Salesforce-flavor sources failing CEL compile; unknown functions (PRIOR(), ISBLANK(), isBlnk()) failing with the "no matching overload" type error the skill promises.

The six FALSE claims, and what measured them

落点beforeafter
L175 "No date arithmetic""CEL has no numeric arithmetic on dates, so these fault and the field silently nulls (the build now rejects them).""a date mixed with a number faults and the field silently nulls (the build rejects it); end - start does not fault, it yields a duration stored as {}."
L419 CEL surfaces`defaultValue` (M9.9b)`defaultValue` (envelope only; bare string = literal)
L428 CEL surfaces`Dataset.records[*]``Seed.records[*]`
L441 cron surfaces`connector.schedule`, `etl.schedule`, `sync.schedule` — pipelines`connector.schedule` — scheduled connector sync
L475 template surfaces`system/notification` — email subject + body, SMS message, push body + message (5 fields)row replaced by `system/email-template`subject, bodyHtml, bodyText (plain strings, the email pipeline renders the holes)
L477 template surfaces`integration/connector/github` — titleTemplate, bodyTemplate (PR + release)row deleted
L155 stdlib pointer"Source: node_modules/@objectstack/formula/src/stdlib.ts" / "…/src/validate.ts""ships dist only — there is no src/ to read in an installed app. Its exported CEL_STDLIB_FUNCTIONS is the canonical list, pinned by two tests"

1. end - start does not fault. The sentence grouped three shapes under "these fault". Measured side by side (build verdict via validateExpression, runtime via celEngine.evaluate):

end - start (both dates) | build=ACCEPT | runtime=OK value={} (object)
start + 30 (literal int) | build=REJECT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + int
start + record.n (num fld) | build=ACCEPT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + dyn<double>
today() + 30 | build=REJECT | runtime=FAULT no such overload: google.protobuf.Timestamp + int

timestamp - timestamp is a valid CEL overload yielding a google.protobuf.Duration; coerce() (cel-engine.ts:1161) hands any non-Date object back key-by-key, so the field receives {}. int() and string() both refuse a Duration, so the value is unusable rather than absent — which is why the "do NOT write it" advice stands while the stated reason did not. The implementation agrees the boundary is date against a number: validate.ts:116 reads "Dates — ARITHMETIC against a number always nulls".

2. Field.defaultValue is not a string | Expression slot. The table's own heading says "All of these spec fields accept string | Expression". Executed against FieldSchema from @objectstack/spec/data:

REJECT | defaultValue: bare string "today()" | Field "d" (date): the default "today()" cannot satisfy this
field's own stored value contract … The engine stores a
literal default VERBATIM
ACCEPT | defaultValue: cel envelope | {"dialect":"cel","source":"today()"}
ACCEPT | visibleWhen: bare string (control) | {"dialect":"cel","source":"record.x == 'a'"}

The control row is the point: a real CEL slot auto-wraps, defaultValue does not. field.zod.ts:894 states the three legal shapes — envelope, runtime TOKEN, or LITERAL. On a text field the bad spelling is silent (it stores the characters today()), which is exactly the AI-authoring trap the sweep exists to close.

3. Dataset.records[*] is the wrong subject.DatasetSchema (ui/dataset.zod.ts:248) is the analytics semantic layer — dimensions / measures — and has no records key at all. The seed-records surface is SeedSchema.records (data/seed.zod.ts:96, z.array(z.record(z.string(), z.unknown()))), registered as the seed metadata kind. The rest of the skill already says "Seed" — the Boundaries table and pattern §4 both do — so the row was the outlier.

4. Two of three named cron surfaces are retired. Measured by enumerating every CronExpressionInputSchema use in the spec: six sites, none of them an etl or a sync schema. retry-policy.zod.ts:86 records that "#6414 retired the whole L2 ETL layer", and connector.zod.ts:21 records that automation/sync.zod.ts was folded into the connector in #4738. connector.schedule — the surviving one — is described in-schema as "Cron expression for scheduled sync", so the row now names it that way.

5 and 6. Two phantom template surfaces.titleTemplate / bodyTemplate return zero hits across packages/**/*.ts — the github-connector row names fields that do not exist anywhere in the repo. The system/notification row names five fields whose schemas were removed: notification.zod.ts:40 records that EmailTemplateSchema, SMSTemplateSchema, PushNotificationSchema and InAppNotificationSchema "were removed from this module too (ADR-0049 enforce-or-remove, v17 window)", left "reachable from no parent schema" by #4610. The same note records that push / in-app have no delivery implementation at all (#3197) — so that half of the row advertised a channel nothing delivers. The replacement row names what does carry holes today: EmailTemplateDefinitionSchema (system/email-template.zod.ts), whose subject / bodyHtml / bodyText are plain strings the email pipeline renders — deliberately spelled "plain strings", because a tmpl envelope would fail their z.string().

7. The stdlib pointer names paths that never ship.packages/formula/package.json declares files: ["dist", "README.md", "CHANGELOG.md"], and npm pack --dry-run --json reports 0src/ entries in the tarball. Both node_modules/@objectstack/formula/src/... paths are unreachable in an installed app. (@objectstack/specdoes publish src/**/*.zod.ts, so the two spec pointers in "See also" and references/_index.md are correct and were left alone.) The replacement also states the sync guarantee precisely: two tests, not one — cel-engine.test.ts:500 asserts every catalog entry resolves at runtime, and skill-catalog-sync.test.ts asserts this table documents every entry.

Written to the token ratchet, not around it

This file sits at zero headroom, so every correction is paid for inside the same file. No ceiling was raised — that is a maintainer's call, not a dev's.

readingbeforeafter
SKILL.md lines (whole file)554551
objectstack-formula/** package lines (all .md)579576
SKILL.md tokens (ceiling 6002)60026002
ratcheted bundle, tokens186903186903
published bundle total, tokens186903186903

The ratchet's own verdict line at head cb9a3cd44:

✓ check-skills-token-ratchet: skills/objectstack-formula/SKILL.md is 6002 tokens (ceiling 6002; headroom 0).
✓ check-skills-token-ratchet: 38 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.

Two nuances were not stuffed into ratcheted text and are recorded for a content/docs/** follow-up instead: (a) the build rejects date-plus-number only when the numeric operand is statically typed — a number-typed field stays dyn and slips through to a runtime fault (measured above, row 3); (b) the null-guard publish gate covers three surfaces (object validation rules, lifecycle hook condition, field requiredWhenvalidate-expressions.ts:820), while the skill names two.

Gates — derived, not recalled, run locally at head cb9a3cd44

Family derived from the real diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack. 13 families; 12 green, 1 NOT MEASURED:

EXIT=0 :: node scripts/check-ci-filter-parity.mjs
EXIT=0 :: node scripts/check-cross-package-test-inputs.mjs
EXIT=0 :: node scripts/check-shard-attestation.mjs
EXIT=0 :: node scripts/check-skills-token-ratchet.mjs
EXIT=3 :: node scripts/check-test-completeness.mjs ← PREREQUISITE NOT MET
EXIT=0 :: pnpm --filter @objectstack/lint run check:doc-formula-expressions
EXIT=0 :: pnpm check:agent-test-spelling
EXIT=0 :: pnpm check:cross-package-test-inputs
EXIT=0 :: pnpm check:doc-authoring
EXIT=0 :: pnpm check:pm-governed-merges
EXIT=0 :: pnpm check:role-word
EXIT=0 :: pnpm check:skill-compatibility
EXIT=0 :: pnpm check:skill-frame-sync

check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET by its own text (it grades a saved turbo run test log and none was named): recorded as NOT MEASURED, not as a red.

check:doc-formula-expressions needed @objectstack/formula and @objectstack/lint built first — its first run was a prerequisite miss, not a finding. Built, re-run, and it quotes itself:

✓ check:doc-formula-expressions self-test: 58 cases passed
✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1451 TS blocks judged clean by @objectstack/formula.
✓ check:doc-formula-expressions (spec TSDoc, #6763): 9 @example(s) judged clean across 1092 packages/spec/src files
✓ check:doc-formula-expressions (field-level `*When`, #11407): 14 predicate(s) judged clean; 6 skipped as undeterminable.

Plus two runs beyond the derived family, because they read this very corpus:

✅ 260 prose examples type-check across 3 surface(s) (pnpm --filter @objectstack/spec check:skill-examples)
Test Files 25 passed (25) · Tests 647 passed (647) (@objectstack/formula, including skill-catalog-sync)

The 647-test run is at parity with the pre-edit baseline (same 25/647), and it contains skill-catalog-sync.test.ts, the drift-guard that pins this table against CEL_STDLIB_FUNCTIONS — so the stdlib edits did not break the catalog contract.

check-nul-bytes clean: OK (scanned 7551 text file(s) … no raw ASCII control bytes).

ESLint was not run repo-wide, and the narrowing is measured, not assumed. Three readings: (1) the population read from eslint.config.mjs itself is **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} plus .ts/.tsx/.mts/.cts blocks — no config block names .md; (2) --format json over the diff's only file returns 1 result with 0 messages and no block applied; (3) type-aware linting is not enabled anywhere — projectService / parserOptions.project occur 0 times in the config, which the file's own line 328 states — so this diff cannot move any verdict on an untouched file. Zero of the changed files are in that gate's population in either direction.

No changeset

This PR releases nothing from any package — a pure skills/** documentation correction, the repo's live convention for which is no changeset. skip-changeset is applied.

Calibration numbers (this flight's purpose)

Full figures are in the report on #13660. Headline: 197 behavioral claims inventoried across 13 classes (of 579 lines, ~118 are non-behavioral — front-matter, positioning, the Boundaries routing table, Open questions, See also — and are out of the inventory by the anchor's rule). 6 FALSE (3.0%), 26 NOT MEASURABLE, 165 VERIFIED.

The density is not uniform, and this is the finding that should size flights ②–⑫: 4 of the 6 falsehoods are in the surfaces/mapping tables — 4/38 = 10.5% there against 2/159 = 1.3% everywhere else, an ~8× concentration. That matches PR #13577's pattern exactly (a mapping column asserting engine behavior), and it is the highest-yield place to point the remaining flights.

What is not here, on purpose

  • No implementation was touched. Verification found no implementation defect worth filing: every disagreement was the document being wrong about settled code, not code contradicting itself.
  • The references/_index.md file is generator-owned (pnpm --filter @objectstack/spec gen:skill-refs) and correct as measured — its @objectstack/spec pointers resolve, because that package does publish src/**/*.zod.ts. Not touched.
  • The 26 NOT MEASURABLE claims are concentrated in §5's previous-binding table and the bulk-write dispatch semantics, whose implementation lives in objectql/runtime hook dispatch rather than @objectstack/formula, plus the determinism contract (needs two full objectstack build runs). They are recorded on the card, not silently skipped.
  • The skill's "always single quotes" style rule is prescriptive, not descriptive — double-quoted CEL literals do evaluate (measured). Left alone: it is a house style, and changing it is not a factual correction.

Generated by Claude Code


Generated by Claude Code

…aim verified against the implementation
Six measured falsehoods corrected, token-neutral under the skills ratchet
(6002/6002, no ceiling raise). Verified against `@objectstack/formula`,
`@objectstack/lint` and `@objectstack/spec` by reading plus executed probes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de
@os-zhuang
os-zhuang marked this pull request as ready for review August 31, 2026 07:15
@os-zhuang
os-zhuang enabled auto-merge August 31, 2026 07:15
@os-zhuang
os-zhuang added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit a7b7a57Aug 31, 2026
34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-13660-formula-skill-sweep branch August 31, 2026 07:34
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills-sweep ①: objectstack-formula (579 lines) — behavioral-claim verification against @objectstack/formula, calibration flight

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation by claude[bot] · Pull Request #13674 · objectstack-ai/objectstack · GitHub
Skip to content

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation - #13674

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep
Aug 31, 2026
Merged

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation#13674
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13660

Program anchor: #13658 — flight ① of the published-skills factual sweep, the calibration flight. That anchor stays open; this PR carries only its member ①, and the roster and re-sizing live on it.

Session, for durable attribution: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de

⛔ Governed surface — human merge, deliberately parked

skills/** is the published, customer-facing surface. This PR is a draft on purpose: auto-merge is not armed, it is not marked ready, no review is requested, and the dispatching seat will not arm it. It waits for a maintainer to merge by hand. A draft awaiting the maintainer's own merge is the correct terminal state here, not a stuck PR.

Method — PR #13577 is the spec

Every behavioral claim in skills/objectstack-formula/** (2 files, 579 lines) was inventoried FIRST, then verified in inventory order against the implementation@objectstack/formula, @objectstack/lint, @objectstack/spec — never against another document. Behavior-bearing claims were verified by reading plus an executed probe; probes were written in the worktree, run, and deleted (they are evidence, not commits — none earned a place as a real pin, and the corpus already has its drift-guard in packages/formula/src/skill-catalog-sync.test.ts).

Non-vacuity control — a claim proven true by execution. SKILL.md's "Null + string throws" section asserts a verbatim runtime message. Probed against celEngine.evaluate:

NULL+STRING :: runtime :: no such overload: dyn<null> + string

The skill's own sentence names that same overload string verbatim, byte for byte, as what null + 'foo' throws — VERIFIED against the line quoted above. Same run also pinned, by execution: floor(-1.2) == -2 and ceil(-1.2) == -1; addMonths(date('2026-01-31'), 1) clamping to Feb 28; today() and daysFromNow(45) landing on UTC midnight; all 8 advertised cel-js built-ins resolving; all three Salesforce-flavor sources failing CEL compile; unknown functions (PRIOR(), ISBLANK(), isBlnk()) failing with the "no matching overload" type error the skill promises.

The six FALSE claims, and what measured them

落点beforeafter
L175 "No date arithmetic""CEL has no numeric arithmetic on dates, so these fault and the field silently nulls (the build now rejects them).""a date mixed with a number faults and the field silently nulls (the build rejects it); end - start does not fault, it yields a duration stored as {}."
L419 CEL surfaces`defaultValue` (M9.9b)`defaultValue` (envelope only; bare string = literal)
L428 CEL surfaces`Dataset.records[*]``Seed.records[*]`
L441 cron surfaces`connector.schedule`, `etl.schedule`, `sync.schedule` — pipelines`connector.schedule` — scheduled connector sync
L475 template surfaces`system/notification` — email subject + body, SMS message, push body + message (5 fields)row replaced by `system/email-template`subject, bodyHtml, bodyText (plain strings, the email pipeline renders the holes)
L477 template surfaces`integration/connector/github` — titleTemplate, bodyTemplate (PR + release)row deleted
L155 stdlib pointer"Source: node_modules/@objectstack/formula/src/stdlib.ts" / "…/src/validate.ts""ships dist only — there is no src/ to read in an installed app. Its exported CEL_STDLIB_FUNCTIONS is the canonical list, pinned by two tests"

1. end - start does not fault. The sentence grouped three shapes under "these fault". Measured side by side (build verdict via validateExpression, runtime via celEngine.evaluate):

end - start (both dates) | build=ACCEPT | runtime=OK value={} (object)
start + 30 (literal int) | build=REJECT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + int
start + record.n (num fld) | build=ACCEPT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + dyn<double>
today() + 30 | build=REJECT | runtime=FAULT no such overload: google.protobuf.Timestamp + int

timestamp - timestamp is a valid CEL overload yielding a google.protobuf.Duration; coerce() (cel-engine.ts:1161) hands any non-Date object back key-by-key, so the field receives {}. int() and string() both refuse a Duration, so the value is unusable rather than absent — which is why the "do NOT write it" advice stands while the stated reason did not. The implementation agrees the boundary is date against a number: validate.ts:116 reads "Dates — ARITHMETIC against a number always nulls".

2. Field.defaultValue is not a string | Expression slot. The table's own heading says "All of these spec fields accept string | Expression". Executed against FieldSchema from @objectstack/spec/data:

REJECT | defaultValue: bare string "today()" | Field "d" (date): the default "today()" cannot satisfy this
field's own stored value contract … The engine stores a
literal default VERBATIM
ACCEPT | defaultValue: cel envelope | {"dialect":"cel","source":"today()"}
ACCEPT | visibleWhen: bare string (control) | {"dialect":"cel","source":"record.x == 'a'"}

The control row is the point: a real CEL slot auto-wraps, defaultValue does not. field.zod.ts:894 states the three legal shapes — envelope, runtime TOKEN, or LITERAL. On a text field the bad spelling is silent (it stores the characters today()), which is exactly the AI-authoring trap the sweep exists to close.

3. Dataset.records[*] is the wrong subject.DatasetSchema (ui/dataset.zod.ts:248) is the analytics semantic layer — dimensions / measures — and has no records key at all. The seed-records surface is SeedSchema.records (data/seed.zod.ts:96, z.array(z.record(z.string(), z.unknown()))), registered as the seed metadata kind. The rest of the skill already says "Seed" — the Boundaries table and pattern §4 both do — so the row was the outlier.

4. Two of three named cron surfaces are retired. Measured by enumerating every CronExpressionInputSchema use in the spec: six sites, none of them an etl or a sync schema. retry-policy.zod.ts:86 records that "#6414 retired the whole L2 ETL layer", and connector.zod.ts:21 records that automation/sync.zod.ts was folded into the connector in #4738. connector.schedule — the surviving one — is described in-schema as "Cron expression for scheduled sync", so the row now names it that way.

5 and 6. Two phantom template surfaces.titleTemplate / bodyTemplate return zero hits across packages/**/*.ts — the github-connector row names fields that do not exist anywhere in the repo. The system/notification row names five fields whose schemas were removed: notification.zod.ts:40 records that EmailTemplateSchema, SMSTemplateSchema, PushNotificationSchema and InAppNotificationSchema "were removed from this module too (ADR-0049 enforce-or-remove, v17 window)", left "reachable from no parent schema" by #4610. The same note records that push / in-app have no delivery implementation at all (#3197) — so that half of the row advertised a channel nothing delivers. The replacement row names what does carry holes today: EmailTemplateDefinitionSchema (system/email-template.zod.ts), whose subject / bodyHtml / bodyText are plain strings the email pipeline renders — deliberately spelled "plain strings", because a tmpl envelope would fail their z.string().

7. The stdlib pointer names paths that never ship.packages/formula/package.json declares files: ["dist", "README.md", "CHANGELOG.md"], and npm pack --dry-run --json reports 0src/ entries in the tarball. Both node_modules/@objectstack/formula/src/... paths are unreachable in an installed app. (@objectstack/specdoes publish src/**/*.zod.ts, so the two spec pointers in "See also" and references/_index.md are correct and were left alone.) The replacement also states the sync guarantee precisely: two tests, not one — cel-engine.test.ts:500 asserts every catalog entry resolves at runtime, and skill-catalog-sync.test.ts asserts this table documents every entry.

Written to the token ratchet, not around it

This file sits at zero headroom, so every correction is paid for inside the same file. No ceiling was raised — that is a maintainer's call, not a dev's.

readingbeforeafter
SKILL.md lines (whole file)554551
objectstack-formula/** package lines (all .md)579576
SKILL.md tokens (ceiling 6002)60026002
ratcheted bundle, tokens186903186903
published bundle total, tokens186903186903

The ratchet's own verdict line at head cb9a3cd44:

✓ check-skills-token-ratchet: skills/objectstack-formula/SKILL.md is 6002 tokens (ceiling 6002; headroom 0).
✓ check-skills-token-ratchet: 38 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.

Two nuances were not stuffed into ratcheted text and are recorded for a content/docs/** follow-up instead: (a) the build rejects date-plus-number only when the numeric operand is statically typed — a number-typed field stays dyn and slips through to a runtime fault (measured above, row 3); (b) the null-guard publish gate covers three surfaces (object validation rules, lifecycle hook condition, field requiredWhenvalidate-expressions.ts:820), while the skill names two.

Gates — derived, not recalled, run locally at head cb9a3cd44

Family derived from the real diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack. 13 families; 12 green, 1 NOT MEASURED:

EXIT=0 :: node scripts/check-ci-filter-parity.mjs
EXIT=0 :: node scripts/check-cross-package-test-inputs.mjs
EXIT=0 :: node scripts/check-shard-attestation.mjs
EXIT=0 :: node scripts/check-skills-token-ratchet.mjs
EXIT=3 :: node scripts/check-test-completeness.mjs ← PREREQUISITE NOT MET
EXIT=0 :: pnpm --filter @objectstack/lint run check:doc-formula-expressions
EXIT=0 :: pnpm check:agent-test-spelling
EXIT=0 :: pnpm check:cross-package-test-inputs
EXIT=0 :: pnpm check:doc-authoring
EXIT=0 :: pnpm check:pm-governed-merges
EXIT=0 :: pnpm check:role-word
EXIT=0 :: pnpm check:skill-compatibility
EXIT=0 :: pnpm check:skill-frame-sync

check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET by its own text (it grades a saved turbo run test log and none was named): recorded as NOT MEASURED, not as a red.

check:doc-formula-expressions needed @objectstack/formula and @objectstack/lint built first — its first run was a prerequisite miss, not a finding. Built, re-run, and it quotes itself:

✓ check:doc-formula-expressions self-test: 58 cases passed
✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1451 TS blocks judged clean by @objectstack/formula.
✓ check:doc-formula-expressions (spec TSDoc, #6763): 9 @example(s) judged clean across 1092 packages/spec/src files
✓ check:doc-formula-expressions (field-level `*When`, #11407): 14 predicate(s) judged clean; 6 skipped as undeterminable.

Plus two runs beyond the derived family, because they read this very corpus:

✅ 260 prose examples type-check across 3 surface(s) (pnpm --filter @objectstack/spec check:skill-examples)
Test Files 25 passed (25) · Tests 647 passed (647) (@objectstack/formula, including skill-catalog-sync)

The 647-test run is at parity with the pre-edit baseline (same 25/647), and it contains skill-catalog-sync.test.ts, the drift-guard that pins this table against CEL_STDLIB_FUNCTIONS — so the stdlib edits did not break the catalog contract.

check-nul-bytes clean: OK (scanned 7551 text file(s) … no raw ASCII control bytes).

ESLint was not run repo-wide, and the narrowing is measured, not assumed. Three readings: (1) the population read from eslint.config.mjs itself is **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} plus .ts/.tsx/.mts/.cts blocks — no config block names .md; (2) --format json over the diff's only file returns 1 result with 0 messages and no block applied; (3) type-aware linting is not enabled anywhere — projectService / parserOptions.project occur 0 times in the config, which the file's own line 328 states — so this diff cannot move any verdict on an untouched file. Zero of the changed files are in that gate's population in either direction.

No changeset

This PR releases nothing from any package — a pure skills/** documentation correction, the repo's live convention for which is no changeset. skip-changeset is applied.

Calibration numbers (this flight's purpose)

Full figures are in the report on #13660. Headline: 197 behavioral claims inventoried across 13 classes (of 579 lines, ~118 are non-behavioral — front-matter, positioning, the Boundaries routing table, Open questions, See also — and are out of the inventory by the anchor's rule). 6 FALSE (3.0%), 26 NOT MEASURABLE, 165 VERIFIED.

The density is not uniform, and this is the finding that should size flights ②–⑫: 4 of the 6 falsehoods are in the surfaces/mapping tables — 4/38 = 10.5% there against 2/159 = 1.3% everywhere else, an ~8× concentration. That matches PR #13577's pattern exactly (a mapping column asserting engine behavior), and it is the highest-yield place to point the remaining flights.

What is not here, on purpose

  • No implementation was touched. Verification found no implementation defect worth filing: every disagreement was the document being wrong about settled code, not code contradicting itself.
  • The references/_index.md file is generator-owned (pnpm --filter @objectstack/spec gen:skill-refs) and correct as measured — its @objectstack/spec pointers resolve, because that package does publish src/**/*.zod.ts. Not touched.
  • The 26 NOT MEASURABLE claims are concentrated in §5's previous-binding table and the bulk-write dispatch semantics, whose implementation lives in objectql/runtime hook dispatch rather than @objectstack/formula, plus the determinism contract (needs two full objectstack build runs). They are recorded on the card, not silently skipped.
  • The skill's "always single quotes" style rule is prescriptive, not descriptive — double-quoted CEL literals do evaluate (measured). Left alone: it is a house style, and changing it is not a factual correction.

Generated by Claude Code


Generated by Claude Code

…aim verified against the implementation
Six measured falsehoods corrected, token-neutral under the skills ratchet
(6002/6002, no ceiling raise). Verified against `@objectstack/formula`,
`@objectstack/lint` and `@objectstack/spec` by reading plus executed probes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de
@os-zhuang
os-zhuang marked this pull request as ready for review August 31, 2026 07:15
@os-zhuang
os-zhuang enabled auto-merge August 31, 2026 07:15
@os-zhuang
os-zhuang added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit a7b7a57Aug 31, 2026
34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-13660-formula-skill-sweep branch August 31, 2026 07:34
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills-sweep ①: objectstack-formula (579 lines) — behavioral-claim verification against @objectstack/formula, calibration flight

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation by claude[bot] · Pull Request #13674 · objectstack-ai/objectstack · GitHub
Skip to content

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation - #13674

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep
Aug 31, 2026
Merged

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation#13674
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13660

Program anchor: #13658 — flight ① of the published-skills factual sweep, the calibration flight. That anchor stays open; this PR carries only its member ①, and the roster and re-sizing live on it.

Session, for durable attribution: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de

⛔ Governed surface — human merge, deliberately parked

skills/** is the published, customer-facing surface. This PR is a draft on purpose: auto-merge is not armed, it is not marked ready, no review is requested, and the dispatching seat will not arm it. It waits for a maintainer to merge by hand. A draft awaiting the maintainer's own merge is the correct terminal state here, not a stuck PR.

Method — PR #13577 is the spec

Every behavioral claim in skills/objectstack-formula/** (2 files, 579 lines) was inventoried FIRST, then verified in inventory order against the implementation@objectstack/formula, @objectstack/lint, @objectstack/spec — never against another document. Behavior-bearing claims were verified by reading plus an executed probe; probes were written in the worktree, run, and deleted (they are evidence, not commits — none earned a place as a real pin, and the corpus already has its drift-guard in packages/formula/src/skill-catalog-sync.test.ts).

Non-vacuity control — a claim proven true by execution. SKILL.md's "Null + string throws" section asserts a verbatim runtime message. Probed against celEngine.evaluate:

NULL+STRING :: runtime :: no such overload: dyn<null> + string

The skill's own sentence names that same overload string verbatim, byte for byte, as what null + 'foo' throws — VERIFIED against the line quoted above. Same run also pinned, by execution: floor(-1.2) == -2 and ceil(-1.2) == -1; addMonths(date('2026-01-31'), 1) clamping to Feb 28; today() and daysFromNow(45) landing on UTC midnight; all 8 advertised cel-js built-ins resolving; all three Salesforce-flavor sources failing CEL compile; unknown functions (PRIOR(), ISBLANK(), isBlnk()) failing with the "no matching overload" type error the skill promises.

The six FALSE claims, and what measured them

落点beforeafter
L175 "No date arithmetic""CEL has no numeric arithmetic on dates, so these fault and the field silently nulls (the build now rejects them).""a date mixed with a number faults and the field silently nulls (the build rejects it); end - start does not fault, it yields a duration stored as {}."
L419 CEL surfaces`defaultValue` (M9.9b)`defaultValue` (envelope only; bare string = literal)
L428 CEL surfaces`Dataset.records[*]``Seed.records[*]`
L441 cron surfaces`connector.schedule`, `etl.schedule`, `sync.schedule` — pipelines`connector.schedule` — scheduled connector sync
L475 template surfaces`system/notification` — email subject + body, SMS message, push body + message (5 fields)row replaced by `system/email-template`subject, bodyHtml, bodyText (plain strings, the email pipeline renders the holes)
L477 template surfaces`integration/connector/github` — titleTemplate, bodyTemplate (PR + release)row deleted
L155 stdlib pointer"Source: node_modules/@objectstack/formula/src/stdlib.ts" / "…/src/validate.ts""ships dist only — there is no src/ to read in an installed app. Its exported CEL_STDLIB_FUNCTIONS is the canonical list, pinned by two tests"

1. end - start does not fault. The sentence grouped three shapes under "these fault". Measured side by side (build verdict via validateExpression, runtime via celEngine.evaluate):

end - start (both dates) | build=ACCEPT | runtime=OK value={} (object)
start + 30 (literal int) | build=REJECT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + int
start + record.n (num fld) | build=ACCEPT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + dyn<double>
today() + 30 | build=REJECT | runtime=FAULT no such overload: google.protobuf.Timestamp + int

timestamp - timestamp is a valid CEL overload yielding a google.protobuf.Duration; coerce() (cel-engine.ts:1161) hands any non-Date object back key-by-key, so the field receives {}. int() and string() both refuse a Duration, so the value is unusable rather than absent — which is why the "do NOT write it" advice stands while the stated reason did not. The implementation agrees the boundary is date against a number: validate.ts:116 reads "Dates — ARITHMETIC against a number always nulls".

2. Field.defaultValue is not a string | Expression slot. The table's own heading says "All of these spec fields accept string | Expression". Executed against FieldSchema from @objectstack/spec/data:

REJECT | defaultValue: bare string "today()" | Field "d" (date): the default "today()" cannot satisfy this
field's own stored value contract … The engine stores a
literal default VERBATIM
ACCEPT | defaultValue: cel envelope | {"dialect":"cel","source":"today()"}
ACCEPT | visibleWhen: bare string (control) | {"dialect":"cel","source":"record.x == 'a'"}

The control row is the point: a real CEL slot auto-wraps, defaultValue does not. field.zod.ts:894 states the three legal shapes — envelope, runtime TOKEN, or LITERAL. On a text field the bad spelling is silent (it stores the characters today()), which is exactly the AI-authoring trap the sweep exists to close.

3. Dataset.records[*] is the wrong subject.DatasetSchema (ui/dataset.zod.ts:248) is the analytics semantic layer — dimensions / measures — and has no records key at all. The seed-records surface is SeedSchema.records (data/seed.zod.ts:96, z.array(z.record(z.string(), z.unknown()))), registered as the seed metadata kind. The rest of the skill already says "Seed" — the Boundaries table and pattern §4 both do — so the row was the outlier.

4. Two of three named cron surfaces are retired. Measured by enumerating every CronExpressionInputSchema use in the spec: six sites, none of them an etl or a sync schema. retry-policy.zod.ts:86 records that "#6414 retired the whole L2 ETL layer", and connector.zod.ts:21 records that automation/sync.zod.ts was folded into the connector in #4738. connector.schedule — the surviving one — is described in-schema as "Cron expression for scheduled sync", so the row now names it that way.

5 and 6. Two phantom template surfaces.titleTemplate / bodyTemplate return zero hits across packages/**/*.ts — the github-connector row names fields that do not exist anywhere in the repo. The system/notification row names five fields whose schemas were removed: notification.zod.ts:40 records that EmailTemplateSchema, SMSTemplateSchema, PushNotificationSchema and InAppNotificationSchema "were removed from this module too (ADR-0049 enforce-or-remove, v17 window)", left "reachable from no parent schema" by #4610. The same note records that push / in-app have no delivery implementation at all (#3197) — so that half of the row advertised a channel nothing delivers. The replacement row names what does carry holes today: EmailTemplateDefinitionSchema (system/email-template.zod.ts), whose subject / bodyHtml / bodyText are plain strings the email pipeline renders — deliberately spelled "plain strings", because a tmpl envelope would fail their z.string().

7. The stdlib pointer names paths that never ship.packages/formula/package.json declares files: ["dist", "README.md", "CHANGELOG.md"], and npm pack --dry-run --json reports 0src/ entries in the tarball. Both node_modules/@objectstack/formula/src/... paths are unreachable in an installed app. (@objectstack/specdoes publish src/**/*.zod.ts, so the two spec pointers in "See also" and references/_index.md are correct and were left alone.) The replacement also states the sync guarantee precisely: two tests, not one — cel-engine.test.ts:500 asserts every catalog entry resolves at runtime, and skill-catalog-sync.test.ts asserts this table documents every entry.

Written to the token ratchet, not around it

This file sits at zero headroom, so every correction is paid for inside the same file. No ceiling was raised — that is a maintainer's call, not a dev's.

readingbeforeafter
SKILL.md lines (whole file)554551
objectstack-formula/** package lines (all .md)579576
SKILL.md tokens (ceiling 6002)60026002
ratcheted bundle, tokens186903186903
published bundle total, tokens186903186903

The ratchet's own verdict line at head cb9a3cd44:

✓ check-skills-token-ratchet: skills/objectstack-formula/SKILL.md is 6002 tokens (ceiling 6002; headroom 0).
✓ check-skills-token-ratchet: 38 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.

Two nuances were not stuffed into ratcheted text and are recorded for a content/docs/** follow-up instead: (a) the build rejects date-plus-number only when the numeric operand is statically typed — a number-typed field stays dyn and slips through to a runtime fault (measured above, row 3); (b) the null-guard publish gate covers three surfaces (object validation rules, lifecycle hook condition, field requiredWhenvalidate-expressions.ts:820), while the skill names two.

Gates — derived, not recalled, run locally at head cb9a3cd44

Family derived from the real diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack. 13 families; 12 green, 1 NOT MEASURED:

EXIT=0 :: node scripts/check-ci-filter-parity.mjs
EXIT=0 :: node scripts/check-cross-package-test-inputs.mjs
EXIT=0 :: node scripts/check-shard-attestation.mjs
EXIT=0 :: node scripts/check-skills-token-ratchet.mjs
EXIT=3 :: node scripts/check-test-completeness.mjs ← PREREQUISITE NOT MET
EXIT=0 :: pnpm --filter @objectstack/lint run check:doc-formula-expressions
EXIT=0 :: pnpm check:agent-test-spelling
EXIT=0 :: pnpm check:cross-package-test-inputs
EXIT=0 :: pnpm check:doc-authoring
EXIT=0 :: pnpm check:pm-governed-merges
EXIT=0 :: pnpm check:role-word
EXIT=0 :: pnpm check:skill-compatibility
EXIT=0 :: pnpm check:skill-frame-sync

check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET by its own text (it grades a saved turbo run test log and none was named): recorded as NOT MEASURED, not as a red.

check:doc-formula-expressions needed @objectstack/formula and @objectstack/lint built first — its first run was a prerequisite miss, not a finding. Built, re-run, and it quotes itself:

✓ check:doc-formula-expressions self-test: 58 cases passed
✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1451 TS blocks judged clean by @objectstack/formula.
✓ check:doc-formula-expressions (spec TSDoc, #6763): 9 @example(s) judged clean across 1092 packages/spec/src files
✓ check:doc-formula-expressions (field-level `*When`, #11407): 14 predicate(s) judged clean; 6 skipped as undeterminable.

Plus two runs beyond the derived family, because they read this very corpus:

✅ 260 prose examples type-check across 3 surface(s) (pnpm --filter @objectstack/spec check:skill-examples)
Test Files 25 passed (25) · Tests 647 passed (647) (@objectstack/formula, including skill-catalog-sync)

The 647-test run is at parity with the pre-edit baseline (same 25/647), and it contains skill-catalog-sync.test.ts, the drift-guard that pins this table against CEL_STDLIB_FUNCTIONS — so the stdlib edits did not break the catalog contract.

check-nul-bytes clean: OK (scanned 7551 text file(s) … no raw ASCII control bytes).

ESLint was not run repo-wide, and the narrowing is measured, not assumed. Three readings: (1) the population read from eslint.config.mjs itself is **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} plus .ts/.tsx/.mts/.cts blocks — no config block names .md; (2) --format json over the diff's only file returns 1 result with 0 messages and no block applied; (3) type-aware linting is not enabled anywhere — projectService / parserOptions.project occur 0 times in the config, which the file's own line 328 states — so this diff cannot move any verdict on an untouched file. Zero of the changed files are in that gate's population in either direction.

No changeset

This PR releases nothing from any package — a pure skills/** documentation correction, the repo's live convention for which is no changeset. skip-changeset is applied.

Calibration numbers (this flight's purpose)

Full figures are in the report on #13660. Headline: 197 behavioral claims inventoried across 13 classes (of 579 lines, ~118 are non-behavioral — front-matter, positioning, the Boundaries routing table, Open questions, See also — and are out of the inventory by the anchor's rule). 6 FALSE (3.0%), 26 NOT MEASURABLE, 165 VERIFIED.

The density is not uniform, and this is the finding that should size flights ②–⑫: 4 of the 6 falsehoods are in the surfaces/mapping tables — 4/38 = 10.5% there against 2/159 = 1.3% everywhere else, an ~8× concentration. That matches PR #13577's pattern exactly (a mapping column asserting engine behavior), and it is the highest-yield place to point the remaining flights.

What is not here, on purpose

  • No implementation was touched. Verification found no implementation defect worth filing: every disagreement was the document being wrong about settled code, not code contradicting itself.
  • The references/_index.md file is generator-owned (pnpm --filter @objectstack/spec gen:skill-refs) and correct as measured — its @objectstack/spec pointers resolve, because that package does publish src/**/*.zod.ts. Not touched.
  • The 26 NOT MEASURABLE claims are concentrated in §5's previous-binding table and the bulk-write dispatch semantics, whose implementation lives in objectql/runtime hook dispatch rather than @objectstack/formula, plus the determinism contract (needs two full objectstack build runs). They are recorded on the card, not silently skipped.
  • The skill's "always single quotes" style rule is prescriptive, not descriptive — double-quoted CEL literals do evaluate (measured). Left alone: it is a house style, and changing it is not a factual correction.

Generated by Claude Code


Generated by Claude Code

…aim verified against the implementation
Six measured falsehoods corrected, token-neutral under the skills ratchet
(6002/6002, no ceiling raise). Verified against `@objectstack/formula`,
`@objectstack/lint` and `@objectstack/spec` by reading plus executed probes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de
@os-zhuang
os-zhuang marked this pull request as ready for review August 31, 2026 07:15
@os-zhuang
os-zhuang enabled auto-merge August 31, 2026 07:15
@os-zhuang
os-zhuang added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit a7b7a57Aug 31, 2026
34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-13660-formula-skill-sweep branch August 31, 2026 07:34
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills-sweep ①: objectstack-formula (579 lines) — behavioral-claim verification against @objectstack/formula, calibration flight

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation by claude[bot] · Pull Request #13674 · objectstack-ai/objectstack · GitHub
Skip to content

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation - #13674

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep
Aug 31, 2026
Merged

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation#13674
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13660

Program anchor: #13658 — flight ① of the published-skills factual sweep, the calibration flight. That anchor stays open; this PR carries only its member ①, and the roster and re-sizing live on it.

Session, for durable attribution: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de

⛔ Governed surface — human merge, deliberately parked

skills/** is the published, customer-facing surface. This PR is a draft on purpose: auto-merge is not armed, it is not marked ready, no review is requested, and the dispatching seat will not arm it. It waits for a maintainer to merge by hand. A draft awaiting the maintainer's own merge is the correct terminal state here, not a stuck PR.

Method — PR #13577 is the spec

Every behavioral claim in skills/objectstack-formula/** (2 files, 579 lines) was inventoried FIRST, then verified in inventory order against the implementation@objectstack/formula, @objectstack/lint, @objectstack/spec — never against another document. Behavior-bearing claims were verified by reading plus an executed probe; probes were written in the worktree, run, and deleted (they are evidence, not commits — none earned a place as a real pin, and the corpus already has its drift-guard in packages/formula/src/skill-catalog-sync.test.ts).

Non-vacuity control — a claim proven true by execution. SKILL.md's "Null + string throws" section asserts a verbatim runtime message. Probed against celEngine.evaluate:

NULL+STRING :: runtime :: no such overload: dyn<null> + string

The skill's own sentence names that same overload string verbatim, byte for byte, as what null + 'foo' throws — VERIFIED against the line quoted above. Same run also pinned, by execution: floor(-1.2) == -2 and ceil(-1.2) == -1; addMonths(date('2026-01-31'), 1) clamping to Feb 28; today() and daysFromNow(45) landing on UTC midnight; all 8 advertised cel-js built-ins resolving; all three Salesforce-flavor sources failing CEL compile; unknown functions (PRIOR(), ISBLANK(), isBlnk()) failing with the "no matching overload" type error the skill promises.

The six FALSE claims, and what measured them

落点beforeafter
L175 "No date arithmetic""CEL has no numeric arithmetic on dates, so these fault and the field silently nulls (the build now rejects them).""a date mixed with a number faults and the field silently nulls (the build rejects it); end - start does not fault, it yields a duration stored as {}."
L419 CEL surfaces`defaultValue` (M9.9b)`defaultValue` (envelope only; bare string = literal)
L428 CEL surfaces`Dataset.records[*]``Seed.records[*]`
L441 cron surfaces`connector.schedule`, `etl.schedule`, `sync.schedule` — pipelines`connector.schedule` — scheduled connector sync
L475 template surfaces`system/notification` — email subject + body, SMS message, push body + message (5 fields)row replaced by `system/email-template`subject, bodyHtml, bodyText (plain strings, the email pipeline renders the holes)
L477 template surfaces`integration/connector/github` — titleTemplate, bodyTemplate (PR + release)row deleted
L155 stdlib pointer"Source: node_modules/@objectstack/formula/src/stdlib.ts" / "…/src/validate.ts""ships dist only — there is no src/ to read in an installed app. Its exported CEL_STDLIB_FUNCTIONS is the canonical list, pinned by two tests"

1. end - start does not fault. The sentence grouped three shapes under "these fault". Measured side by side (build verdict via validateExpression, runtime via celEngine.evaluate):

end - start (both dates) | build=ACCEPT | runtime=OK value={} (object)
start + 30 (literal int) | build=REJECT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + int
start + record.n (num fld) | build=ACCEPT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + dyn<double>
today() + 30 | build=REJECT | runtime=FAULT no such overload: google.protobuf.Timestamp + int

timestamp - timestamp is a valid CEL overload yielding a google.protobuf.Duration; coerce() (cel-engine.ts:1161) hands any non-Date object back key-by-key, so the field receives {}. int() and string() both refuse a Duration, so the value is unusable rather than absent — which is why the "do NOT write it" advice stands while the stated reason did not. The implementation agrees the boundary is date against a number: validate.ts:116 reads "Dates — ARITHMETIC against a number always nulls".

2. Field.defaultValue is not a string | Expression slot. The table's own heading says "All of these spec fields accept string | Expression". Executed against FieldSchema from @objectstack/spec/data:

REJECT | defaultValue: bare string "today()" | Field "d" (date): the default "today()" cannot satisfy this
field's own stored value contract … The engine stores a
literal default VERBATIM
ACCEPT | defaultValue: cel envelope | {"dialect":"cel","source":"today()"}
ACCEPT | visibleWhen: bare string (control) | {"dialect":"cel","source":"record.x == 'a'"}

The control row is the point: a real CEL slot auto-wraps, defaultValue does not. field.zod.ts:894 states the three legal shapes — envelope, runtime TOKEN, or LITERAL. On a text field the bad spelling is silent (it stores the characters today()), which is exactly the AI-authoring trap the sweep exists to close.

3. Dataset.records[*] is the wrong subject.DatasetSchema (ui/dataset.zod.ts:248) is the analytics semantic layer — dimensions / measures — and has no records key at all. The seed-records surface is SeedSchema.records (data/seed.zod.ts:96, z.array(z.record(z.string(), z.unknown()))), registered as the seed metadata kind. The rest of the skill already says "Seed" — the Boundaries table and pattern §4 both do — so the row was the outlier.

4. Two of three named cron surfaces are retired. Measured by enumerating every CronExpressionInputSchema use in the spec: six sites, none of them an etl or a sync schema. retry-policy.zod.ts:86 records that "#6414 retired the whole L2 ETL layer", and connector.zod.ts:21 records that automation/sync.zod.ts was folded into the connector in #4738. connector.schedule — the surviving one — is described in-schema as "Cron expression for scheduled sync", so the row now names it that way.

5 and 6. Two phantom template surfaces.titleTemplate / bodyTemplate return zero hits across packages/**/*.ts — the github-connector row names fields that do not exist anywhere in the repo. The system/notification row names five fields whose schemas were removed: notification.zod.ts:40 records that EmailTemplateSchema, SMSTemplateSchema, PushNotificationSchema and InAppNotificationSchema "were removed from this module too (ADR-0049 enforce-or-remove, v17 window)", left "reachable from no parent schema" by #4610. The same note records that push / in-app have no delivery implementation at all (#3197) — so that half of the row advertised a channel nothing delivers. The replacement row names what does carry holes today: EmailTemplateDefinitionSchema (system/email-template.zod.ts), whose subject / bodyHtml / bodyText are plain strings the email pipeline renders — deliberately spelled "plain strings", because a tmpl envelope would fail their z.string().

7. The stdlib pointer names paths that never ship.packages/formula/package.json declares files: ["dist", "README.md", "CHANGELOG.md"], and npm pack --dry-run --json reports 0src/ entries in the tarball. Both node_modules/@objectstack/formula/src/... paths are unreachable in an installed app. (@objectstack/specdoes publish src/**/*.zod.ts, so the two spec pointers in "See also" and references/_index.md are correct and were left alone.) The replacement also states the sync guarantee precisely: two tests, not one — cel-engine.test.ts:500 asserts every catalog entry resolves at runtime, and skill-catalog-sync.test.ts asserts this table documents every entry.

Written to the token ratchet, not around it

This file sits at zero headroom, so every correction is paid for inside the same file. No ceiling was raised — that is a maintainer's call, not a dev's.

readingbeforeafter
SKILL.md lines (whole file)554551
objectstack-formula/** package lines (all .md)579576
SKILL.md tokens (ceiling 6002)60026002
ratcheted bundle, tokens186903186903
published bundle total, tokens186903186903

The ratchet's own verdict line at head cb9a3cd44:

✓ check-skills-token-ratchet: skills/objectstack-formula/SKILL.md is 6002 tokens (ceiling 6002; headroom 0).
✓ check-skills-token-ratchet: 38 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.

Two nuances were not stuffed into ratcheted text and are recorded for a content/docs/** follow-up instead: (a) the build rejects date-plus-number only when the numeric operand is statically typed — a number-typed field stays dyn and slips through to a runtime fault (measured above, row 3); (b) the null-guard publish gate covers three surfaces (object validation rules, lifecycle hook condition, field requiredWhenvalidate-expressions.ts:820), while the skill names two.

Gates — derived, not recalled, run locally at head cb9a3cd44

Family derived from the real diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack. 13 families; 12 green, 1 NOT MEASURED:

EXIT=0 :: node scripts/check-ci-filter-parity.mjs
EXIT=0 :: node scripts/check-cross-package-test-inputs.mjs
EXIT=0 :: node scripts/check-shard-attestation.mjs
EXIT=0 :: node scripts/check-skills-token-ratchet.mjs
EXIT=3 :: node scripts/check-test-completeness.mjs ← PREREQUISITE NOT MET
EXIT=0 :: pnpm --filter @objectstack/lint run check:doc-formula-expressions
EXIT=0 :: pnpm check:agent-test-spelling
EXIT=0 :: pnpm check:cross-package-test-inputs
EXIT=0 :: pnpm check:doc-authoring
EXIT=0 :: pnpm check:pm-governed-merges
EXIT=0 :: pnpm check:role-word
EXIT=0 :: pnpm check:skill-compatibility
EXIT=0 :: pnpm check:skill-frame-sync

check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET by its own text (it grades a saved turbo run test log and none was named): recorded as NOT MEASURED, not as a red.

check:doc-formula-expressions needed @objectstack/formula and @objectstack/lint built first — its first run was a prerequisite miss, not a finding. Built, re-run, and it quotes itself:

✓ check:doc-formula-expressions self-test: 58 cases passed
✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1451 TS blocks judged clean by @objectstack/formula.
✓ check:doc-formula-expressions (spec TSDoc, #6763): 9 @example(s) judged clean across 1092 packages/spec/src files
✓ check:doc-formula-expressions (field-level `*When`, #11407): 14 predicate(s) judged clean; 6 skipped as undeterminable.

Plus two runs beyond the derived family, because they read this very corpus:

✅ 260 prose examples type-check across 3 surface(s) (pnpm --filter @objectstack/spec check:skill-examples)
Test Files 25 passed (25) · Tests 647 passed (647) (@objectstack/formula, including skill-catalog-sync)

The 647-test run is at parity with the pre-edit baseline (same 25/647), and it contains skill-catalog-sync.test.ts, the drift-guard that pins this table against CEL_STDLIB_FUNCTIONS — so the stdlib edits did not break the catalog contract.

check-nul-bytes clean: OK (scanned 7551 text file(s) … no raw ASCII control bytes).

ESLint was not run repo-wide, and the narrowing is measured, not assumed. Three readings: (1) the population read from eslint.config.mjs itself is **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} plus .ts/.tsx/.mts/.cts blocks — no config block names .md; (2) --format json over the diff's only file returns 1 result with 0 messages and no block applied; (3) type-aware linting is not enabled anywhere — projectService / parserOptions.project occur 0 times in the config, which the file's own line 328 states — so this diff cannot move any verdict on an untouched file. Zero of the changed files are in that gate's population in either direction.

No changeset

This PR releases nothing from any package — a pure skills/** documentation correction, the repo's live convention for which is no changeset. skip-changeset is applied.

Calibration numbers (this flight's purpose)

Full figures are in the report on #13660. Headline: 197 behavioral claims inventoried across 13 classes (of 579 lines, ~118 are non-behavioral — front-matter, positioning, the Boundaries routing table, Open questions, See also — and are out of the inventory by the anchor's rule). 6 FALSE (3.0%), 26 NOT MEASURABLE, 165 VERIFIED.

The density is not uniform, and this is the finding that should size flights ②–⑫: 4 of the 6 falsehoods are in the surfaces/mapping tables — 4/38 = 10.5% there against 2/159 = 1.3% everywhere else, an ~8× concentration. That matches PR #13577's pattern exactly (a mapping column asserting engine behavior), and it is the highest-yield place to point the remaining flights.

What is not here, on purpose

  • No implementation was touched. Verification found no implementation defect worth filing: every disagreement was the document being wrong about settled code, not code contradicting itself.
  • The references/_index.md file is generator-owned (pnpm --filter @objectstack/spec gen:skill-refs) and correct as measured — its @objectstack/spec pointers resolve, because that package does publish src/**/*.zod.ts. Not touched.
  • The 26 NOT MEASURABLE claims are concentrated in §5's previous-binding table and the bulk-write dispatch semantics, whose implementation lives in objectql/runtime hook dispatch rather than @objectstack/formula, plus the determinism contract (needs two full objectstack build runs). They are recorded on the card, not silently skipped.
  • The skill's "always single quotes" style rule is prescriptive, not descriptive — double-quoted CEL literals do evaluate (measured). Left alone: it is a house style, and changing it is not a factual correction.

Generated by Claude Code


Generated by Claude Code

…aim verified against the implementation
Six measured falsehoods corrected, token-neutral under the skills ratchet
(6002/6002, no ceiling raise). Verified against `@objectstack/formula`,
`@objectstack/lint` and `@objectstack/spec` by reading plus executed probes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de
@os-zhuang
os-zhuang marked this pull request as ready for review August 31, 2026 07:15
@os-zhuang
os-zhuang enabled auto-merge August 31, 2026 07:15
@os-zhuang
os-zhuang added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit a7b7a57Aug 31, 2026
34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-13660-formula-skill-sweep branch August 31, 2026 07:34
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills-sweep ①: objectstack-formula (579 lines) — behavioral-claim verification against @objectstack/formula, calibration flight

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation by claude[bot] · Pull Request #13674 · objectstack-ai/objectstack · GitHub
Skip to content

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation - #13674

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep
Aug 31, 2026
Merged

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation#13674
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13660

Program anchor: #13658 — flight ① of the published-skills factual sweep, the calibration flight. That anchor stays open; this PR carries only its member ①, and the roster and re-sizing live on it.

Session, for durable attribution: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de

⛔ Governed surface — human merge, deliberately parked

skills/** is the published, customer-facing surface. This PR is a draft on purpose: auto-merge is not armed, it is not marked ready, no review is requested, and the dispatching seat will not arm it. It waits for a maintainer to merge by hand. A draft awaiting the maintainer's own merge is the correct terminal state here, not a stuck PR.

Method — PR #13577 is the spec

Every behavioral claim in skills/objectstack-formula/** (2 files, 579 lines) was inventoried FIRST, then verified in inventory order against the implementation@objectstack/formula, @objectstack/lint, @objectstack/spec — never against another document. Behavior-bearing claims were verified by reading plus an executed probe; probes were written in the worktree, run, and deleted (they are evidence, not commits — none earned a place as a real pin, and the corpus already has its drift-guard in packages/formula/src/skill-catalog-sync.test.ts).

Non-vacuity control — a claim proven true by execution. SKILL.md's "Null + string throws" section asserts a verbatim runtime message. Probed against celEngine.evaluate:

NULL+STRING :: runtime :: no such overload: dyn<null> + string

The skill's own sentence names that same overload string verbatim, byte for byte, as what null + 'foo' throws — VERIFIED against the line quoted above. Same run also pinned, by execution: floor(-1.2) == -2 and ceil(-1.2) == -1; addMonths(date('2026-01-31'), 1) clamping to Feb 28; today() and daysFromNow(45) landing on UTC midnight; all 8 advertised cel-js built-ins resolving; all three Salesforce-flavor sources failing CEL compile; unknown functions (PRIOR(), ISBLANK(), isBlnk()) failing with the "no matching overload" type error the skill promises.

The six FALSE claims, and what measured them

落点beforeafter
L175 "No date arithmetic""CEL has no numeric arithmetic on dates, so these fault and the field silently nulls (the build now rejects them).""a date mixed with a number faults and the field silently nulls (the build rejects it); end - start does not fault, it yields a duration stored as {}."
L419 CEL surfaces`defaultValue` (M9.9b)`defaultValue` (envelope only; bare string = literal)
L428 CEL surfaces`Dataset.records[*]``Seed.records[*]`
L441 cron surfaces`connector.schedule`, `etl.schedule`, `sync.schedule` — pipelines`connector.schedule` — scheduled connector sync
L475 template surfaces`system/notification` — email subject + body, SMS message, push body + message (5 fields)row replaced by `system/email-template`subject, bodyHtml, bodyText (plain strings, the email pipeline renders the holes)
L477 template surfaces`integration/connector/github` — titleTemplate, bodyTemplate (PR + release)row deleted
L155 stdlib pointer"Source: node_modules/@objectstack/formula/src/stdlib.ts" / "…/src/validate.ts""ships dist only — there is no src/ to read in an installed app. Its exported CEL_STDLIB_FUNCTIONS is the canonical list, pinned by two tests"

1. end - start does not fault. The sentence grouped three shapes under "these fault". Measured side by side (build verdict via validateExpression, runtime via celEngine.evaluate):

end - start (both dates) | build=ACCEPT | runtime=OK value={} (object)
start + 30 (literal int) | build=REJECT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + int
start + record.n (num fld) | build=ACCEPT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + dyn<double>
today() + 30 | build=REJECT | runtime=FAULT no such overload: google.protobuf.Timestamp + int

timestamp - timestamp is a valid CEL overload yielding a google.protobuf.Duration; coerce() (cel-engine.ts:1161) hands any non-Date object back key-by-key, so the field receives {}. int() and string() both refuse a Duration, so the value is unusable rather than absent — which is why the "do NOT write it" advice stands while the stated reason did not. The implementation agrees the boundary is date against a number: validate.ts:116 reads "Dates — ARITHMETIC against a number always nulls".

2. Field.defaultValue is not a string | Expression slot. The table's own heading says "All of these spec fields accept string | Expression". Executed against FieldSchema from @objectstack/spec/data:

REJECT | defaultValue: bare string "today()" | Field "d" (date): the default "today()" cannot satisfy this
field's own stored value contract … The engine stores a
literal default VERBATIM
ACCEPT | defaultValue: cel envelope | {"dialect":"cel","source":"today()"}
ACCEPT | visibleWhen: bare string (control) | {"dialect":"cel","source":"record.x == 'a'"}

The control row is the point: a real CEL slot auto-wraps, defaultValue does not. field.zod.ts:894 states the three legal shapes — envelope, runtime TOKEN, or LITERAL. On a text field the bad spelling is silent (it stores the characters today()), which is exactly the AI-authoring trap the sweep exists to close.

3. Dataset.records[*] is the wrong subject.DatasetSchema (ui/dataset.zod.ts:248) is the analytics semantic layer — dimensions / measures — and has no records key at all. The seed-records surface is SeedSchema.records (data/seed.zod.ts:96, z.array(z.record(z.string(), z.unknown()))), registered as the seed metadata kind. The rest of the skill already says "Seed" — the Boundaries table and pattern §4 both do — so the row was the outlier.

4. Two of three named cron surfaces are retired. Measured by enumerating every CronExpressionInputSchema use in the spec: six sites, none of them an etl or a sync schema. retry-policy.zod.ts:86 records that "#6414 retired the whole L2 ETL layer", and connector.zod.ts:21 records that automation/sync.zod.ts was folded into the connector in #4738. connector.schedule — the surviving one — is described in-schema as "Cron expression for scheduled sync", so the row now names it that way.

5 and 6. Two phantom template surfaces.titleTemplate / bodyTemplate return zero hits across packages/**/*.ts — the github-connector row names fields that do not exist anywhere in the repo. The system/notification row names five fields whose schemas were removed: notification.zod.ts:40 records that EmailTemplateSchema, SMSTemplateSchema, PushNotificationSchema and InAppNotificationSchema "were removed from this module too (ADR-0049 enforce-or-remove, v17 window)", left "reachable from no parent schema" by #4610. The same note records that push / in-app have no delivery implementation at all (#3197) — so that half of the row advertised a channel nothing delivers. The replacement row names what does carry holes today: EmailTemplateDefinitionSchema (system/email-template.zod.ts), whose subject / bodyHtml / bodyText are plain strings the email pipeline renders — deliberately spelled "plain strings", because a tmpl envelope would fail their z.string().

7. The stdlib pointer names paths that never ship.packages/formula/package.json declares files: ["dist", "README.md", "CHANGELOG.md"], and npm pack --dry-run --json reports 0src/ entries in the tarball. Both node_modules/@objectstack/formula/src/... paths are unreachable in an installed app. (@objectstack/specdoes publish src/**/*.zod.ts, so the two spec pointers in "See also" and references/_index.md are correct and were left alone.) The replacement also states the sync guarantee precisely: two tests, not one — cel-engine.test.ts:500 asserts every catalog entry resolves at runtime, and skill-catalog-sync.test.ts asserts this table documents every entry.

Written to the token ratchet, not around it

This file sits at zero headroom, so every correction is paid for inside the same file. No ceiling was raised — that is a maintainer's call, not a dev's.

readingbeforeafter
SKILL.md lines (whole file)554551
objectstack-formula/** package lines (all .md)579576
SKILL.md tokens (ceiling 6002)60026002
ratcheted bundle, tokens186903186903
published bundle total, tokens186903186903

The ratchet's own verdict line at head cb9a3cd44:

✓ check-skills-token-ratchet: skills/objectstack-formula/SKILL.md is 6002 tokens (ceiling 6002; headroom 0).
✓ check-skills-token-ratchet: 38 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.

Two nuances were not stuffed into ratcheted text and are recorded for a content/docs/** follow-up instead: (a) the build rejects date-plus-number only when the numeric operand is statically typed — a number-typed field stays dyn and slips through to a runtime fault (measured above, row 3); (b) the null-guard publish gate covers three surfaces (object validation rules, lifecycle hook condition, field requiredWhenvalidate-expressions.ts:820), while the skill names two.

Gates — derived, not recalled, run locally at head cb9a3cd44

Family derived from the real diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack. 13 families; 12 green, 1 NOT MEASURED:

EXIT=0 :: node scripts/check-ci-filter-parity.mjs
EXIT=0 :: node scripts/check-cross-package-test-inputs.mjs
EXIT=0 :: node scripts/check-shard-attestation.mjs
EXIT=0 :: node scripts/check-skills-token-ratchet.mjs
EXIT=3 :: node scripts/check-test-completeness.mjs ← PREREQUISITE NOT MET
EXIT=0 :: pnpm --filter @objectstack/lint run check:doc-formula-expressions
EXIT=0 :: pnpm check:agent-test-spelling
EXIT=0 :: pnpm check:cross-package-test-inputs
EXIT=0 :: pnpm check:doc-authoring
EXIT=0 :: pnpm check:pm-governed-merges
EXIT=0 :: pnpm check:role-word
EXIT=0 :: pnpm check:skill-compatibility
EXIT=0 :: pnpm check:skill-frame-sync

check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET by its own text (it grades a saved turbo run test log and none was named): recorded as NOT MEASURED, not as a red.

check:doc-formula-expressions needed @objectstack/formula and @objectstack/lint built first — its first run was a prerequisite miss, not a finding. Built, re-run, and it quotes itself:

✓ check:doc-formula-expressions self-test: 58 cases passed
✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1451 TS blocks judged clean by @objectstack/formula.
✓ check:doc-formula-expressions (spec TSDoc, #6763): 9 @example(s) judged clean across 1092 packages/spec/src files
✓ check:doc-formula-expressions (field-level `*When`, #11407): 14 predicate(s) judged clean; 6 skipped as undeterminable.

Plus two runs beyond the derived family, because they read this very corpus:

✅ 260 prose examples type-check across 3 surface(s) (pnpm --filter @objectstack/spec check:skill-examples)
Test Files 25 passed (25) · Tests 647 passed (647) (@objectstack/formula, including skill-catalog-sync)

The 647-test run is at parity with the pre-edit baseline (same 25/647), and it contains skill-catalog-sync.test.ts, the drift-guard that pins this table against CEL_STDLIB_FUNCTIONS — so the stdlib edits did not break the catalog contract.

check-nul-bytes clean: OK (scanned 7551 text file(s) … no raw ASCII control bytes).

ESLint was not run repo-wide, and the narrowing is measured, not assumed. Three readings: (1) the population read from eslint.config.mjs itself is **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} plus .ts/.tsx/.mts/.cts blocks — no config block names .md; (2) --format json over the diff's only file returns 1 result with 0 messages and no block applied; (3) type-aware linting is not enabled anywhere — projectService / parserOptions.project occur 0 times in the config, which the file's own line 328 states — so this diff cannot move any verdict on an untouched file. Zero of the changed files are in that gate's population in either direction.

No changeset

This PR releases nothing from any package — a pure skills/** documentation correction, the repo's live convention for which is no changeset. skip-changeset is applied.

Calibration numbers (this flight's purpose)

Full figures are in the report on #13660. Headline: 197 behavioral claims inventoried across 13 classes (of 579 lines, ~118 are non-behavioral — front-matter, positioning, the Boundaries routing table, Open questions, See also — and are out of the inventory by the anchor's rule). 6 FALSE (3.0%), 26 NOT MEASURABLE, 165 VERIFIED.

The density is not uniform, and this is the finding that should size flights ②–⑫: 4 of the 6 falsehoods are in the surfaces/mapping tables — 4/38 = 10.5% there against 2/159 = 1.3% everywhere else, an ~8× concentration. That matches PR #13577's pattern exactly (a mapping column asserting engine behavior), and it is the highest-yield place to point the remaining flights.

What is not here, on purpose

  • No implementation was touched. Verification found no implementation defect worth filing: every disagreement was the document being wrong about settled code, not code contradicting itself.
  • The references/_index.md file is generator-owned (pnpm --filter @objectstack/spec gen:skill-refs) and correct as measured — its @objectstack/spec pointers resolve, because that package does publish src/**/*.zod.ts. Not touched.
  • The 26 NOT MEASURABLE claims are concentrated in §5's previous-binding table and the bulk-write dispatch semantics, whose implementation lives in objectql/runtime hook dispatch rather than @objectstack/formula, plus the determinism contract (needs two full objectstack build runs). They are recorded on the card, not silently skipped.
  • The skill's "always single quotes" style rule is prescriptive, not descriptive — double-quoted CEL literals do evaluate (measured). Left alone: it is a house style, and changing it is not a factual correction.

Generated by Claude Code


Generated by Claude Code

…aim verified against the implementation
Six measured falsehoods corrected, token-neutral under the skills ratchet
(6002/6002, no ceiling raise). Verified against `@objectstack/formula`,
`@objectstack/lint` and `@objectstack/spec` by reading plus executed probes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de
@os-zhuang
os-zhuang marked this pull request as ready for review August 31, 2026 07:15
@os-zhuang
os-zhuang enabled auto-merge August 31, 2026 07:15
@os-zhuang
os-zhuang added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit a7b7a57Aug 31, 2026
34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-13660-formula-skill-sweep branch August 31, 2026 07:34
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills-sweep ①: objectstack-formula (579 lines) — behavioral-claim verification against @objectstack/formula, calibration flight

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation by claude[bot] · Pull Request #13674 · objectstack-ai/objectstack · GitHub
Skip to content

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation - #13674

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep
Aug 31, 2026
Merged

docs(skills): objectstack-formula factual sweep — every behavioral claim verified against the implementation#13674
os-zhuang merged 1 commit into
mainfrom
claude/issue-13660-formula-skill-sweep

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13660

Program anchor: #13658 — flight ① of the published-skills factual sweep, the calibration flight. That anchor stays open; this PR carries only its member ①, and the roster and re-sizing live on it.

Session, for durable attribution: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de

⛔ Governed surface — human merge, deliberately parked

skills/** is the published, customer-facing surface. This PR is a draft on purpose: auto-merge is not armed, it is not marked ready, no review is requested, and the dispatching seat will not arm it. It waits for a maintainer to merge by hand. A draft awaiting the maintainer's own merge is the correct terminal state here, not a stuck PR.

Method — PR #13577 is the spec

Every behavioral claim in skills/objectstack-formula/** (2 files, 579 lines) was inventoried FIRST, then verified in inventory order against the implementation@objectstack/formula, @objectstack/lint, @objectstack/spec — never against another document. Behavior-bearing claims were verified by reading plus an executed probe; probes were written in the worktree, run, and deleted (they are evidence, not commits — none earned a place as a real pin, and the corpus already has its drift-guard in packages/formula/src/skill-catalog-sync.test.ts).

Non-vacuity control — a claim proven true by execution. SKILL.md's "Null + string throws" section asserts a verbatim runtime message. Probed against celEngine.evaluate:

NULL+STRING :: runtime :: no such overload: dyn<null> + string

The skill's own sentence names that same overload string verbatim, byte for byte, as what null + 'foo' throws — VERIFIED against the line quoted above. Same run also pinned, by execution: floor(-1.2) == -2 and ceil(-1.2) == -1; addMonths(date('2026-01-31'), 1) clamping to Feb 28; today() and daysFromNow(45) landing on UTC midnight; all 8 advertised cel-js built-ins resolving; all three Salesforce-flavor sources failing CEL compile; unknown functions (PRIOR(), ISBLANK(), isBlnk()) failing with the "no matching overload" type error the skill promises.

The six FALSE claims, and what measured them

落点beforeafter
L175 "No date arithmetic""CEL has no numeric arithmetic on dates, so these fault and the field silently nulls (the build now rejects them).""a date mixed with a number faults and the field silently nulls (the build rejects it); end - start does not fault, it yields a duration stored as {}."
L419 CEL surfaces`defaultValue` (M9.9b)`defaultValue` (envelope only; bare string = literal)
L428 CEL surfaces`Dataset.records[*]``Seed.records[*]`
L441 cron surfaces`connector.schedule`, `etl.schedule`, `sync.schedule` — pipelines`connector.schedule` — scheduled connector sync
L475 template surfaces`system/notification` — email subject + body, SMS message, push body + message (5 fields)row replaced by `system/email-template`subject, bodyHtml, bodyText (plain strings, the email pipeline renders the holes)
L477 template surfaces`integration/connector/github` — titleTemplate, bodyTemplate (PR + release)row deleted
L155 stdlib pointer"Source: node_modules/@objectstack/formula/src/stdlib.ts" / "…/src/validate.ts""ships dist only — there is no src/ to read in an installed app. Its exported CEL_STDLIB_FUNCTIONS is the canonical list, pinned by two tests"

1. end - start does not fault. The sentence grouped three shapes under "these fault". Measured side by side (build verdict via validateExpression, runtime via celEngine.evaluate):

end - start (both dates) | build=ACCEPT | runtime=OK value={} (object)
start + 30 (literal int) | build=REJECT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + int
start + record.n (num fld) | build=ACCEPT | runtime=FAULT no such overload: dyn<google.protobuf.Timestamp> + dyn<double>
today() + 30 | build=REJECT | runtime=FAULT no such overload: google.protobuf.Timestamp + int

timestamp - timestamp is a valid CEL overload yielding a google.protobuf.Duration; coerce() (cel-engine.ts:1161) hands any non-Date object back key-by-key, so the field receives {}. int() and string() both refuse a Duration, so the value is unusable rather than absent — which is why the "do NOT write it" advice stands while the stated reason did not. The implementation agrees the boundary is date against a number: validate.ts:116 reads "Dates — ARITHMETIC against a number always nulls".

2. Field.defaultValue is not a string | Expression slot. The table's own heading says "All of these spec fields accept string | Expression". Executed against FieldSchema from @objectstack/spec/data:

REJECT | defaultValue: bare string "today()" | Field "d" (date): the default "today()" cannot satisfy this
field's own stored value contract … The engine stores a
literal default VERBATIM
ACCEPT | defaultValue: cel envelope | {"dialect":"cel","source":"today()"}
ACCEPT | visibleWhen: bare string (control) | {"dialect":"cel","source":"record.x == 'a'"}

The control row is the point: a real CEL slot auto-wraps, defaultValue does not. field.zod.ts:894 states the three legal shapes — envelope, runtime TOKEN, or LITERAL. On a text field the bad spelling is silent (it stores the characters today()), which is exactly the AI-authoring trap the sweep exists to close.

3. Dataset.records[*] is the wrong subject.DatasetSchema (ui/dataset.zod.ts:248) is the analytics semantic layer — dimensions / measures — and has no records key at all. The seed-records surface is SeedSchema.records (data/seed.zod.ts:96, z.array(z.record(z.string(), z.unknown()))), registered as the seed metadata kind. The rest of the skill already says "Seed" — the Boundaries table and pattern §4 both do — so the row was the outlier.

4. Two of three named cron surfaces are retired. Measured by enumerating every CronExpressionInputSchema use in the spec: six sites, none of them an etl or a sync schema. retry-policy.zod.ts:86 records that "#6414 retired the whole L2 ETL layer", and connector.zod.ts:21 records that automation/sync.zod.ts was folded into the connector in #4738. connector.schedule — the surviving one — is described in-schema as "Cron expression for scheduled sync", so the row now names it that way.

5 and 6. Two phantom template surfaces.titleTemplate / bodyTemplate return zero hits across packages/**/*.ts — the github-connector row names fields that do not exist anywhere in the repo. The system/notification row names five fields whose schemas were removed: notification.zod.ts:40 records that EmailTemplateSchema, SMSTemplateSchema, PushNotificationSchema and InAppNotificationSchema "were removed from this module too (ADR-0049 enforce-or-remove, v17 window)", left "reachable from no parent schema" by #4610. The same note records that push / in-app have no delivery implementation at all (#3197) — so that half of the row advertised a channel nothing delivers. The replacement row names what does carry holes today: EmailTemplateDefinitionSchema (system/email-template.zod.ts), whose subject / bodyHtml / bodyText are plain strings the email pipeline renders — deliberately spelled "plain strings", because a tmpl envelope would fail their z.string().

7. The stdlib pointer names paths that never ship.packages/formula/package.json declares files: ["dist", "README.md", "CHANGELOG.md"], and npm pack --dry-run --json reports 0src/ entries in the tarball. Both node_modules/@objectstack/formula/src/... paths are unreachable in an installed app. (@objectstack/specdoes publish src/**/*.zod.ts, so the two spec pointers in "See also" and references/_index.md are correct and were left alone.) The replacement also states the sync guarantee precisely: two tests, not one — cel-engine.test.ts:500 asserts every catalog entry resolves at runtime, and skill-catalog-sync.test.ts asserts this table documents every entry.

Written to the token ratchet, not around it

This file sits at zero headroom, so every correction is paid for inside the same file. No ceiling was raised — that is a maintainer's call, not a dev's.

readingbeforeafter
SKILL.md lines (whole file)554551
objectstack-formula/** package lines (all .md)579576
SKILL.md tokens (ceiling 6002)60026002
ratcheted bundle, tokens186903186903
published bundle total, tokens186903186903

The ratchet's own verdict line at head cb9a3cd44:

✓ check-skills-token-ratchet: skills/objectstack-formula/SKILL.md is 6002 tokens (ceiling 6002; headroom 0).
✓ check-skills-token-ratchet: 38 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.

Two nuances were not stuffed into ratcheted text and are recorded for a content/docs/** follow-up instead: (a) the build rejects date-plus-number only when the numeric operand is statically typed — a number-typed field stays dyn and slips through to a runtime fault (measured above, row 3); (b) the null-guard publish gate covers three surfaces (object validation rules, lifecycle hook condition, field requiredWhenvalidate-expressions.ts:820), while the skill names two.

Gates — derived, not recalled, run locally at head cb9a3cd44

Family derived from the real diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack. 13 families; 12 green, 1 NOT MEASURED:

EXIT=0 :: node scripts/check-ci-filter-parity.mjs
EXIT=0 :: node scripts/check-cross-package-test-inputs.mjs
EXIT=0 :: node scripts/check-shard-attestation.mjs
EXIT=0 :: node scripts/check-skills-token-ratchet.mjs
EXIT=3 :: node scripts/check-test-completeness.mjs ← PREREQUISITE NOT MET
EXIT=0 :: pnpm --filter @objectstack/lint run check:doc-formula-expressions
EXIT=0 :: pnpm check:agent-test-spelling
EXIT=0 :: pnpm check:cross-package-test-inputs
EXIT=0 :: pnpm check:doc-authoring
EXIT=0 :: pnpm check:pm-governed-merges
EXIT=0 :: pnpm check:role-word
EXIT=0 :: pnpm check:skill-compatibility
EXIT=0 :: pnpm check:skill-frame-sync

check-test-completeness.mjs exits 3 = PREREQUISITE NOT MET by its own text (it grades a saved turbo run test log and none was named): recorded as NOT MEASURED, not as a red.

check:doc-formula-expressions needed @objectstack/formula and @objectstack/lint built first — its first run was a prerequisite miss, not a finding. Built, re-run, and it quotes itself:

✓ check:doc-formula-expressions self-test: 58 cases passed
✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1451 TS blocks judged clean by @objectstack/formula.
✓ check:doc-formula-expressions (spec TSDoc, #6763): 9 @example(s) judged clean across 1092 packages/spec/src files
✓ check:doc-formula-expressions (field-level `*When`, #11407): 14 predicate(s) judged clean; 6 skipped as undeterminable.

Plus two runs beyond the derived family, because they read this very corpus:

✅ 260 prose examples type-check across 3 surface(s) (pnpm --filter @objectstack/spec check:skill-examples)
Test Files 25 passed (25) · Tests 647 passed (647) (@objectstack/formula, including skill-catalog-sync)

The 647-test run is at parity with the pre-edit baseline (same 25/647), and it contains skill-catalog-sync.test.ts, the drift-guard that pins this table against CEL_STDLIB_FUNCTIONS — so the stdlib edits did not break the catalog contract.

check-nul-bytes clean: OK (scanned 7551 text file(s) … no raw ASCII control bytes).

ESLint was not run repo-wide, and the narrowing is measured, not assumed. Three readings: (1) the population read from eslint.config.mjs itself is **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} plus .ts/.tsx/.mts/.cts blocks — no config block names .md; (2) --format json over the diff's only file returns 1 result with 0 messages and no block applied; (3) type-aware linting is not enabled anywhere — projectService / parserOptions.project occur 0 times in the config, which the file's own line 328 states — so this diff cannot move any verdict on an untouched file. Zero of the changed files are in that gate's population in either direction.

No changeset

This PR releases nothing from any package — a pure skills/** documentation correction, the repo's live convention for which is no changeset. skip-changeset is applied.

Calibration numbers (this flight's purpose)

Full figures are in the report on #13660. Headline: 197 behavioral claims inventoried across 13 classes (of 579 lines, ~118 are non-behavioral — front-matter, positioning, the Boundaries routing table, Open questions, See also — and are out of the inventory by the anchor's rule). 6 FALSE (3.0%), 26 NOT MEASURABLE, 165 VERIFIED.

The density is not uniform, and this is the finding that should size flights ②–⑫: 4 of the 6 falsehoods are in the surfaces/mapping tables — 4/38 = 10.5% there against 2/159 = 1.3% everywhere else, an ~8× concentration. That matches PR #13577's pattern exactly (a mapping column asserting engine behavior), and it is the highest-yield place to point the remaining flights.

What is not here, on purpose

  • No implementation was touched. Verification found no implementation defect worth filing: every disagreement was the document being wrong about settled code, not code contradicting itself.
  • The references/_index.md file is generator-owned (pnpm --filter @objectstack/spec gen:skill-refs) and correct as measured — its @objectstack/spec pointers resolve, because that package does publish src/**/*.zod.ts. Not touched.
  • The 26 NOT MEASURABLE claims are concentrated in §5's previous-binding table and the bulk-write dispatch semantics, whose implementation lives in objectql/runtime hook dispatch rather than @objectstack/formula, plus the determinism contract (needs two full objectstack build runs). They are recorded on the card, not silently skipped.
  • The skill's "always single quotes" style rule is prescriptive, not descriptive — double-quoted CEL literals do evaluate (measured). Left alone: it is a house style, and changing it is not a factual correction.

Generated by Claude Code


Generated by Claude Code

…aim verified against the implementation
Six measured falsehoods corrected, token-neutral under the skills ratchet
(6002/6002, no ceiling raise). Verified against `@objectstack/formula`,
`@objectstack/lint` and `@objectstack/spec` by reading plus executed probes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de
@os-zhuang
os-zhuang marked this pull request as ready for review August 31, 2026 07:15
@os-zhuang
os-zhuang enabled auto-merge August 31, 2026 07:15
@os-zhuang
os-zhuang added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit a7b7a57Aug 31, 2026
34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-13660-formula-skill-sweep branch August 31, 2026 07:34
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills-sweep ①: objectstack-formula (579 lines) — behavioral-claim verification against @objectstack/formula, calibration flight

2 participants

@os-zhuang@claude