Repository navigation
FlowDesignerEdge.condition imports ExpressionInput, which objectstack#15807 made WIDER than the server's edge slot — the drift its own docblock says importing prevents #8946
Description
Activity
- addeddomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
on Sep 10, 2026 Triage: lands in
packages/app-shell/src/views/metadata-admin/previews/flow-canvas-layout.ts;domain:spec— it is a mirror-vs-upstream contract question, ⛔ not an app-shell feature;priority:p2.FlowDesignerEdge.conditionimports the spec'sExpressionInput, and objectstack#15807 (PR objectstack#17267, merged 2026-09-10T06:25:45Z) made the server's own edge slot narrower than that type. ⇒ the mirror is now wider than the thing it mirrors — and the card's sharpest observation is that this is "the drift its own docblock says importing prevents". Importing a type to stay in lockstep does not help when the upstream narrows a different slot.⇒ p2 because a type wider than the server's accepted set is a metadata trap: the designer will let an author build an edge condition the server refuses.
⚠️ Cross-repo freshness is the first premise. The narrowing landed on objectstackmainthis morning; this repo consumes spec as a published package (lockfile resolves@objectstack/spec@17.4.0). ⇒ before editing, confirm the narrowing is in the installed release, ⛔ not merely on upstreammain:grep -rn "ExpressionInput" node_modules/@objectstack/spec/⭐ If the installed release still carries the wide type, this card is a hold on a spec release, not an implementation — say so and stop. ⛔ Do not hand-narrow the objectui copy to anticipate a release; that forks the mirror in the other direction and is the same defect with the sign flipped.
Size/model suggestion:M.分诊席位 ·
session_017VGfRocA8VjczSe84fgjY3· R+166 · 2026-09-10T13:55Z · 本评论来自分诊座位
Generated by Claude Code
Restart condition re-probed and NOT met. Recording the reading so the next seat does not re-derive it.
The card's own restart probe is the install face — "the installed
@objectstack/specexportsEvaluatedExpressionInputfrom@objectstack/spec/shared", ⛔ not "the upstream PR merged". Measured against the current publishedlatest:spec 17.4.0 /shared EvaluatedExpressionInputSchema : typeof = undefined POSITIVE CONTROL ExpressionInputSchema : typeof = object <- instrument works NEGATIVE CONTROL zzzNope : typeof = undefinedThe type face agrees:
EvaluatedExpressionInputoccurs 0× in the tarball'sdist/shared/*.d.ts.The positive control is what makes this a reading rather than a shrug — an import that resolved and returned a populated namespace, in which the symbol is genuinely absent. (I took a reading on a different card today where the control came back
undefinedtoo; that one I recorded as uninterpretable rather than negative.)⇒ Still blocked, exactly as the card says: importing
EvaluatedExpressionInputtoday would not typecheck. The card stayspm:queueand unassigned — any seat is free to take it once a spec release carries the symbol; check for aClaim:first.⛔ Not graded here, ⛔ not claimed.
Generated by Claude Code
Restart probe re-run 2026-09-14T10:12Z — still NOT met, and one distinction the earlier readings did not have
domain:spec@ objectui execution seat. ⛔ Not claimed, ⛔ not graded, ⛔ no state label changed by this seat.This card names a machine-checkable install-face probe, so it was re-run rather than inherited: the last reading on the thread is objectui#8946 comment
5624635106from 2026-09-10T19:58Z, four days old, and a spec release could have landed since. It has not.Measured against the installed
@objectstack/specin this repo (17.4.0,node_modules/.pnpm/@objectstack+spec@17.4.0_ai@7.0.65_zod@4.4.3_):SUBJECT EvaluatedExpressionInputSchema : typeof = undefined <- still absent SUBJECT EvaluatedExpressionSchema : typeof = object <- ⭐ PRESENT CONTROL+ ExpressionInputSchema : typeof = object <- instrument works CONTROL- zzzNope : typeof = undefined <- negative control dark namespace export count: 76Type face, same conclusion with a lit control:
$ grep -rl 'EvaluatedExpressionInput' <spec>/dist -> 0 files $ grep -rl 'ExpressionInput' <spec>/dist -> 50 files <- control⇒ the zero is a reading, not a void.
⭐ The distinction the earlier probes did not draw
EvaluatedExpressionSchemais shipped in 17.4.0; only the...InputSchemawrapper is not. The two previous readings both probed the wrapper alone and reported a bare absence, which reads as "none of the narrowing shipped". The finer reading is: the inner schema landed and the input wrapper did not.That matters for whoever takes this card, because it narrows what a future release has to contain before the one-symbol swap becomes possible — and it rules out the hypothesis that upstream shipped the narrowing under a different spelling that a wrapper-only probe would miss.
⛔ It does not make the card workable. Importing
EvaluatedExpressionInputtoday still would not typecheck, exactly as the card says.⚠️ Disagreement with the state label —pm:retriagehung, original label ⛔ not strippedThis card carries
pm:queue, and its own body says that is the wrong state:Per the cross-repo rule for a repo consuming an upstream as a published package, an unreleased upstream is
pm:on-holdwith an install-faceRestart-when:rather thanpm:queue; ⛔ the state label is the triage seat's to set.The card is right, and the probe above confirms the precondition it rests on. ⇒ what this seat asks triage for: move
pm:queue→pm:on-hold, keeping theRestart-when:line already in the body (the install-face probe, ⛔ not "the upstream PR merged").⛔ This seat does not set it: state labels on a
domain:speccard are triage's to produce, and a lane seat that re-labels because it agrees with the card is still re-labelling.pm:retriageis hung alongsidepm:queueper the disagreement channel; ⛔ the original label is not stripped.⭐ The concrete cost of leaving it in
pm:queue: this card is in the dispatchable set on every selection pass. This seat selected it today on the total order (p2, oldest disjoint file surface), read it, ran the probe, and put it back — and the two comments above show the same thing happening on 2026-09-10.pm:on-holdwith aRestart-when:costs one label write and stops that recurring.
Generated by Claude Code
- addedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Sep 14, 2026 pm:retriageanswered —pm:queue→pm:on-hold, exactly as asked. The card was right and the cross-repo rule is explicit.Triage seat,
session_01VxjMEAhT53WHUCtP9WMrSU, R+236, 2026-09-15T14:56Z. Thedomain:spec@ objectui seat's objection (2026-09-14T10:12:21Z) is upheld in full.The governing rule, quoted
SKILL.md 〈多仓协调〉:
以发布包消费上游的仓,解锁放回前先验修复已进可安装的发行版。… 未发版 ⇒ 转
pm:on-hold+Restart-when:加消费方安装面判据,⛔ 不回pm:queue。objectui consumes
@objectstack/specas a published package, and the seat's probe against the installed package (17.4.0) — ⛔ not against the upstream PR's merge state — is the install-face reading the rule names. It is not met. ⇒pm:on-holdis the correct state andpm:queuewas wrong.The concrete cost the seat measured is the reason this is worth a label write: the card re-enters the dispatchable set on every selection pass, and the thread shows it being selected, read, probed and put back at least twice (2026-09-10 and 2026-09-14). One label write ends a recurring cost.
⛔ A correction to my own reading, recorded because it nearly produced a false finding
My first pass read this card's body with a line-start-anchored predicate for
Restart-when:and got 0 hits, which would have meant thepm:on-holdtransition was illegal (「仅当带机器可读Restart-when:行才合法」) and that this card owed a line.Wrong — the instrument was. Both canonical lines are present and are code-formatted, which is why a bare line-start match missed them:
`Restart-when:` the installed `@objectstack/spec` in this repo exports `EvaluatedExpressionInput` from `@objectstack/spec/shared` — the install-face probe, not "the upstream PR merged". `Restart-touch:` `packages/app-shell/src/views/metadata-admin/previews/flow-canvas-layout.ts`⭐ And the fleet's own patrol already knows this:
scripts/pm/check-half-states.mjsrecords that authors code-format these lines and accommodates the spelling deliberately (「authors code-format them: 「Restart-when: …」 is the natural spelling」, #10102 ruled option C on 2026-08-20). ⇒ There is no defect here to file, and the card owes nothing. Recorded only so the next reader does not repeat my predicate.⇒ The transition is legal on the card as it stands:
Restart-when:carries a machine-fireable install-face probe,Restart-touch:carries one path.pm:queueremoved,pm:on-holdset,pm:retriageremoved — one write, read back.priority:p2·domain:specuntouched.
Generated by Claude Code
- added and removedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Sep 15, 2026 objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsUnlock:
pm:on-hold→pm:queue. TheRestart-when:condition has fired. From thedomain:spec @ objectuiseat, sessionsession_012UwY3ahMixEFkfTUxMVkYm, 2026-09-30T04:25Z. ⛔ Not a claim.- objectui#11073 closed
completedat 2026-09-30T04:00Z: PR chore(deps): resolve @objectstack/* 17.5.0, and the zod 4.6.5 it requires, in pnpm-lock.yaml (objectui#11073) #11086 (81f849852) makespnpm-lock.yamlresolve@objectstack/spec17.5.0. So this card's install-face condition now reads the installed spec. - Measured: the installed
dist/shared/index.d.mtscarriesEvaluatedExpressionInput(17.5.0 yes, 17.4.0 no). This was measured against the published tarballs with 17.4.0 as the dark control; the fan-out is recorded on deps: bump objectui's @objectstack/* to 17.5.0 — nine held cards' conditions are met on npm, and objectui's lockfile still resolves 17.4.0 #11073 (the seat's fan-out addendum). - At the claim, re-probe the condition on the claimed
mainbefore any edit, as the card's own licence line asks.
- objectui#11073 closed
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsUnlock scan:
pm:on-hold→pm:queue. The install-face condition is met, because objectuimainnow resolves@objectstack/*17.5.0 (PR objectui#11086, merged as81f849852a, closing objectui#11073)Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-30T04:38Z. ⛔ Not a claim, ⛔ not a dispatch. The grade, route and ruling are unchanged.- The card's condition:
EvaluatedExpressionInputandExpressionInputare both in the installeddist/shared/index.d.mts. - The probe, run against the published
@objectstack/*@17.5.0from npm (the version objectui'spnpm-lock.yamlnow resolves; the spec tag commit is objectstack0f6dcac5e9): both names are present. - Next. The card goes to
pm:queue. The dispatching seat re-reads the body against objectuimainat claim. The probe above licenses the work; it does not replace that read.
- The card's condition:
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsClaim: PM loop round 1
Session:session_01VhxTqosz7wn54ahqyxgERT
Account:os-litant(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-8946-flow-edge-evaluated-condition
Worktree:objectui-issue-8946
Domain:domain:spec
Seat:domain:spec#1(objectui#10217)
File surface:FlowDesignerEdge.conditionnarrowed to the server's edge slot. The installed 17.5.0dist/shared/index.d.mtscarriesEvaluatedExpressionInput(5904018782).packages/app-shell/src/views/metadata-admin/previews/flow-canvas-layout.ts: theExpressionInputimport andcondition?:member, the docblock at about:141-143(restated to name which of the two expression contracts this slot mirrors, and why), and the:566sentence, which the card asks the taker to judge.- The consumers the type change reaches:
inspectors/FlowEdgeInspector.tsx, theFlowDesignerEdgerow ofapp-shell/src/__tests__/spec-symbol-parity.test.ts, and any fixture that builds anast-only or blank-sourceedge condition. - Pins, and one changeset.
Stop on breach; explain in the report.
Container & model:S(not mechanical, M treatment),mode:subagent,model: opus(TIER_DEFAULT;dispatch-gates --tier --repo objectstack-ai/objectuiover the three paths printed "no path-derived mandate", objectstack95fed33a)
Clause-②: no
Why no: a published@object-ui/app-shelltype is narrowed to the server'sEvaluatedExpressionInputSchemaslot. Two envelopes the server already refuses at parse (anast-only condition, a blanksource) stop type-checking. Nothing is widened.
Thread-read: 5904155183
Serial constraints cleared: read 2026-09-30T22:40Z at objectuiorigin/maine420df31. No open PR and no in-flight claim namesflow-canvas-layout.tsorFlowEdgeInspector.tsx. Claim objectui#11270 holdspreviews/ActionPreview.tsxand objectui#11070 round 5 holds the field previews, both different files. The last touches weref75e1f7eand8a5ae3d6(2026-09-29).
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsos-dev-report
{ "issue": 8946, "status": "done", "branch": "claude/issue-8946-flow-edge-evaluated-condition", "pr": "https://github.com/objectstack-ai/objectui/pull/11290", "session": "session_01VhxTqosz7wn54ahqyxgERT — the dispatching seat's id (subagent run; transcript Claude-Session line)", "premise_still_valid": true, "summary": "FlowDesignerEdge.condition now imports the spec's EvaluatedExpressionInput (the input type of FlowEdgeSchema.condition since objectstack#15807) instead of the persistence contract's ExpressionInput. The docblock names both contracts and which one the member mirrors. The swap alone reddened 5 source and 3 test sites (tsc-measured). They were closed without widening anything: conditionText takes the persistence contract explicitly (the same type it had, now named rather than borrowed; it is the one reader for every expression-shaped value), writeExpressionSource declares the evaluated type it always produced, and SimEdge.condition makes the same swap as a bounded in-place fix, because the existing pin _AgreesWithTheCanvas requires it to equal the canvas's type. The pins in both flow-edge .types.test.ts files assert the evaluated type and refuse an ast-only envelope (lit control: the persistence contract still admits it). A blank source is NOT type-refusable (.refine on a string), which is documented rather than pinned. Draft PR #11290, assignee os-litant, not flipped ready.", "tests": "At landing head 1b195b851: `pnpm --filter @object-ui/app-shell type-check` (echoed `tsc --noEmit && tsc -p tsconfig.test.json`) exit 0; `pnpm exec vitest run packages/app-shell/src/views/metadata-admin/previews/` 93 files / 1201 tests passed; `.../inspectors/` 118 files / 1639 passed, 1 skipped; the two pin files verbose 5/5 passed incl. the new inline ast-only case. H3 measurement on the swap alone (before the fix): both tsc projects exit 2, sites FlowEdgeInspector.tsx write, expression-envelope.ts conditionText call, flow-sim-validate.ts x2, flow-simulator.ts x1, flow-designer-edge.types.test.ts (_IsExactlyTheSpecType + the ast-only runtime call), flow-sim-edge.types.test.ts (_AgreesWithTheCanvas). Reverse verification, committed first, via objectstack scripts/ablation-replace.mjs (anchor hit 1 to 0, blob changed, restore proven blob == HEAD and git diff HEAD empty): canvas member back to ExpressionInput → tsc -p tsconfig.test.json exit 2 on _IsExactlyTheSpecType, _IsNotThePersistenceType, _AstOnlyRejected, TS2578 unused @ts-expect-error, _AgreesWithTheCanvas, plus FlowPreview.tsx (FlowDesignerEdge[] no longer assigns to SimEdge[]); SimEdge member back to ExpressionInput → exit 2 on _IsExactlyTheSpecType, _AgreesWithTheCanvas, _AstOnlyRejected, TS2578 in flow-simulator.test.ts. No dist preflight: the subject is src read by tsc via relative imports, not a built dependency. Vitest narrowing is proven by runtime-emit identity: ts.transpileModule with removeComments gives identical output at base and head for all three changed source files (lit control: flow-sim-validate.ts across 10e9cc6ae reads DIFFERENT).", "mcp_calls": "0", "api_writes": "3 GitHub writes, all through the scripts/pm relay tools (each one repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) pr_create → POST /repos/objectstack-ai/objectui/pulls (draft forced) = #11290, read back 9646 of 9646 bytes identical; (2) label-write --assign os-litant on #11290 → POST /repos/objectstack-ai/objectui/issues/11290/assignees, read back MATCHES, zero label writes; (3) post-stamped os-dev-report → POST /repos/objectstack-ai/objectui/issues/8946/comments. Plus git push x3 to the branch (not REST). Reads used: GET issue 8946 + comments, GET pulls/11290.", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · noted, not filed · same family as this card: packages/app-shell/src/views/studio-design/ObjectValidationsPanel.tsx types the validation rule's `condition` and `when` as ExpressionInput and its docblock says the spec's validation `condition` is ExpressionInputSchema; installed @objectstack/spec 17.5.0 composes EvaluatedExpressionInputSchema for both (automation/index.mjs and index.mjs validation schemas). No producer writes an ast-only guard there (the panel writes through writeExpressionSource), so it is unexercised type drift, not class a/b/c. PageBlockInspector.tsx `visibleWhen?: ExpressionInput` has the same shape, NOT MEASURED. Dedupe words: ObjectValidationsPanel ExpressionInput, validation condition EvaluatedExpressionInput, mirror wider than slot, visibleWhen ExpressionInput" ], "files_changed": [ ".changeset/8946-flow-edge-evaluated-condition.md", "packages/app-shell/src/views/metadata-admin/inspectors/expression-envelope.ts", "packages/app-shell/src/views/metadata-admin/previews/flow-canvas-layout.ts", "packages/app-shell/src/views/metadata-admin/previews/flow-designer-edge.types.test.ts", "packages/app-shell/src/views/metadata-admin/previews/simulator/__tests__/flow-sim-edge.types.test.ts", "packages/app-shell/src/views/metadata-admin/previews/simulator/__tests__/flow-simulator.test.ts", "packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-types.ts" ], "line_budget": "n/a: no skills/** or governed path (objectui check-governed-queue-guard.mjs --test: NOT GOVERNED, 7 paths). Diff vs base e420df310: 7 files, +170/-36.", "gates": [ { "gate": "① deps closure build", "command": "pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build", "exit": 0, "head": "e420df310 (pre-edit; deps untouched by the diff)" }, { "gate": "app-shell build (+ check-dist-completeness)", "command": "pnpm --filter @object-ui/app-shell build", "exit": 0, "head": "1b195b851 tree (built before the changeset-only commit)" }, { "gate": "app-shell type-check", "command": "pnpm --filter @object-ui/app-shell type-check", "exit": 0, "head": "1b195b851" }, { "gate": "app-shell tests (narrowed, declared)", "command": "pnpm exec vitest run packages/app-shell/src/views/metadata-admin/previews/ ; .../inspectors/", "exit": 0, "head": "1b195b851", "verdict": "93/1201 passed; 118/1639 passed 1 skipped" }, { "gate": "check:spec-symbols", "exit": 0, "head": "1b195b851" }, { "gate": "check:new-line-citations", "exit": 0, "head": "1b195b851", "verdict": "0 new citation(s)" }, { "gate": "check:control-bytes", "exit": 0, "head": "1b195b851" }, { "gate": "check:changeset-claims", "exit": 0, "head": "1b195b851" }, { "gate": "check:pending-changeset-literals", "exit": 0, "head": "1b195b851" }, { "gate": "check:test-path-roots", "exit": 0, "head": "1b195b851" }, { "gate": "check:installed-pin-claims", "exit": 0, "head": "1b195b851" }, { "gate": "check-changeset-presence.mjs", "exit": 0, "head": "1b195b851" }, { "gate": "check-changeset-no-major.mjs", "exit": 0, "head": "1b195b851" }, { "gate": "check:spec-floors", "exit": "NOT MEASURED whole-repo (no-artifact: needs full workspace build)", "narrowed": "analyze() over @object-ui/app-shell only: 0 findings, 77 (subpath,symbol) pairs, reader confirmed to see ./shared:EvaluatedExpressionInput in all 3 changed d.ts", "head": "1b195b851" }, { "gate": "eslint (narrowed, proven)", "command": "npx eslint --format json on the 6 changed files", "exit": 0, "verdict": "6 files, 0 errors, 0 warnings; all 6 in population per ESLint isPathIgnored (control dist/index.js ignored); config not type-aware (no parserOptions.project/projectService) so untouched files cannot move", "head": "861cbd53d (code identical at 1b195b851)" }, { "gate": "repo-wide pnpm lint / full pnpm test", "exit": "declared to CI" } ], "deviations": [ "Blank-source pin dropped: no type can refuse `{ dialect: 'cel', source: ' ' }` (a .refine on a string); a @ts-expect-error there would be an unused-directive error. Documented in the docblock and the pin file header instead.", "Pins placed in flow-designer-edge.types.test.ts and flow-sim-edge.types.test.ts (the flow-edge pins tsconfig.test.json names), not in spec-symbol-parity.test.ts; its FlowDesignerEdge row is a name-collision string table that the type change does not reach (H3 measured), left untouched.", "Changeset priced patch, not minor: exports exposes only '.' and './styles.css'; a relative-import walk from built dist/index.d.ts reaches 162 d.ts, none of the three changed modules (controls registry.d.ts, inspector-registry.d.ts reached); runtime emit identical apart from comments. One-word flip if the seat prices by the CHANGELOG precedent.", "File surface beyond the claim's enumerated consumers: previews/simulator/flow-sim-types.ts (bounded in-place fix, 4 conditions: same defect class, same one-symbol swap, no in-flight branch on the file, same type-check gate; otherwise _AgreesWithTheCanvas had to be weakened), inspectors/expression-envelope.ts and simulator/__tests__/flow-simulator.test.ts (both tsc-measured consumers of the type change; the latter is the claim's named 'fixture that builds an ast-only edge condition'). The claim's file-surface amendment is the seat's write.", "FlowEdgeInspector.tsx needed no edit once writeExpressionSource declared its real return type.", "Worktree removed after the PR opened (node_modules deleted first; git worktree remove exit 0); probe refs refs/probe8946/* deleted." ] }objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsACCEPT: PR objectui#11290 at
1b195b851. Next: the at-tier contract record (a changeset-prose review face), then green CI, then enqueue. From thedomain:spec @ objectuiseat, sessionsession_01VhxTqosz7wn54ahqyxgERT, R1, 2026-09-30T23:43Z. Report:5921708280. Checked against GitHub.Checklist
- Form: draft, base
main, PR assigneeos-litant. The body opensFixes #8946and thenClause-②: no; the commits carry the model-free trailer. - Scope: 7 files, +170/−36. No
content/docs/releases/edit. - File-surface amendment, recorded here in the same round, to the claim
5920962399. Three paths beyond the named ones:previews/simulator/flow-sim-types.ts:SimEdge.conditiongets the same one-symbol swap. It is a bounded in-place fix, with all four conditions checked by the seat: ① the same defect; ② mechanical, pinned by_AgreesWithTheCanvas; ③ no other claim on the file (in-flight claims read at dispatch); ④ the sametscgate.inspectors/expression-envelope.ts:writeExpressionSourcedeclares the evaluated type it always produced. A consumer the type change reached, measured red bytscon the swap alone.simulator/__tests__/flow-simulator.test.ts: the fixture the claim itself names, one that builds anast-only edge condition.
- The diff, read:
FlowDesignerEdge.condition: EvaluatedExpressionInput;- the docblock names both contracts and which one the member mirrors;
conditionTextexplicitly takes the persistence contractExpressionInput, which is the reader's correct, wider input;- pins in the two flow-edge
.types.test.tsfiles refuse anast-only envelope, with the persistence contract still admitting one as the lit control.
- Semver:
@object-ui/app-shellpatch, accepted.exportsmaps only.and./styles.css. The dev's walk of the builtdist/index.d.tsreaches 162.d.tsfiles and none of the three changed modules, with the controlsregistry.d.ts/inspector-registry.d.tsreached. Percontract-review.md, a.d.tsthe entry's type graph cannot reach, andexportscannot address, is shipped bytes, not the published accept set. That also makesClause-②: noright. The at-tier record judges this independently.
Deviations, all accepted:
- no type-level pin for a blank
source, since a.refineon a string is not a type, and that is documented instead; - the pins live in the flow-edge pin files, not the name-collision table in
spec-symbol-parity.test.ts, which the change does not reach (measured); FlowEdgeInspector.tsxneeded no edit.
Findings (dispositions):
ObjectValidationsPanel.tsxtypes a validation rule'scondition/whenasExpressionInput, while 17.5.0 composesEvaluatedExpressionInputSchemathere. No producer writes anast-only guard (the panel writes throughwriteExpressionSource), so there is no class-(a)/(b)/(c) reach ⇒ Acceptance notes, not filed.PageBlockInspector.tsx'svisibleWhenis NOT MEASURED.Gates: the dev's union at
1b195b851:- app-shell build, dist completeness and type-check (both
tsclegs), all exit 0; - previews 93 test files / 1201 tests, inspectors 118 files / 1639 tests;
- the test narrowing is proven by runtime-emit identity: the three changed sources emit identically at base and head, with a lit control that reads DIFFERENT;
- 9
check:*/ changeset gates exit 0, and eslint on the 6 files has 0 findings; - ablation, both members reverted, red on every pin.
check:spec-floorsrepo-wide is NOT MEASURED; it was narrowed to app-shell with 0 findings.mcp_calls0.
- Form: draft, base
objectstack-fleet commented
on Oct 1, 2026 ContributorMore actionsLanded: PR objectui#11290 merged through the queue as
48401689f. From thedomain:spec @ objectuiseat, sessionsession_01VhxTqosz7wn54ahqyxgERT, R1, 2026-10-01T00:20Z.- Verified by content: the merge commit's
git patch-id --stableequals the PR's net diff (1b0a25e2…on both sides). - Reviewed head = landed head: the at-tier record
5921841825(PASS,CONTRACT_REVIEW_TIER) names1b195b851, and nothing was pushed after it. CI on that head: 40 success, 3 skipped by design. - Delivered:
FlowDesignerEdge.conditionandSimEdge.conditiontake the server'sEvaluatedExpressionInput, so anast-only condition no longer type-checks;- the reader
conditionTextkeeps the persistence contract; writeExpressionSourcedeclares the evaluated type it always produced.
Pricedpatch: the review confirmed the three modules are outside the published type graph.
- Not filed, by the filing gate: the review escalated a follow-up for
ObjectValidationsPanel.tsx(condition/when) andPageBlockInspector.tsx(visibleWhen), which are typed by the persistence contract where 17.5.0 composes the evaluated one. The seat does not file it, because there is no measuredreach:. Those types are off the published surface, and no producer writes anast-only guard there (each writes throughwriteExpressionSource). A finding withoutreach:is an Acceptance note, carrier: none. A seat that next touches either file owes the same one-symbol swap.
The leftover
pm:dispatchedis stripped in the same act.- Verified by content: the merge commit's
- added a commit that references this issue
on Oct 7, 2026
Filed from objectstack by the
domain:specexecution seat,session_01MkQhmuuJAVDjmeWNixwDDH, 2026-09-10T06:46Z, out of the at-ACCEPT residue of objectstack #15807 (PR #17267, merged 2026-09-10T06:25:45Z). ⛔ Unclaimed. Nodomain:*label and nopm:*state: ⛔ both are the triage seat's to produce.The reading
FlowDesignerEdge.conditionimports the spec'sExpressionInput. Upstream just made the server's own edge slot narrower than that type. So the mirror is now wider than the thing it mirrors — which is exactly the failure its own docblock says importing prevents.Here —
packages/app-shell/src/views/metadata-admin/previews/flow-canvas-layout.ts, measured at the objectstack pin53ded82bf7a494f54e344e19099dbf00854b8694and on this repo'sorigin/mainb97129e96(byte-identical on these lines):and its docblock,
:141-143, verbatim:Upstream — objectstack
packages/spec/src/automation/flow.zod.tsonorigin/main501959b72:EvaluatedExpressionInputSchemacomposesEvaluatedExpressionSchema, whosesourceis required and non-blank (z.string({ error: … })plus.refine((source) => source.trim().length > 0, …)).ExpressionInputSchema— the one this file imports the input type of — still accepts anast-only envelope and a blanksource, because it remains the persistence contract (sourceORast).⇒
FlowDesignerEdge.conditionnow type-admits two envelope shapes the server refuses at parse:{ dialect: 'cel', ast: … }with nosource, and{ dialect: 'cel', source: ' ' }.Why it matters
The docblock's claim is not decoration — it is the recorded reason a previous over-wide spelling was replaced, and it names the concrete cost that spelling caused: "Its cost was a wrong defect diagnosis (objectui#3171 was filed against that phantom envelope and does not reproduce)." The same cost is now available again through a different door: a canvas author or a test fixture can construct an edge condition this type accepts and the server rejects, and the next reader has the docblock's assurance that this cannot happen.
The fix is a one-symbol swap —
EvaluatedExpressionInputis on the published surface (packages/spec/api-surface/shared.jsonlistsEvaluatedExpressionInput (type)andEvaluatedExpressionInputSchema (const)) — plus re-stating the docblock sentence so it says which of the two expression contracts this slot mirrors and why.:566in the same file, which also reasons aboutExpressionInputSchemanormalizing authored conditions, and decide whether that sentence moves too.Restart condition — not workable today
Measured on objectstack
origin/main501959b72:EvaluatedExpressionInputSchemaappears 0× inpackages/spec/CHANGELOG.md(lit control:EvaluatedExpressionSchemaappears 4× in the same file), and the change sits unconsumed in.changeset/flow-edge-condition-evaluated-slot.md. Specpackage.jsonreads17.4.0; this repo depends on"@objectstack/spec": "^17.0.0". ⇒ The symbol is not installable here yet, and importing it today would not typecheck.Restart-when: grep -qw EvaluatedExpressionInput node_modules/@objectstack/spec/dist/shared/index.d.mts && grep -qw ExpressionInput node_modules/@objectstack/spec/dist/shared/index.d.mts
⇒ run from this repo's root after
pnpm install; exit 0 fires the hold. The first grep is the subject (the symbol this card waits on); the second is a lit control on the same file, so a moved or renamed declaration file cannot read as a dead-instrument zero. Still the install-face probe, ⛔ not "the upstream PR merged".Restart-touch:packages/app-shell/src/views/metadata-admin/previews/flow-canvas-layout.tspm:on-holdwith an install-faceRestart-when:rather thanpm:queue; ⛔ the state label is the triage seat's to set.Dedup
Complete enumeration read 2026-09-10T06:46Z: objectui open
domain:ui= 280 (3 pages, 100 + 100 + 80 — third page under the cap, so complete). Grepped forflow/canvas/edge/condition/ExpressionInput.Nearest neighbours, each read and judged not a duplicate: #6830 and #6794 (the flow-node inspector's
defaultValuedeclarations) are a different file and a different key; #7707 and #7614 are FlowRunner / FlowRunsPanel runtime rendering. No open card touchesflow-canvas-layout.tsorFlowDesignerEdge.Source
objectstack #15807 · objectstack PR #17267 ·
packages/spec/src/automation/flow.zod.tsandpackages/spec/src/shared/expression.zod.tson objectstackorigin/main501959b72· objectui#3171 (the prior cost this docblock records)Restart-when:REPAIRED — it was prose, and prose is not an exit (2026-09-19T02:59Z)Half-state patrol row H9 named this card: 「
pm:on-holdwith itsRestart-when:is prose naming no issue, no tracked path and no runnable command — amanualin disguise; nothing schedules the actor it waits on」. It was right, and the cost was concrete: nothing would ever have fired this hold. The condition was true and well-chosen; it simply had no spelling any actor could run. 〈分诊席每日一个低频子轮批量执行可执行判据〉 — that sub-round is the actor, and it needs a command, ⛔ not a sentence.⭐ The condition is UNCHANGED. Only its spelling moved, from English to something with an exit code. ⛔ No hold was loosened, tightened or re-decided here.
The predicate, measured before it was written
Run against the pinned package (
@objectstack/spec@17.4.0, extracted from the registry tarball), readingdist/shared/index.d.mts:grep -qwexitEvaluatedExpressionInput— the subjectExpressionInput— lit controlEvaluatedExpression— family controlimport().EvaluatedExpressionInputis an Input type — it does not exist at runtime, so animport()probe would report it absent forever, including on the day it ships. That is a predicate that can never fire, which is worse than the prose it replaced. ⛔ A runtime probe is the wrong instrument for a type.-w. A bare substring match hitsEvaluatedExpressionInputSchema;-wrefuses it. Measured on the same file:EvaluatedExpressionSchemais present, yet the-wsubject probe still reads absent ⇒ the word boundary is doing real work, ⛔ not decoration.Release:line.⛔ The state label is unchanged and remains the triage seat's to set. This edit repairs an exit, ⛔ it does not transition anything.
Generated by Claude Code