Skip to content

Studio: the Validations tab holds the autosave on a served CEL verdict at the rule's own path, as the RLS arm does (#27482 layer 2 of 2) #27644

Closed

Description

@objectstack-fleet

triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

Sub-issue of #27482 (layer 2 of 2: the console). Filed by triage on the pm:retriage request 6501208028 from domain:ui#3. ⛔ Not a claim.

Blocked-by: #27643 (lint emits validations.N.condition), on main.

What is wrong

PR #27612 made a served CEL error at a CelPredicateField's own verdict path hold the Permission matrix autosave (draftVerdict.ts serverHoldAt / useServerHoldAt, through onLintChange). The Validations tab has no such hold:

  • DataPillar (studio-design/StudioDesignSurface.tsx) drops the served _diagnostics.
  • ConditionBuilder carries no verdictPath.

So a rule the engine refuses (the nullable-compare rule) still reads "Valid CEL" and autosaves (PUT 200) until Publish refuses it.

Expected

  • DataPillar keeps the draft read's _diagnostics.
  • ConditionBuilder and ObjectValidationsPanel carry the rule's verdictPath.
  • The served error at that path holds the save through the existing serverHoldAt while the judged text stands. Editing the text releases it.
  • Pinned like PermissionMatrixEditor.serverVerdictHold-27482.test.tsx.

Constraints

QA-source: #27438 · studio-authoring.validation-rules-authoring


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    on Oct 29, 2026
  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 错的必须被响亮拒绝并给处方 | studio-authoring.validation-rules-authoring | P③

    Triage: p2 · domain:ui · area:studio · layer 2 of #27482 · pm:blocked on #27643 · 2026-10-29T16:06Z

    ⛔ Not a claim. Filed by triage on #27482's retriage. It reuses #27612's serverHoldAt, with no second hold and no client copy of the rule.

    • bug, pm:blocked.

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

    Claim: PM loop round 1
    Session: session_01Utp6CbDAucNyQ4UtzCJKyh
    Account: os-bill
    Branch: claude/project-thread-aewbdy (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-27644
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (DataPillar only), ui/packages/app-shell/src/views/studio-design/ObjectValidationsPanel.tsx, ui/packages/app-shell/src/views/metadata-admin/inspectors/ConditionBuilder.tsx, ui/packages/app-shell/src/views/metadata-admin/{draftVerdict.ts,CelPredicateField.tsx} (reuse only), their tests, .changeset/*.md (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier on fec60e00: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the Validations tab keeps the draft read's served _diagnostics, carries each rule's verdictPath, and holds the autosave through #27612's serverHoldAt while the judged text stands; no second hold, no client copy of the publish rule
    Thread-read: 6501879879
    Premise: holds
    Serial constraints cleared: #27643 closed on main; draft PR #27988 (#27896) edits StudioDesignSurface.tsx in InterfacesPillar only (disjoint hunks from DataPillar), merge origin/main before the PR

    Dispatched 2026-10-30T06:44Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · os-bill · session_01Utp6CZjUkAAB57j1TQa8sE
    os-dev-report

    {
      "issue": 27644,
      "status": "done",
      "branch": "claude/project-thread-aewbdy",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28027",
      "session": "session_01Utp6CZjUkAAB57j1TQa8sE",
      "premise_still_valid": true,
      "summary": "DataPillar now keeps the object draft read's served _diagnostics and judged body, re-reads the draft after each save, and provides both (DraftVerdictContext / DraftVerdictBodyContext) to the Validations tab only. ObjectValidationsPanel passes each guard verdictPath validations.N.condition (or .when), N = index in the raw draft.validations list. ConditionBuilder forwards verdictPath to CelPredicateField in raw mode (the #27612 onLintChange hold) and, in row mode, which mounts no CEL editor, reads the same useServerHoldAt itself to show and count the served error. Measured: the stored nullable-compare guard opens in ROW mode, so the raw-only half would not have held the card's shape (ablation 2).",
      "tests": "head 479a7575: new ConditionBuilder.serverHold-27644 + DataPillar.validationVerdictHold-27644: 2 files / 9 tests passed; 169 related app-shell files (ObjectValidationsPanel*, DataPillar*, StudioDesignSurface*, ConditionBuilder*, CelPredicateField*, serverVerdict*): Test Files 169 passed, Tests 1234 passed (lock VERDICT command-exit 0); pnpm --filter @object-ui/app-shell type-check exit 0 (both tsc projects). Ablation 1 (DraftVerdictContext value objVerdict to undefined, ablation-replace anchor 1 to 0): DataPillar 5 failed / 1 passed (control), restored blob == HEAD, git diff HEAD empty. Ablation 2 (raw ? celErrors : held.length to 0): 4 failed / 5 passed (both row-builder cases + DataPillar a, e), restored blob == HEAD. Both ablations on src (relative imports in app-shell tests, no dist leg). Full packages/app-shell/ suite (~1950 files) NOT MEASURED locally: stopped after 1h+ when #27988 landed and main was merged again; declared to CI Test UI.",
      "gates": "local, head 479a7575: dispatch-gates --commands 34 derived (same list at 23227677, ae70f346, 479a7575); each recorded before any pipe: 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:undeclared-dep-imports :: exit 0; pnpm check:watch-hint-literal :: exit 0; pnpm check:where-matcher :: exit 0; pnpm check:type-check-debt :: exit 0. --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. Extra: pnpm check:new-line-citations 0 new (exit 0); eslint on 5 changed ui files 0 errors. CI check-runs on 28027: in_progress at report time.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no GitHub MCP tool used (one claude-code-remote subscribe_pr_activity, not GitHub MCP)",
      "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (relay pr_create to PR 28027), POST /repos/objectstack-ai/objectstack/issues/27644/comments (this report via post-stamped)",
      "line_budget": "465 insertions / 11 deletions vs origin/main 066ff51e: ConditionBuilder.tsx +30/-3, ObjectValidationsPanel.tsx +14, StudioDesignSurface.tsx +38/-7, two new test files +99 and +275, changeset +10",
      "files_changed": [
        ".changeset/27644-validation-rule-served-verdict-hold.md",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/ConditionBuilder.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/ConditionBuilder.serverHold-27644.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/ObjectValidationsPanel.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
        "ui/packages/app-shell/src/views/studio-design/DataPillar.validationVerdictHold-27644.test.tsx"
      ],
      "deviations": [
        "StudioDesignSurface.tsx: one name (DraftVerdictBodyContext) added to the shared draftVerdict import block, outside DataPillar; every other hunk is inside DataPillar.",
        "ConditionBuilder.tsx reads useServerHoldAt directly in row mode: the same #27612 helper, not a second hold; needed because a stored refused guard opens as a row with no CelPredicateField mounted.",
        "draftVerdict.ts and CelPredicateField.tsx not edited.",
        "PR body attribution link: used the session context's thread link (this card thread) rather than the dispatch's seat-thread link; the session context requires its block verbatim.",
        "PR body order: Fixes and the two declaration lines precede the maintainer summary, per os-dev.md (the order wins over the dispatch).",
        "Harness after-PR assignee/review step skipped, as the dispatch orders; the PR has no assignee and no reviewer.",
        "Subscribed this thread to PR 28027 activity so CI reds reach it (dispatch: drive CI to green).",
        "Narrowed: full packages/app-shell/ suite not completed locally (declared to CI Test UI).",
        "Not reproduced live: the pre-fix PUT 200 was not reproduced on a running stack; ablations are the before-state evidence."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted in PR Acceptance notes, not filed: serverHoldAt compares the judged value at the path with the on-screen text, so a guard stored as an expression envelope (dialect + source) is never held; no producer of envelope-form validation guards found (none in examples/)"
      ]
    }
  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

    ACCEPT → PR #28027 comment 6519399127 · 2026-10-30T09:23Z


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:studioChanging a running app without code — authoring, publish, docs and the portalbugSomething isn't workingdomain:uipm:dispatchedpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions