Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

conditionalFormatting on the object-view node has a SECOND read on the author-reachable kanban path — the #5097 host-surface exemption is narrower for this one key #5248

Closed

Description

@os-support-ai

Found while implementing the ruled exemption on #5097. Filed unassigned, not claiming. No code change was made for it — #5097's ruling preserves the status quo either way (nothing declared, nothing removed).

What was measured

Measured on main at e03dfa5ea (and identical at #5097's base 9fbb9b52f).

The #5097 ruling exempts 27 keys read off the object-view node as HOST-COMPOSITION surface. Its stated basis is measured reachability: the renderListView delegation branch in packages/plugin-view/src/ObjectView.tsx is entered only when a host supplies the prop, and the registered renderer ObjectViewRenderer (packages/plugin-view/src/index.tsx:58) does not, so the schema-registration path documented to authors cannot reach those keys.

That basis holds for 26 of the 27. It is narrower for conditionalFormatting, which has a second read site outside the delegation branch:

  • packages/plugin-view/src/ObjectView.tsx:1094 — inside the host-only renderListView branch, with its 30 neighbours.
  • packages/plugin-view/src/ObjectView.tsx:768 — inside generateViewSchema's kanban branch, as the last fallback of kanbanConditionalFormatting (kanbanCfg.conditionalFormatting ?? activeView?.conditionalFormatting ?? (schema as any).conditionalFormatting), forwarded to the emitted object-kanban node.

The kanban branch is on the author-reachable path: renderContent delegates to renderListView only when the prop is present; otherwise, for a non-grid view type, it calls generateViewSchema(currentViewType) and hands the result to SchemaRenderer. That is exactly what ObjectViewRenderer does. So an author who registers an object-view node with a kanban view type and a top-level conditionalFormatting rule does get an effect today.

conditionalFormatting is not a declared member of ObjectViewSchema (derived from packages/types/src/objectql.ts + BaseSchema: 42 declared members, conditionalFormatting is not among them), and it is not in the object-view registry inputs (packages/plugin-view/src/index.tsx:71-86), so nothing on the authoring surface offers it and nothing rejects it — BaseSchema's index signature means tsc is silent too.

The question

One key is honoured on the authored path while being recorded as host surface. Three ways out, and it shapes the public contract, so it wants a ruling rather than a guess:

  1. Declare conditionalFormatting on ObjectViewSchema (and add it to the object-view registry inputs) — it does something on the author's path, so the "declaring it would promise a surface that does nothing" argument does not apply to it.
  2. Keep it exempt and narrow the kanban read — drop the (schema as any).conditionalFormatting fallback at :768 so the key is genuinely host-only, and let kanban conditional formatting be authored where it is declared. This changes behaviour for any document relying on the top-level rule, so it needs the usual liveness check first.
  3. Keep both as they are, and record the asymmetry as the exemption's known edge (what ObjectView 用 (schema as any) 从 object-view 节点读 31 个键转发给 renderListView,其中 27 个不是 ObjectViewSchema 声明成员 —— #5091 在 ObjectView 上的同形实例 #5097's comment block does today).

Where it is recorded now

The exemption block added by #5097 names this asymmetry in full and points here, so a later sweep meets a statement rather than a flat claim. See packages/plugin-view/src/ObjectView.tsx (OBJECT_VIEW_HOST_COMPOSITION_KEYS), section "The one asymmetry".

Related

Activity

  1. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    CollaboratorAuthor

    Triage: escalating to the decision inbox — one key (conditionalFormatting) is honoured on the author-reachable kanban path while recorded as host-only surface; all three ways out shape the public contract, and the #5097 ruling is the neighbouring decision this must stay consistent with. Typed Task.

    Four-prism:

    Recommendation: 2, gated on the liveness check coming back empty — narrow the kanban read so the key is genuinely host-only. If liveness finds real authored usage, switch to 1 (declare it: it demonstrably does something for authors, so the "promises a dead surface" objection does not apply).


    Generated by Claude Code

  2. os-support-ai commented on Aug 19, 2026

    @os-support-ai
    CollaboratorAuthor

    Claim: PM loop round 17
    Session: session_01RV6yuVCxymHYE16PL9vQkE
    Branch: claude/issue-5248-conditional-formatting-kanban-read
    Worktree: objectui-issue-5248
    Domain: repo:objectui (execution seat)
    File surface: packages/plugin-view/src/ObjectView.tsx (the (schema as any).conditionalFormatting fallback around :768 and the OBJECT_VIEW_HOST_COMPOSITION_KEYS note), plus tests under packages/plugin-view. If the liveness check fires the Option 1 branch, the surface additionally covers ObjectViewSchema in packages/types and the object-view registry inputs — declare that in your report the moment you know. ⛔ OUT: packages/cli/** (#5127), packages/plugin-grid/src/ObjectGrid.tsx (#5148), console top bar + packages/app-shell/** (#5287), packages/components/** (#5125) — all in flight this round.
    Container & model: S–M, mode:subagent, model: opus — the edit is small either way; the liveness check decides which edit, and it is the judgement.
    Clause-②: branch-dependent, and you must state which applies in the PR. Option 2 narrows — a key stops being honoured on the author path, restoring declared = enforced; that is no. Option 1 declares a new authorable key, which widens the accepted set; that is yes. Both branches are pre-ruled, so this does not change where the card goes — but it changes what the PR body must say.
    Serial constraints cleared: packages/plugin-view/src/ObjectView.tsx is free — I checked the two cards that held it. #5102 is closed, via PR5274 (merged 2026-08-18 23:45Z); #5097's PR5252 (the 27-key host-surface exemption you are amending) is merged. Rebase on current main and re-read :768 — the line will have moved.

    Zone 1 — RULED, and both branches are pre-ruled. Do not escalate.

    Maintainer, 2026-08-19, verbatim 「全部接受」, recorded on this card:

    Ruling: Option 2, gated on the liveness check; if liveness finds real usage, Option 1

    • First step of the dispatched work is the liveness check: does any authored document rely on a top-level conditionalFormatting reaching the kanban branch via the (schema as any).conditionalFormatting fallback at ObjectView.tsx:768?
    • Liveness empty (expected) → Option 2: drop that fallback read, making the key genuinely host-only, so the ObjectView 用 (schema as any) 从 object-view 节点读 31 个键转发给 renderListView,其中 27 个不是 ObjectViewSchema 声明成员 —— #5091 在 ObjectView 上的同形实例 #5097 exemption's stated basis ("reachable only via the host-supplied renderListView delegation") becomes true for all 27 keys. Update the OBJECT_VIEW_HOST_COMPOSITION_KEYS "one asymmetry" note to record the resolution.
    • Liveness finds real authored usage → Option 1: declare conditionalFormatting on ObjectViewSchema and add it to the object-view registry inputs (it demonstrably does something on the author path, so it is not a dead-surface promise).
    • Either way, report which branch fired and the evidence. Option 3 (record the asymmetry and keep both) is rejected — a ruled exemption with a known counter-example decays.

    ⛔ Do not implement Option 3. "Record the asymmetry and keep both reads" is the comfortable middle and it is explicitly rejected: it leaves a ruled exemption standing next to a known counter-example, which is how the exemption stops meaning anything.

    Zone 2 — the liveness check IS the first deliverable, and the ruling's expectation is not evidence

    The ruling says "(expected)" about the empty result. That is a prediction, not a reading. Treat it as something to falsify:

    Tests

    Option 2 branch:

    • A top-level conditionalFormatting on an object-view node no longer reaches the kanban branch. This is the change; it must fail before your edit.
    • The host-supplied renderListView delegation path still carries it. ⛔ Do not narrow that too — the key stays host surface, it just stops being author-reachable.
    • The other 26 keys in OBJECT_VIEW_HOST_COMPOSITION_KEYS are untouched.

    Option 1 branch:

    Reverse-verify: restore the file, keep the tests, predict which go red before running, report predicted vs observed. State per leg whether a build artifact sits between your edit and the thing under test (plugin-view consumes @object-ui/core and @object-ui/types through dist).

    Gates

    Derive from your diff (scripts/pm/dispatch-gates.mjs is objectstack-only). Run vitest from the repo root — the reason is not that a package-scoped run is refused (17 packages own a standalone vitest.config.ts, plugin-view among them, and are not refused) but that a package-scoped run uses a different config than CI does, so its green is not CI's green. Changeset required (@object-ui/plugin-view, plus @object-ui/types on the Option 1 branch); patch or minor, ⛔ never major.

    ⛔ Do not background a long check and end your turn. ⛔ No exhaustive paginated API sweeps.

    Deliver

    Draft PR against main, first body line Fixes #5248. State which branch fired in the first paragraph of the PR body, with the evidence. Push the branch early. Return the JSON report and post it as a comment whose first line is the literal <!-- os-dev-report --> marker.


    Generated by Claude Code

  3. os-support-ai commented on Aug 19, 2026

    @os-support-ai
    CollaboratorAuthor
    {
      "issue": 5248,
      "status": "done",
      "branch": "claude/issue-5248-conditional-formatting-kanban-read",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5336",
      "premise_still_valid": true,
      "summary": "OPTION 2 FIRED. The liveness check came back empty, so the `(schema as any).conditionalFormatting` fallback was dropped from `generateViewSchema`'s kanban branch (packages/plugin-view/src/ObjectView.tsx) — the key is now genuinely host-only and the #5097 exemption's stated basis holds for all 27 keys. Evidence, each zero paired with a counter-probe through the same method on the same corpus: objectui content/docs+skills+examples+apps has `conditionalFormatting` in exactly 2 files (plugin-grid.mdx, on `object-grid`'s DECLARED key; skills/objectui/guides/schema-expressions.md, on a `list-view` node) — controls `object-view` → 5 files, `groupByField` → 1 file; objectstack authors no `object-view` node at all (2 prose mentions repo-wide) against controls `object-form` → 54 files and `object-grid` → 19 files, and its only real authored `conditionalFormatting` (examples/app-showcase/src/ui/views/field-zoo.view.ts:61) is a VIEW-level rule on a grid view, i.e. the `activeView` link this PR keeps. Structural cross-check: no file in either repo carries both an object-view NODE and the key — the 7 files carrying both mentions are declarations, the code under change, its pin test, app-shell's host (which builds its object-view node WITHOUT the key and reads the key into the delegated list-view node), and prose. The `OBJECT_VIEW_HOST_COMPOSITION_KEYS` 'one asymmetry' note is rewritten as RESOLVED with that evidence; the host `renderListView` delegation is NOT narrowed and its forwarding is pinned.",
      "tests": "All measured at HEAD 6325ef257 (clean tree), vitest run from the repo ROOT (CI's config). `pnpm exec vitest run packages/plugin-view/` → Test Files 17 passed (17), Tests 180 passed (180). `pnpm exec turbo run type-check --filter=@object-ui/plugin-view --concurrency=2` → Tasks: 16 successful, 16 total (package-scoped `pnpm type-check` alone fails with TS2307 'Cannot find module @object-ui/*' — unbuilt workspace dists, pre-existing on untouched files, not a regression; turbo builds the closure first). eslint on the 3 changed source files → 0 errors, 65 warnings, all `no-explicit-any` of the same kind the neighbouring mapFlatten test already carries. `pnpm run check:control-bytes` → OK (4726 tracked text files); `check-changeset-presence.mjs` → OK (1 changeset); `check-changeset-no-major.mjs` → OK. REVERSE VERIFICATION, predicted before running: restore ObjectView.tsx to origin/main, keep both test files → predicted 4 red / 13 green, naming them (new file: 'a TOP-LEVEL rule … does not reach the kanban node', 'a top-level key does not fill in for a view that declares none'; pin: 'NO exempt key is read outside the fence', '`conditionalFormatting` in particular is no longer read outside the fence'). OBSERVED: exactly those four — `Tests 4 failed | 13 passed (17)` — no surprises, no direction reversal. Restoration leg: `Test Files 2 passed (2) / Tests 17 passed (17)`. BUILD ARTIFACT PER LEG: none on either leg, and no rebuild was owed — the repo-root vitest config aliases every `@object-ui/*` specifier straight at `packages/*/src` (vitest.config.mts:244-285, incl. core/types/react), no package in the worktree had a `dist/` at test time, the edited file is in the package under test and imported relatively, and the pin test reads ObjectView.tsx from disk at import. The red leg landing exactly on the predicted four is itself the proof the mutation reached the tests rather than a stale artefact.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Draft PR: #5336 — first body line Fixes #5248, first paragraph states the branch that fired. Changeset: @object-ui/plugin-view minor. Reported at draft-PR time per the 2026-08-10 ruling; CI convergence is the PM's to read.


    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

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions