Skip to content

Commit 2cc4884

Browse files
claude[bot]claude
andauthored
docs(formulas): drop the M9.1 / M9.2 phase language from the docs page and the pending changesets (#17851)
The M9.1 / M9.2 expression phases — including the promise that `ast` becomes required in build output — were asserted across the tree and defined in no ADR. Decision batch #123 item 1 (director seat, 2026-09-12) ruled option **B**, maintainer verbatim 「同意」: the phase assertions are removed, and `ast` stays an accepted optional persisted form with no promise of becoming required. This PR is the **docs half** of that ruling — the published docs page and the two still-unconsumed changesets now state the contract in the present tense: `source` is the canonical persisted form, `ast` is an optional opaque structured value. Per the ruling's item 4 (「Files under `packages/spec` and `packages/formula` are the spec seat's: the `domain:skills` taker edits docs and coordinates the spec half as a sibling PR by that seat, ⛔ not a cross-lane rider」), nothing under `packages/` is touched here; the spec half is a sibling PR by that seat, tracked as #17849. No ADR is written — B means there is no phase to define. Part of #17323 ## Acceptance notes ### Premises, re-taken at this branch's base `758ac409` 1. **HOLDS.** `git grep -l 'M9\.[12]' origin/main` returns exactly seven files — `.changeset/blank-node-condition-refused-at-registration.md` (1 hit), `.changeset/flow-edge-condition-evaluated-slot.md` (3 hits), `content/docs/data-modeling/formulas.mdx` (1 hit), `packages/formula/src/cel-engine.ts`, `packages/formula/src/normalize.ts`, `packages/spec/CHANGELOG.md`, `packages/spec/src/shared/expression.zod.ts`. The last four are the spec seat's and are untouched here; after this PR the repo-wide count is those four and nothing else. 2. **HOLDS, with a timing note.** Both changesets are present and unconsumed under `.changeset/` at this base. ⚠️ The standing Version Packages PR #17076 (`changeset-release/main`, bot-authored) lists both of them as `removed` — it is the consuming side. So this correction has to land **before** that PR does; once a release consumes these files, the ruling's item 3 routes the same correction to a `CHANGELOG.md` erratum instead, which rides the spec half. 3. **HOLDS.** `formulas.mdx` :27 (`deleted in M9`), :111 (`M9.9b`) and :583 (`M9.5`) are other milestone references and are byte-identical — they do not appear in the diff, and `git grep -c 'M9' -- content/docs/data-modeling/formulas.mdx` still reports 3 (now at :27 / :112 / :584, shifted by the one line this PR adds). 4. **HOLDS.** All 22 open PRs' file lists were re-scanned; none names the three files, with the single qualification in premise 2 — #17076 lists the two changesets as consumed, which is not a competing edit. ### The sentences, before → after **A · `content/docs/data-modeling/formulas.mdx` :86-87** ``` - artifact always contains the full envelope (and, after M9.2, the AST). + artifact always contains the full envelope: `source` is the canonical persisted + form, and `ast` is an optional opaque structured value. ``` **B1 · `.changeset/flow-edge-condition-evaluated-slot.md` :30** — inside the blockquote that reproduces the refusal sentence: ``` - > engine evaluates `source` (the canonical persisted form of phase M9.1) and + > engine evaluates `source` (the canonical persisted form) and ``` **B2 · `.changeset/flow-edge-condition-evaluated-slot.md` :44-48** — the phase-2 promise itself: ``` - the persistence contract (`source` OR `ast`), whose docblock declares that - `ast` becomes required in build output at phase M9.2. When AST-only - evaluation lands, `EvaluatedExpressionSchema` is the one place to relax, and - every evaluated slot follows. + the persistence contract (`source` OR `ast`), where `ast` is accepted as an + optional opaque structured value and carries no promise of becoming required. + If AST-only evaluation is ever chartered, `EvaluatedExpressionSchema` is the + one place to relax, and every evaluated slot follows. ``` **B3 · `.changeset/flow-edge-condition-evaluated-slot.md` :73** ``` - carried only `ast` has no evaluable form under M9.1: author its `source`. A + carried only `ast` has no evaluable form: author its `source`. A ``` **C · `.changeset/blank-node-condition-refused-at-registration.md` :33** — the eighth site, which neither the card nor the ruling named. **What it said:** the same verbatim blockquote of the refusal sentence that B1 carries, with the identical `(the canonical persisted form of phase M9.1)` parenthetical; it is the only M9 reference in that file, and it gets the identical treatment: ``` - > engine evaluates `source` (the canonical persisted form of phase M9.1) and + > engine evaluates `source` (the canonical persisted form) and ``` Nothing else in the three files is touched: 3 files, 9 insertions, 8 deletions, and the changeset frontmatter (package and bump level) of both changesets is unchanged. ### D · Verification | reading | result | |---|---| | `git grep -l 'M9\.[12]' -- content .changeset` | 0 files (exit 1, no match) | | control — `git grep -c 'M9' -- content/docs/data-modeling/formulas.mdx` | still `3` (the :27 / :112 / :584 milestones) | | control — the pattern still matches elsewhere, so it is not a dead pattern: `git grep -c 'M9\.[12]'` | 4 files, all the spec seat's | | dark control — `git grep -c 'M91zzz' -- content .changeset` | 0 (exit 1), so the pattern does not match everything | | `grep -naP` for raw control bytes over the three files | clean (exit 1) | ### Coordination with the spec half, #17849 The blockquote edited in B1 and C is a **verbatim quote of an exported constant** — `EVALUATED_EXPRESSION_SOURCE_REQUIRED` in `packages/spec/src/shared/expression.zod.ts`, which still carries `of phase M9.1` on `main`. This PR drops exactly that phase clause and keeps `(the canonical persisted form)`, because "`source` is the canonical persisted form" is the present-tense contract the ruling's item 1 names. For the quote to stay a quote, the spec half must land that same wording in the constant. Flagged rather than worked around: `packages/spec` is not this lane's, per item 4. ### Changeset: `skip-changeset`, measured Two existing changesets are edited and none is added, so the changeset count this PR adds is 0 and the label is the applicable rule (`AGENTS.md` Post-Task Checklist step 3: the label is for a diff that publishes nothing from any released package). Measured rather than assumed: - no published package's `files[]` ships `content/**` or `.changeset/**` — 0 matching entries across every non-private workspace manifest; - the added strings (`carries no promise of becoming required`, `is an optional opaque structured value`) appear in 0 published paths; - positive control — `canonical persisted form` **does** hit a published path (`packages/spec/CHANGELOG.md`, `packages/spec/src/shared/expression.zod.ts`), so the measurement is not vacuous. The publication this correction affects is already declared by the two changesets it edits (`@objectstack/spec` minor, `@objectstack/service-automation` minor); a third changeset would declare a release of its own for a prose correction that rides theirs. The `CHANGELOG.md` erratum for the already-published M9.2 text is the ruling's item 2 and belongs to the spec half. ### Gates Derived in this worktree with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths — the tool took the change set from git itself at `9e12d4a62` vs merge base `758ac4097`): **51 commands**. Every one was run; reconciliation with `--ran` reports **51 derived, 51 run, 0 NOT-MEASURED, 0 UNRUN**, all exit 0. Four of them first exited `3` / `1` as **PREREQUISITE NOT MET** — nothing measured, unbuilt workspace packages, not findings: `check:doc-formula-expressions` and `check:doc-security-posture` (`@objectstack/formula`, `@objectstack/lint` unbuilt), `check:docs-transcript-drift` (same), and `check:skill-examples` (`packages/client-react/dist` held no `.d.ts`). After `pnpm exec turbo run build --filter='@objectstack/lint...' --filter='@objectstack/formula' --filter='@objectstack/client-react...' --concurrency=2`, all four are exit 0. Verdict lines, as each gate printed them: ``` ✓ check-adr-0087-registration: this PR adds no declared-breaking changeset (2 non-breaking changeset(s) seen). ✓ This diff introduces no `major` bump. ✓ No empty-frontmatter changeset introduced by this diff (2 declaring changeset(s) added). ✓ doc authoring guard: 401 files clean — no bare metadata literals. check-nul-bytes: OK (scanned 8476 text file(s) -- 8476 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes). ✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 438 files / 1374 TS blocks judged clean by @objectstack/formula. ✓ check-docs-transcript-drift: 4 declared transcript value(s) across 402 page(s) under content/docs/ equal what the registry derives today, and no undeclared block quotes one. ``` `node scripts/check-changeset-fixed.mjs` was run as well and is exit 0 — the derivation flags its roster as living under `.changeset`, one of this PR's paths, so its silence is evidence in neither direction until run. It reads `.changeset/config.json` and the workspace manifests, neither of which this PR touches. Repo-wide scans (`pnpm lint` and the rest) are CI's, as are the two path-scheduled CI jobs and the 11 declared-wide families the derivation names as unplaceable locally. ### Noted, not filed - `scripts/check-adr-0087-registration.mjs`'s pass line labels every **skipped** changeset "non-breaking" (`${result.skipped.length} non-breaking changeset(s) seen`), so the two changesets here — both declared breaking at base and skipped as inherited stock by the specified `M breaking at head, already breaking at base` row — are reported as non-breaking. The verdict is correct and the row is a pinned self-test case; only the summary wording under-describes. A pass-line wording nit, not a defect class, not a contract violation and not an authoring trap, so no card. Taker: none — no queued PR touches that script. --- _Generated by [Claude Code](https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6b97a20 commit 2cc4884

3 files changed

Lines changed: 9 additions & 8 deletions

File tree

‎.changeset/blank-node-condition-refused-at-registration.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ nodes:
3030
```
3131
3232
> An expression in an evaluated slot needs a non-blank `source`: the expression
33-
> engine evaluates `source` (the canonical persisted form of phase M9.1) and
33+
> engine evaluates `source` (the canonical persisted form) and
3434
> cannot evaluate `ast` alone, so an envelope carrying only `ast`, or a `source`
3535
> that is blank after trimming, would validate and register and then fault at
3636
> run time. Write `{ dialect: 'cel', source: '…' }`.

‎.changeset/flow-edge-condition-evaluated-slot.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ edges:
2727
```
2828
2929
> An expression in an evaluated slot needs a non-blank `source`: the expression
30-
> engine evaluates `source` (the canonical persisted form of phase M9.1) and
30+
> engine evaluates `source` (the canonical persisted form) and
3131
> cannot evaluate `ast` alone, so an envelope carrying only `ast`, or a `source`
3232
> that is blank after trimming, would validate and register and then fault at
3333
> run time. Write `{ dialect: 'cel', source: '…' }`.
@@ -42,10 +42,10 @@ edges:
4242
`invalid_union` issue at the slot carrying the sentence above; a blank
4343
`source` inside an envelope surfaces as one `custom` issue at `source`.
4444
- **`ExpressionSchema` / `ExpressionInputSchema` are NOT narrowed.** They remain
45-
the persistence contract (`source` OR `ast`), whose docblock declares that
46-
`ast` becomes required in build output at phase M9.2. When AST-only
47-
evaluation lands, `EvaluatedExpressionSchema` is the one place to relax, and
48-
every evaluated slot follows.
45+
the persistence contract (`source` OR `ast`), where `ast` is accepted as an
46+
optional opaque structured value and carries no promise of becoming required.
47+
If AST-only evaluation is ever chartered, `EvaluatedExpressionSchema` is the
48+
one place to relax, and every evaluated slot follows.
4949
- **`structuralConditionRefusal` no longer admits an `ast`-only envelope** on
5050
either structural condition slot (`config.condition` on a node,
5151
`edge.condition`). #15662's refusal admitted it on purpose through a
@@ -70,7 +70,7 @@ edges:
7070
it. None of them grew a rule of its own.
7171

7272
**What an author does with a refused edge condition.** An edge condition that
73-
carried only `ast` has no evaluable form under M9.1: author its `source`. A
73+
carried only `ast` has no evaluable form: author its `source`. A
7474
whitespace-only condition — envelope or bare string — was never a predicate
7575
(the engine answered `false`, so that edge never fired): remove the
7676
`condition` key if the edge was meant to be unconditional, or write the

‎content/docs/data-modeling/formulas.mdx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ Formatters: `currency[:CODE]`, `number[:decimals]`, `percent[:decimals]`,
8484

8585
At **input** time you may write a bare string for shorthand — the spec
8686
transforms it into the right envelope based on the field type. The compiled
87-
artifact always contains the full envelope (and, after M9.2, the AST).
87+
artifact always contains the full envelope: `source` is the canonical persisted
88+
form, and `ast` is an optional opaque structured value.
8889

8990
{/* os:check */}
9091
```ts

0 commit comments

Comments
 (0)