Skip to content

fix(studio): the Data pillar's Validations and Actions panels and ResourceEditPage keep a CEL hold with the draft (#27629) - #27697

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-9u7v4h
Oct 29, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-9u7v4h

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

维护者速读

  • 改了什么:Studio 数据页的「校验规则」「动作」两个面板,以及元数据编辑页(ResourceEditPage),表达式写坏后离开标签页或换选中项,草稿仍然不保存;页面上出现一行提示点名是哪条规则 / 哪个动作 / 哪一项,带「Show me」跳回去修。
  • 为什么改:按 Studio Data › Form: closing the field inspector releases the CEL error hold and autosaves the broken formula (returnType dropped); switching object discards the held edit silently #27485 裁决 A(6500685972,「同意」)第 3 条把「拦截跟草稿走」推广到兄弟面;以前一离开标签页或换选中,自动保存就把引擎拒收的表达式写进草稿,直到发布才报错且不点名。
  • 风险与回滚:只改前端拦截判定,不动 useDraftAutoSave 的计时器与单飞;最坏情况是多拦一次(提示行 + Show me 可解),回滚即 revert 本 PR。
  • 先看:ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx(panelCelVerdicts 一段)、ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx(inspectorVerdicts 与 selectionSlicePath)、两个新测试文件。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/DataPillar.panelHold-27629.test.tsx packages/app-shell/src/views/metadata-admin/ResourceEditPage.celHold-27629.test.tsx,预期 8/8 通过。
  • 需要你做的:无,席位落地。

Fixes #27629

Clause-②: no
Metadata-protocol: none

What changes

The maintainer's ruling on #27485 (comment 6500685972, letter A) makes a CEL parse-fault hold belong to the draft, not to the open editor. PR #27617 did that for the Data pillar's field inspector; this PR ports the same shape to the sibling surfaces item 3 of that ruling names. One measured repro per surface came first (all failed on main, see Tests).

  • Data pillar, Validations and Actions panels. The panels now also report their verdicts per rule or action name (onCelVerdictsChange, pruned to the items in the draft; onBlockingIssuesChange is unchanged). DataPillar keeps each verdict in panelCelVerdicts with the stamp of what it judged (panelItemCelStamp: the rule or action itself plus the object's field names, the counterpart of fieldCelStamp), retaken while the panel is mounted. With the tab left, a kept verdict holds while the draft still carries that stamp; an edit to the item, its removal or the object's next load ends it. While the panel is mounted its own report is the live verdict and wins. The held line names the item (new clauses engine.studio.held.celInvalidRule / celInvalidAction), its "Show me" reopens it (the Validations panel gains the focus prop the Actions panel already had), and the rail switch's flush-or-ask, the Studio Publish refusal and the held-edit report all read the same objHeld, unchanged.
  • Settings panel: left out, does not reproduce. ObjectDefaultInspector mounts no CEL editor, so the panel never reports a verdict (its own header says so). It keeps the tab-stamped count it had.
  • ResourceEditPage. Each selection's last live inspector verdict is kept in inspectorVerdicts with the fingerprint of the draft slice it judged: selectionSlicePath reads the selection id as a path (a page block's regions[0].components[0]), else finds the object whose id / name / key, or map key, is the id; the default inspector and an unlocatable id fall back to the whole document. The fingerprint is issueSliceFingerprint, the one the server-refusal hold already uses. A kept verdict holds Save, the autosave and the shortcut while that slice is unchanged; the editor shows a held line (engine.studio.held.inspectorInvalid) naming the item, and "Show me" reselects it. Item change and the item's load clear the kept verdicts.
  • ⛔ Per ruling item 4: no dialog on close, no silent revert, no second autosave path, and useDraftAutoSave.ts is untouched.

Tests

  • New DataPillar.panelHold-27629.test.tsx (5 cases): leaving Validations / Actions keeps the hold and the held line names the rule / action; "Show me" reopens it with its text and fixing it releases the hold; removing the rule ends it.
  • New ResourceEditPage.celHold-27629.test.tsx (3 cases): clearing or changing the selection keeps Save and the autosave held and names the block; "Show me" reselects it and fixing it releases.
  • DataPillar.panelGate.test.tsx: the case "expires the panel count when the author leaves the Rules tab" pinned the superseded tab stamp; flipped to "keeps the hold when the author leaves the Rules tab, and names the rule", citing the ruling.

Readings, all on the final head 7484a827 (the work merged with origin/main at 15b099e7) unless a line says otherwise:

Repro on main (before the fix, tree 5d759b1 + the two new test files): 8 failed / 8
DataPillar.panelHold-27629: 5 failed (leaving Validations / Actions: client.save called 1 time; held line absent)
ResourceEditPage.celHold-27629: 3 failed (save called 1 time after clearing or changing the selection; no held line)
Test Files  2 failed (2) · Tests  8 failed (8)
Affected suites (pre-merge head efe3f9a): 198 files / 1283 tests passed

Every DataPillar.*, StudioDesignSurface.*, ObjectValidationsPanel*, ObjectActionsPanel*, ObjectSettingsPanel* test under views/studio-design/ and every ResourceEditPage.* test under views/metadata-admin/, --reporter=verbose, under the verification lock: Test Files 198 passed (198) · Tests 1283 passed (1283), lock VERDICT command-exit 0. The rest of the ui tree is left to CI Test UI.

After merging origin/main (head 7484a82)
pnpm install --frozen-lockfile :: exit 0
pnpm --filter '@object-ui/app-shell^...' build :: exit 0
pnpm --filter @object-ui/app-shell run type-check :: exit 0
vitest: DataPillar.panelHold-27629, ResourceEditPage.celHold-27629, DataPillar.panelGate, DataPillar.celGate, ResourceEditPage.celGate, ObjectValidationsPanel.celGate :: Test Files 6 passed (6) · Tests 27 passed (27)
Ablations (one per surface, restored, blob == HEAD proven by ablation-replace)
  • StudioDesignSurface.tsx: the kept panel-verdict condition replaced by if (false) { → leaving the Validations tab keeps the hold… and leaving the Actions tab keeps the hold… both red (2 failed); restored, git diff HEAD empty.
  • ResourceEditPage.tsx: the kept inspector-verdict condition replaced by if (false) { → all 3 cases of ResourceEditPage.celHold-27629 red; restored, git diff HEAD empty.
  • app-shell tests import src directly, so no dist build sits between the mutation and the run.
Derived gates (dispatch-gates --commands on this diff): 34 run, 34 exit 0, reconciled
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0

dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. Also node scripts/check-new-cross-file-line-citations.mjs (ui): 0 new citations, exit 0. Families CI runs beyond the derived set are read from this PR's check-runs.

Acceptance notes

  1. Residual 250 ms edge, named again, not closed. The editors lint 250 ms after the last keystroke, so a flush (the Data rail switch) inside that window can still read the previous verdict, and a reopened held item reports nothing for about that long (its kept verdict stands meanwhile, so it stays held). Closing it needs a "lint pending" signal from the editors to the host; no such signal exists on this surface, and adding one reaches into every CEL editor, so it is out of this card's file surface. The timer path stays safe: a rising hold cancels the 1.5 s timer and a falling one re-arms a fresh one.
  2. ResourceEditPage's slice locator is best-effort and fails toward release. When a selection id is neither a path nor an item identity in the draft, the kept verdict is stamped against the whole document, so any edit ends the hold early (the old behaviour, narrowed). A wrongly located slice can over-hold; the held line's "Show me" reopens the item and its live verdict then wins, so Save is never wedged with nothing on screen.
  3. The inspector channel on ResourceEditPage is shared. It carries CEL faults and the required-field refusal in a gated section (ruling 5831744213); both are now kept with the draft, since the count does not tell them apart. The held line's wording is neutral for that reason.
  4. Stale contract prose outside this card's surface. MetadataInspectorProps.onBlockingIssuesChange in inspector-registry.ts still says hosts "must reset their own count when the selection changes"; both hosts now keep it with the draft (the field inspector since fix(studio): a Data rail switch sends the pending edit first; a broken CEL formula stays held after the inspector closes and keeps its returnType (#27511, #27485) #27617). Not edited here (file not in the claim); a doc line for the seat to route.
  5. Commit trailer. The first commit on this branch (49b01042) carries the dispatching seat's session id in its Claude-Session: trailer instead of this run's; later commits carry this run's. History is not rewritten (the branch is not claude/issue-*, so no force push).

Verification

Run the two new suites (command in 维护者速读). In a running Console: open Studio, Data, an object, Validations, set a rule's guard to record.status ==, switch to Form: the draft stays unsaved and the line "Not saved yet: an expression on the rule “…” has an error to fix" appears with Show me.


Generated by Claude Code

…tPage CEL hold (#27629)

Fail on main: leaving the Validations or Actions tab, or clearing or
changing the ResourceEditPage selection, releases the hold and the
autosave sends the broken predicate.

Claude-Session: https://claude.ai/code/session_01Utp6CaJoinvGQSbHeDaWBe
Co-authored-by: Claude <noreply@anthropic.com>
…he draft; changeset (#27629)

Claude-Session: https://claude.ai/code/session_01Utp6CYaLJTMXdChBaqK9KL
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

席位核对

ACCEPT · head 7484a82795ca27cd73f7d1b36fcbd03a821a9e8c · 2026-10-29T19:40Z

  • Shape: draft → main, Fixes #27629 on its own line, no other card number beside a closing keyword. Clause-②: no and Metadata-protocol: none match the claim; @object-ui/app-shell is private and @object-ui/i18n gains three held-clause keys (en + zh). No contract review owed.
  • Ruling: implements item 3 of the maintainer's ruling on Studio Data › Form: closing the field inspector releases the CEL error hold and autosaves the broken formula (returnType dropped); switching object discards the held edit silently #27485 (comment 6500685972) for the sibling surfaces, inside item 4's bounds: no close dialog, no silent revert, no second autosave path, useDraftAutoSave.ts untouched.
  • Premise, measured: one repro per surface first, 8 new cases red on main. Validations and Actions reproduced: they report per-item verdicts (onCelVerdictsChange), the Data pillar keeps them in panelCelVerdicts stamped by panelItemCelStamp and folds a held rule or action into objHeld, so the held line with "Show me", the rail's flush-or-ask and the Publish refusal read it unchanged. Settings did not reproduce (its inspector mounts no CEL editor) and is left out. ResourceEditPage reproduced: kept inspector verdicts stamped with issueSliceFingerprint over the selection's slice, failing toward release (an unlocatable slice is the whole document, so any edit ends the hold), with its own held line and "Show me". The panelGate case "expires on tab leave" is flipped per the ruling.
  • Gates (check-runs on the head): 44 success, 11 skipped, 0 failure. check-governed-merges --pr 27697: NOT governed, 830 changed lines.
  • Acceptance notes, not filed: the residual flush-inside-250-ms lint window (needs a lint-pending signal, outside this surface); the inspector-registry.ts doc line that still says hosts reset on selection change (a one-line doc fix for the next PR that touches that file).
  • Accepted deviations: commit 49b01042's trailer carries the seat's session id (not rewritten, no force push); the attribution line links the card thread.
  • Landing: non-governed, all checks green, mergeable_state clean ⇒ the seat lands it (pr_ready + automerge_enable).

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 29, 2026 19:41
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 29, 2026 19:41
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit c5d51f0 Oct 29, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-9u7v4h branch October 29, 2026 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tooling

Projects

None yet

1 participant