Skip to content

The M9.1 / M9.2 expression phases — including "ast becomes required in build output" — are asserted in six files and one published docs page, and defined in no ADR #17323

Description

@os-bill

Filed by the domain:spec execution seat, session_01MkQhmuuJAVDjmeWNixwDDH, 2026-09-10T06:40Z, out of the at-ACCEPT residue of #15807 (PR #17267). ⛔ Unclaimed. No domain:* label and no pm:* state: ⛔ both are the triage seat's to produce.

Measured on origin/main 501959b72.

What was measured

The platform has a two-phase plan for expressions, named M9.1 and M9.2, whose phase-2 clause is a hard promise about the build output:

packages/spec/src/shared/expression.zod.ts, the ExpressionSchema docblock:

Phase 1 (M9.1): source is the canonical persisted form. ast is reserved and accepted as opaque structured value — objectstack compile will fill it in M9.2 with the engine's parsed AST so the artifact carries an AST-only representation.

Phase 2 (M9.2+): ast becomes required in build output; source is kept only for round-trip / debug.

That promise is now load-bearing in six files across two subsystems and a published docs page — not one docblock. git grep -l 'M9\.[12]' origin/main returns exactly:

file what it asserts
packages/spec/src/shared/expression.zod.ts the phase definition itself, plus 6 further citations (:114, :138, :163, :420, …) that scope other rules to a phase
content/docs/data-modeling/formulas.mdx published documentation: "artifact always contains the full envelope (and, after M9.2, the AST)"
packages/formula/src/cel-engine.ts "…as the canonical form during M9.1 and revisit AST-only execution in [M9.2]"
packages/formula/src/normalize.ts "engine (M9.2 deliverable). Source is retained for round-trip / debug."
packages/spec/CHANGELOG.md released changelog text
.changeset/flow-edge-condition-evaluated-slot.md #15807's own changeset — i.e. the promise was cited again this week

The gap

git grep -l 'M9\.[12]' origin/main -- docs/adr returns 0 files. There is no ADR that defines M9.1, defines M9.2, or records the phase-2 promise.

Controls for that zero, all on 501959b72:

  • lit — git grep -lc 'expression' origin/main -- docs/adr28 files, so the path and the tool both work;
  • lit — git grep -c 'M9\.' origin/main -- .14 hits repo-wide, so the token itself is findable;
  • dark — git grep -c 'M91zzz' origin/main -- .0, so the pattern does not match everything.

⚠️ ADR-0032 (docs/adr/0032-unified-expression-layer.md) exists and is the natural home; it does not carry the phases.

Why it matters

  1. A published docs page tells customers the build output will change shape, and nothing behind that page is a decision record. If the phase is ever dropped or re-scoped, there is no document to amend — only six scattered comments, one of which is customer-facing prose.
  2. The promise gates real behaviour today. EvaluatedExpressionSchema exists precisely because M9.2 has not happened: source is required and non-blank in an evaluated slot because "the engine evaluates (M9.1), and ast alone cannot be run" (expression.zod.ts). The docblock at :138 states outright that when AST-only evaluation lands, that schema "is the one place to" change. So the phase is not aspirational — it is the stated reason a live schema is shaped the way it is.
  3. It is now load-bearing across a pin. spec/automation: FlowEdgeSchema.condition still accepts an envelope the engine cannot evaluate (ast-only, whitespace-only source) — the evaluated-slot rule of #15430 has not reached the edge condition #15807 / PR feat(spec)!: FlowEdgeSchema.condition is an evaluated slot — composes EvaluatedExpressionInputSchema; structuralConditionRefusal drops the ast-only admission (#15807) #17267 put EvaluatedExpressionInputSchema on the flow edge condition, and objectui mirrors that slot at its pin. Two repositories' contracts now rest on a milestone whose definition exists only in comments.

The question

Amend ADR-0032 to define M9.1 / M9.2 and record the phase-2 promise, or drop the phase language from all six files and let each rule state its own reason. Either is fine; the current state — a customer-facing promise with no decision record — is the one that is not.

⚠️ The seat has no reading on which. This looks like an ADR-shaped question, which is on the manual floor (代裁人工地板: ADR 变更), so ⛔ it must not be auto-adjudicated even if the direction looks obvious.

Restart-touch: docs/adr/0032-unified-expression-layer.md, packages/spec/src/shared/expression.zod.ts

Re-check before acting: git grep -l 'M9\.[12]' origin/main -- docs/adr — if this is no longer 0, the card is answered.

Dedup

Complete enumerations read 2026-09-10T06:40Z: objectstack open domain:spec = 111, open finding = 160, open domain:services = 94; union 295 grepped for M9 / ast / ExpressionSchema / expression envelope / compile.

Nearest neighbours, each read and judged not a duplicate:

Source

packages/spec/src/shared/expression.zod.ts (ExpressionSchema / EvaluatedExpressionSchema docblocks) · content/docs/data-modeling/formulas.mdx · packages/formula/src/cel-engine.ts, normalize.ts · .changeset/flow-edge-condition-evaluated-slot.md · #15807 / PR #17267


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions