You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(spec,formula): state the Expression contract in the present tense — drop the M9.1 / M9.2 phase language (#18557)
Part of #17849
Clause-②: no
The spec-seat half of #17323, ruled **B** by the director seat (comment
`5644710461`, decision batch #123 item 1, 2026-09-12; maintainer
verbatim 「同意」). The M9.1 / M9.2 phase language is dropped from the
citation sites in `packages/spec` and `packages/formula`, and the
contract each of them describes is restated in the present tense. ⛔ No
accept-set change: no key is added, renamed, re-typed or retired, and
every envelope that parsed before parses byte-identically.
The docs half (PR #17851, `Part of #17323`) merged on
2026-09-12T13:17:02Z, so `content/docs/data-modeling/formulas.mdx` and
the two pending changesets already carry the new wording. This PR is the
other half the ruling's item 4 assigns to this seat — `packages/formula`
included, which the domain table calls `domain:engine` and the ruling
puts here verbatim.
## What the text says now
- `source` is the canonical persisted form — it is what the engine
evaluates.
- `ast` is accepted beside it as an optional opaque structured value
(each engine validates its own shape), and it carries no promise of
becoming required.
- A slot whose value the engine RUNS requires `source`;
`EvaluatedExpressionSchema` is where that is spelled out.
| file | `M9\.[12]` hits at `79a046f` | after |
|:--|--:|--:|
| `packages/spec/src/shared/expression.zod.ts` | 7 | 0 |
| `packages/formula/src/cel-engine.ts` | 1 | 0 |
| `packages/formula/src/normalize.ts` | 1 | 0 |
Lit control for the same probe: `ExpressionSchema` in
`expression.zod.ts` reads 19, so the zero is a reading and not a broken
path.
`cel-engine.ts`'s AST-only arm also carried an `M9.7` promise ("revisit
AST-only execution in M9.7 when we cut the spec persistence over")
**inside the same sentence** as its `M9.1` hit. It goes with the
sentence rather than being left as a phase promise standing beside a
de-phased one; it is named here rather than smuggled.
## The verbatim-quote constraint, and the proof it holds
Coordination note `5645945565`: the two pending changesets quote the
exported constant `EVALUATED_EXPRESSION_SOURCE_REQUIRED` VERBATIM inside
a blockquote, and the docs half already edited them to read 「(the
canonical persisted form)」. The constant loses exactly the four words
`of phase M9.1` and nothing else, so the quote stays a quote. Proved
mechanically rather than by eye — the constant's runtime value was
reassembled from the source and compared with each changeset's
blockquote, joined:
```
.changeset/flow-edge-condition-evaluated-slot.md ==> BYTE-IDENTICAL to the constant
.changeset/blank-node-condition-refused-at-registration.md ==> BYTE-IDENTICAL to the constant
```
No consumer parses that sentence: every call site imports the constant
by name, and the message assertions in `packages/lint`,
`packages/services/service-automation` and `packages/spec` read
substrings that never contained the phase id — the non-blank-source
clause and the cannot-evaluate-ast clause.
## ⚠️ Declared deviation — the `packages/spec/CHANGELOG.md` erratum is
NOT in this PR
The card's work items and the ruling's item 2 also name an erratum line
under the already-published `## 17.4.0` entry that promised M9.2 (3 hits
at `:1096`, `:1103`, `:1119`). It is **deliberately not carried here**,
because AGENTS.md's Documentation Guardrails row for
`packages/*/CHANGELOG.md` is categorical about the packaging:
> ❌ Never edit in a code PR … Factual error in a released entry →
**amend that entry in a dedicated docs-only PR**, ⛔ never an erratum in
a later entry and never a rider on code changes
This PR is a code PR: it moves a published string and owes a `patch`
changeset. The ruling names 「the #17026 shape」 for the erratum, and that
shape — read off the card #17026 and PR #17896 that executed it — **is**
a dedicated docs-only PR with `skip-changeset`, whose executing seat
quoted this same AGENTS.md row verbatim as its authorization chain. So
the ruling and the guardrail agree; only the card's file-surface
bundling puts them in one PR.
⇒ The erratum is owed as a **separate docs-only PR**, and #17849 stays
open for it — which is why this body reads `Part of #17849` and not the
closing form the dispatch asked for. The residual after this PR,
measured on this branch:
```
$ git grep -lE 'M9\.[12]' -- packages
packages/spec/CHANGELOG.md # 3 hits — the erratum's target, owed as a docs-only PR
```
Nothing under `content/` remains; the docs half cleared it.
## Verification
Everything below is a real local run, exit codes landed to disk before
reading (⛔ never through a pipe).
| run | verdict |
|:--|:--|
| `pnpm --filter @objectstack/spec --filter @objectstack/formula build`
| `VERDICT command-exit 0` (shared verify lock) |
| `pnpm --filter @objectstack/spec check:generated` | exit 0 — ✓ all 15
generated artifacts up to date (nothing to regenerate: no `.describe()`
moved) |
| `pnpm --filter @objectstack/spec --filter @objectstack/formula
typecheck` | exit 0 |
| `pnpm --filter @objectstack/spec --filter @objectstack/formula test` |
exit 0 — spec 483 files / 13773 tests, formula 29 files / 856 tests |
| `pnpm lint` (`eslint . --no-inline-config`, whole repo) | exit 0 |
| derived gate families (`dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands`) | 78 derived · 76 run green · 2
NOT MEASURED |
`--ran` reconciliation with exit codes recorded per family: `78 derived
famil(ies) accounted for — 76 run, 2 NOT-MEASURED (2 DERIVED from a
recorded exit 3)`. The two are `check:dual-build-cjs-loads` and
`check:lean-entry-closure`, both exit **3 = PREREQUISITE NOT MET** (they
read the `dist/` of ~80 packages this tree never built) — ⛔ neither a
pass nor a failure; CI's `Build Core` is where they run.
Two families needed a prerequisite before they could answer, and both
are green once given it: `check:doc-formula-expressions` (exit 3 until
`@objectstack/lint` was built, then exit 0) and
`check-plugin-teardown-shape --self-test` (exit 1 on this shallow clone
until its pinned fixture commit was fetched, then exit 0 / 47 cases).
## Acceptance notes
⚠️ **One gate reds on this tree and it is NOT this diff** — `pnpm
check:cross-package-test-inputs` (exit 1), flagging `@objectstack/cli
descends a directory tree from packages/spec/dist/`, rooted in
`packages/cli/test/init-created-files-summary.e2e.test.ts`. Ablated to
the cause on a **pristine `origin/main` worktree with zero diff**:
```
node scripts/check-cross-package-test-inputs.mjs -> exit 0 (OK: 29 package(s) … 13 walked root(s) judged)
mkdir -p packages/spec/dist && (same command) -> exit 1 (the identical failure text)
rm -rf packages/spec/dist && (same command) -> exit 0 (restored; tree clean)
```
⇒ the verdict turns on whether the gitignored `packages/spec/dist`
exists, not on any source change — and building `packages/spec` is
exactly what AGENTS.md requires before the spec artifact gates run. CI
cannot see it: `Lint & Repo Gates` installs and never builds, so the
gate is green there. Filed as a finding for the dispatching seat rather
than fixed here — `packages/cli` and
`scripts/check-cross-package-test-inputs.mjs` are both outside this
card's file surface.
Noted, not filed: nothing else. The three source files were read whole
and carry no other stale phase promise; the remaining `M9.x` mentions in
the tree (`M9.9b` in `default-value-shape.ts`, `action.zod.ts`,
`objectql/engine.ts`; `M9.5` / `M9.7` in `objectql` and the docs page)
are ROADMAP section references outside the ruling's seven citation
sites, and the ruling names only the M9.1 / M9.2 phase promise.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3)_
Co-authored-by: Claude <noreply@anthropic.com>
0 commit comments