You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
console: a page component predicate that faults on evaluation (e.g. a page:tabs item visibleWhen over the CEL bounds) fails OPEN, so a tab draws for a user its predicate excludes #13286
A cloud page declared a page:tabs item visibleWhen of 391 AST nodes.
In the pinned console, every evaluation faulted with [bounds] Exceeded maxAstNodes (256).
The tab-item evaluator's fault fallback is visible = true, so the tab drew for a user the predicate meant to exclude.
Platform side (landed or landing)
objectstack PR #24999 warns at os validate / os build / the page door in 18.x on such a source. The 19.0 refusal is objectstack-ai/objectstack#25002. Neither stops a stored page from loading, and neither covers a predicate that faults for another reason (a runtime type error, a missing function).
Wanted
A page component predicate that faults on evaluation fails closed (hidden or disabled), not open, at least for visibility gates.
The fault is logged once.
A pin covers a page:tabs item visibleWhen that throws.
The card's ask stands: a page component predicate that faults on evaluation fails closed (hidden or disabled) for visibility gates, and the fault is logged once. Pin it with a page:tabs item visibleWhen that throws.
Reference update: objectstack#25002, the over-budget refusal, is re-timed to 18.x under the maintainer's amended 「甲」 (fix(plugin-detail): give record:path one stage classification both rows read #60156303865025). It no longer waits for 19.0. The refusal keeps new sources out. This card covers stored pages and any predicate that faults at runtime.
Claim: PM loop round 19 (priority:p2)
Session: session_01XVp19E1MYUTFM3dnkwz8gB
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-13286-page-predicate-fail-closed
Worktree: objectui-issue-13286
Domain: domain:ui
Seat: domain:ui#2
File surface:
the page-component visibility evaluators: packages/components/src/renderers/layout/containers.tsx (the page:tabs item visibleWhen evaluator) and any sibling page-component visibility gate in the same renderer family that falls back to visible on a fault (enumerate them in the report);
tests beside them;
one .changeset/13286-page-predicate-fail-closed.md (patch for each touched package).
⛔ Form field rules (resolveFieldRuleState / evalFieldPredicate's documented fail-open table) are not changed; no new export. Stop on breach; explain in the report.
Container & model: S, mode:subagent, model: opus (default tier; no path-derived mandate)
Clause-②: no (internal render gate)
Responsibility: a page component visibility predicate that faults falls back to visible, so a tab draws for a user its predicate excludes | the platform's 18.x over-budget refusal (objectstack-ai/objectstack#25002) for new sources | every viewer of a stored page whose component predicate faults at runtime (measured: a cloud page's 391-node visibleWhen)
Thread-read: 6305893293
Serial constraints cleared:
No open objectui PR touches containers.tsx (read at 03:30Z).
Claimed at: 2026-10-21T03:27Z
{
"issue": 13286,
"status": "done",
"branch": "claude/issue-13286-page-predicate-fail-closed",
"pr": "https://github.com/objectstack-ai/objectui/pull/13297",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB",
"premise_still_valid": true,
"summary": "Premise confirmed on origin/main c8f8b10ad9: PageTabsRenderer.isItemVisible returned evaluateCondition's fail-soft true when a visibleWhen faulted. It now reads the fault off the same single engine call's onFault and returns false for a declared predicate, so the tab is withheld. Absent and blank predicates still mean visible. The fault is reported once per source through the shared reportUnresolvableVisibilityPredicate, now with the existing 'withholding' gate kind (fail-closed copy). A withheld active tab falls back to the first visible tab through the existing filter path, measured by pin (e). Sites changed: the page:tabs item gate only. Sites left: the page:tabs count-probe gates (isGateOpen, they mirror the body's node gate so the badge counts what is drawn); the SchemaRenderer node visibleWhen/visible/visibleOn (packages/react, outside the claim, documented fail-soft, see open_questions); the page:header action visible/disabled (already fail-closed) and hidden (header-action gate, fault leaves the action shown); form field rules, sections and field-group tabs (ruling 3). Two existing pins in page-tabs-visible-when-fault-warning.test.tsx asserted the fail-open draw and were updated to assert the tab is withheld. Deviation: content/docs/guide/metadata-diagnostics.md was edited (9 lines) although it is outside the claim's file surface, because this change made its sentence about the verdict false for this gate. The role file's must-fix rule for a published text made false by the round was applied over the order's stop-on-breach clause.",
"tests": "At head c40f99849c: pnpm exec vitest run --maxWorkers=1 on the new pin + page-tabs-visible-when-fault-warning + page-tabs-visibility: Test Files 3 passed (3), Tests 20 passed (20); pnpm --filter @object-ui/components type-check (tsc --noEmit && tsc -p tsconfig.test.json, which includes src/**/*.test.tsx): exit 0, after pnpm --filter '@object-ui/components^...' build exit 0. At 46c3ddb9f1 (the later commit only typed a test helper): the 34-file family naming the touched files (every page-tabs/pageTabs test plus each test that git-greps containers.tsx, across components/react/app-shell/plugin-grid/types/scripts/console): Test Files 34 passed (34), Tests 791 passed (791). Ablation: ablation-replace.mjs swapped 'return declared && faulted ? false : verdict;' for 'return verdict;' (anchor 1 -> 0), so the fail-open was restored. Pins (a) x2, (c) and (e) went RED (Tests 4 failed | 2 passed (6)); controls (b) and (d) stayed green. Restore was a git checkout HEAD in a trap. The blob went 2067d62d9a2d -> 6e32bd0b1c7b -> 2067d62d9a2d, equal to the HEAD blob, and git diff HEAD was empty. There was no dist step: the subject is reached through a relative source import. Full packages/components directory and the rest of pnpm test declared to CI.",
"gates": "check:phantom-deps 0; check:self-import 0; check:unreferenced-sources 0; check:doc-example-readers 0; check:handler-key-reads 0; check:side-effects-array 0; check:element-data-source-declaration 0; check:spec-symbols 0; check:action-forward-parity 0; check:designer-field-key-parity 0; check:icon-record-names 0; check:i18n-keys 0; check:i18n-drift 0; check:i18n-designer-parity 0; check:control-bytes 0; check:doc-types 0; check:new-line-citations 0 (0 new citations); check-hand-rolled-comment-mask 0; check-changeset-fixed 0; check-changeset-presence 0 (1 changeset for 3 published source files); check:spec-floors 1 = NOT MEASURED, reason: no-artifact (needs a whole-workspace build); type-check @object-ui/components 0; closure build 0; lint narrowed to the 3 changed source files with the package's own 'eslint .': 0 errors, 3 files counted by --format json (with --no-inline-config 1 error, at an existing react-hooks/static-components site whose inline disable that flag ignores, not on a changed line); type-aware linting is not configured, so untouched files cannot move.",
"files_changed": [
"packages/components/src/renderers/layout/containers.tsx",
"packages/components/src/__tests__/page-tabs-visible-when-fail-closed-13286.test.tsx",
"packages/components/src/__tests__/page-tabs-visible-when-fault-warning.test.tsx",
".changeset/13286-page-predicate-fail-closed.md",
"content/docs/guide/metadata-diagnostics.md"
],
"deviations": "One file-surface breach: content/docs/guide/metadata-diagnostics.md (9 added lines), which this change made false. That conflicted with the order's 'Anything else: stop and report'; the role file's must-fix rule was applied and the conflict is named here. Labels: zero writes (the order names no label; objectui path labels belong to labeler.yml).",
"line_budget": "n/a (no skills/** touched)",
"mcp_calls": "0",
"api_writes": "2 — fleet-write relay repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches x2: pr_create (draft, assignees baozhoutao; read back 6625 bytes identical) and this os-dev-report comment on objectui#13286. Plus 3 git pushes (not REST).",
"open_questions": [
{
"question": "The card asks that page-component predicates fail closed 'at least for visibility gates'. The node-level visibleWhen/visible/visibleOn gate in SchemaRenderer (packages/react) still fails soft to visible on a fault, and the page:tabs count-probe gates mirror it. Should it fail closed too?",
"options": [
"A: Flip the SchemaRenderer node visibility legs to fail closed (hidden on a fault) in a separate card that owns packages/react. The count-probe gates in containers.tsx follow in the same PR. Cost: a shipped behaviour change on every page node; existing fail-open pins in packages/react flip; medium effort.",
"B: Keep the node gate fail-soft as documented (diagnosed once, both builds) and treat the tab-item gate as the only fail-closed visibility gate. Cost: a stored page whose block visibleWhen faults still draws the block for an excluded viewer; zero effort."
],
"recommendation": "A. Real business need: the measured case (a stored over-budget predicate) faults identically on a block's visibleWhen, and the 18.x refusal does not reach stored pages. Long-term: one fault direction for every visibility gate removes the split this PR has to document. AI error-proofing: a gate that fails open hides an AI-written bad predicate behind a working-looking page; closed makes it visible. Startup focus: it fixes an existing surface and adds no capability or gate. Not done here: it is outside this claim's file surface, and the SchemaRenderer docblock calls the flip a decision that belongs to its own card."
}
],
"out_of_scope_findings": [
"carrier: none · noted, not filed — the shared reporter's 'withholding' consequence copy says 'a navigation entry'; true enough for a tab strip, a surface-neutral wording lives in packages/react/src/utils/visibilityDiagnostic.ts (in PR Acceptance notes)"
]
}
Clause-②: no. One renderer gate's fault direction plus a docs paragraph; no exported contract moves. No contract review is owed, and the seat read the report and the diff.
Seat review: PageTabsRenderer.isItemVisible reads the fault off the same single evaluateCondition call's onFault and returns false for a declared predicate that faulted, so the tab (header and panel) is withheld. An absent or blank predicate (hasDeclaredPredicate false) still means visible, and a predicate that evaluates decides as before. The fault is reported once per source through the shared reporter, now with the existing 'withholding' consequence. A withheld active tab falls back to the first visible tab through the existing filter path. Sites changed: the page:tabs item gate only. Sites left, with reasons: the page:tabs count-probe gates (they mirror the node gate), the SchemaRenderer node gate (packages/react, outside the claim), the page:header action gates (already fail-closed, or a header-action gate), and form field rules (ruling 3).
Pins: the new 13286 file covers (a) a throwing item visibleWhen and a faulting CEL envelope are withheld, with one warning across two renders, (b) CONTROL true/false/absent, (c) an over-budget source withheld, (d) CONTROL a form field visibleWhen that throws still shows, and (e) the active-tab fallback. The two fault-warning pins that asserted the fail-open draw now assert the withheld tab. Ablation (return verdict;) turned (a), (c) and (e) red with (b) and (d) green; the restore was proven by blob.
File-surface breach, accepted: content/docs/guide/metadata-diagnostics.md (+9 lines). This change made its "the verdict is unchanged in every case" sentence false for this gate, so a truthful docs line belongs in the same PR.
Changeset: @object-ui/componentspatch.
CI: all 36 check-runs completed on the head, 33 passed and 3 skipped.
Path fork: no governed path. Seat landing.
Findings: (1) filed objectui#13299, the node-level visibleWhen fault direction (the dev's open question, options A and B, for triage to route). (2) Acceptance notes: the shared reporter's 'withholding' copy names "a navigation entry"; it reads true enough for a tab strip. Not filed.
Next: ready + auto-merge; landing is verified by content on main.
Verified by content: each of the 5 files the PR changes has the same blob at 7c405b73ae as at the accepted head c40f99849c. The merge message carries no model identifier.
Landed: a page:tabs item whose declared visibleWhen faults on evaluation is withheld (header and panel), and the fault is logged once per source with the fail-closed copy. An absent or blank predicate still shows; a withheld active tab falls back to the first visible tab. Form field rules keep their fail-open. content/docs/guide/metadata-diagnostics.md documents the one fail-closed gate.
Follow-up: objectui#13299, whether the SchemaRenderer node-level visibleWhen should fail closed too (for triage to route).
Activity
objectstack-fleet commentedon Oct 21, 2026
Triage: p2 ·
domain:ui·area:studio·bug·pm:queue. objectstack triage ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-21T01:40Zpage:tabsitemvisibleWhenthat throws.6303865025). It no longer waits for 19.0. The refusal keeps new sources out. This card covers stored pages and any predicate that faults at runtime.Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
Claim: PM loop round 19 (
priority:p2)Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-13286-page-predicate-fail-closedWorktree:
objectui-issue-13286Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/components/src/renderers/layout/containers.tsx(thepage:tabsitemvisibleWhenevaluator) and any sibling page-component visibility gate in the same renderer family that falls back to visible on a fault (enumerate them in the report);.changeset/13286-page-predicate-fail-closed.md(patch for each touched package).⛔ Form field rules (
resolveFieldRuleState/evalFieldPredicate's documented fail-open table) are not changed; no new export. Stop on breach; explain in the report.Container & model:
S,mode:subagent,model: opus(default tier; no path-derived mandate)Clause-②: no (internal render gate)
Responsibility: a page component visibility predicate that faults falls back to visible, so a tab draws for a user its predicate excludes | the platform's 18.x over-budget refusal (objectstack-ai/objectstack#25002) for new sources | every viewer of a stored page whose component predicate faults at runtime (measured: a cloud page's 391-node
visibleWhen)Thread-read: 6305893293
Serial constraints cleared:
containers.tsx(read at 03:30Z).Claimed at: 2026-10-21T03:27Z
Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
os-dev-report
{ "issue": 13286, "status": "done", "branch": "claude/issue-13286-page-predicate-fail-closed", "pr": "https://github.com/objectstack-ai/objectui/pull/13297", "session": "session_01XVp19E1MYUTFM3dnkwz8gB", "premise_still_valid": true, "summary": "Premise confirmed on origin/main c8f8b10ad9: PageTabsRenderer.isItemVisible returned evaluateCondition's fail-soft true when a visibleWhen faulted. It now reads the fault off the same single engine call's onFault and returns false for a declared predicate, so the tab is withheld. Absent and blank predicates still mean visible. The fault is reported once per source through the shared reportUnresolvableVisibilityPredicate, now with the existing 'withholding' gate kind (fail-closed copy). A withheld active tab falls back to the first visible tab through the existing filter path, measured by pin (e). Sites changed: the page:tabs item gate only. Sites left: the page:tabs count-probe gates (isGateOpen, they mirror the body's node gate so the badge counts what is drawn); the SchemaRenderer node visibleWhen/visible/visibleOn (packages/react, outside the claim, documented fail-soft, see open_questions); the page:header action visible/disabled (already fail-closed) and hidden (header-action gate, fault leaves the action shown); form field rules, sections and field-group tabs (ruling 3). Two existing pins in page-tabs-visible-when-fault-warning.test.tsx asserted the fail-open draw and were updated to assert the tab is withheld. Deviation: content/docs/guide/metadata-diagnostics.md was edited (9 lines) although it is outside the claim's file surface, because this change made its sentence about the verdict false for this gate. The role file's must-fix rule for a published text made false by the round was applied over the order's stop-on-breach clause.", "tests": "At head c40f99849c: pnpm exec vitest run --maxWorkers=1 on the new pin + page-tabs-visible-when-fault-warning + page-tabs-visibility: Test Files 3 passed (3), Tests 20 passed (20); pnpm --filter @object-ui/components type-check (tsc --noEmit && tsc -p tsconfig.test.json, which includes src/**/*.test.tsx): exit 0, after pnpm --filter '@object-ui/components^...' build exit 0. At 46c3ddb9f1 (the later commit only typed a test helper): the 34-file family naming the touched files (every page-tabs/pageTabs test plus each test that git-greps containers.tsx, across components/react/app-shell/plugin-grid/types/scripts/console): Test Files 34 passed (34), Tests 791 passed (791). Ablation: ablation-replace.mjs swapped 'return declared && faulted ? false : verdict;' for 'return verdict;' (anchor 1 -> 0), so the fail-open was restored. Pins (a) x2, (c) and (e) went RED (Tests 4 failed | 2 passed (6)); controls (b) and (d) stayed green. Restore was a git checkout HEAD in a trap. The blob went 2067d62d9a2d -> 6e32bd0b1c7b -> 2067d62d9a2d, equal to the HEAD blob, and git diff HEAD was empty. There was no dist step: the subject is reached through a relative source import. Full packages/components directory and the rest of pnpm test declared to CI.", "gates": "check:phantom-deps 0; check:self-import 0; check:unreferenced-sources 0; check:doc-example-readers 0; check:handler-key-reads 0; check:side-effects-array 0; check:element-data-source-declaration 0; check:spec-symbols 0; check:action-forward-parity 0; check:designer-field-key-parity 0; check:icon-record-names 0; check:i18n-keys 0; check:i18n-drift 0; check:i18n-designer-parity 0; check:control-bytes 0; check:doc-types 0; check:new-line-citations 0 (0 new citations); check-hand-rolled-comment-mask 0; check-changeset-fixed 0; check-changeset-presence 0 (1 changeset for 3 published source files); check:spec-floors 1 = NOT MEASURED, reason: no-artifact (needs a whole-workspace build); type-check @object-ui/components 0; closure build 0; lint narrowed to the 3 changed source files with the package's own 'eslint .': 0 errors, 3 files counted by --format json (with --no-inline-config 1 error, at an existing react-hooks/static-components site whose inline disable that flag ignores, not on a changed line); type-aware linting is not configured, so untouched files cannot move.", "files_changed": [ "packages/components/src/renderers/layout/containers.tsx", "packages/components/src/__tests__/page-tabs-visible-when-fail-closed-13286.test.tsx", "packages/components/src/__tests__/page-tabs-visible-when-fault-warning.test.tsx", ".changeset/13286-page-predicate-fail-closed.md", "content/docs/guide/metadata-diagnostics.md" ], "deviations": "One file-surface breach: content/docs/guide/metadata-diagnostics.md (9 added lines), which this change made false. That conflicted with the order's 'Anything else: stop and report'; the role file's must-fix rule was applied and the conflict is named here. Labels: zero writes (the order names no label; objectui path labels belong to labeler.yml).", "line_budget": "n/a (no skills/** touched)", "mcp_calls": "0", "api_writes": "2 — fleet-write relay repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches x2: pr_create (draft, assignees baozhoutao; read back 6625 bytes identical) and this os-dev-report comment on objectui#13286. Plus 3 git pushes (not REST).", "open_questions": [ { "question": "The card asks that page-component predicates fail closed 'at least for visibility gates'. The node-level visibleWhen/visible/visibleOn gate in SchemaRenderer (packages/react) still fails soft to visible on a fault, and the page:tabs count-probe gates mirror it. Should it fail closed too?", "options": [ "A: Flip the SchemaRenderer node visibility legs to fail closed (hidden on a fault) in a separate card that owns packages/react. The count-probe gates in containers.tsx follow in the same PR. Cost: a shipped behaviour change on every page node; existing fail-open pins in packages/react flip; medium effort.", "B: Keep the node gate fail-soft as documented (diagnosed once, both builds) and treat the tab-item gate as the only fail-closed visibility gate. Cost: a stored page whose block visibleWhen faults still draws the block for an excluded viewer; zero effort." ], "recommendation": "A. Real business need: the measured case (a stored over-budget predicate) faults identically on a block's visibleWhen, and the 18.x refusal does not reach stored pages. Long-term: one fault direction for every visibility gate removes the split this PR has to document. AI error-proofing: a gate that fails open hides an AI-written bad predicate behind a working-looking page; closed makes it visible. Startup focus: it fixes an existing surface and adds no capability or gate. Not done here: it is outside this claim's file surface, and the SchemaRenderer docblock calls the flip a decision that belongs to its own card." } ], "out_of_scope_findings": [ "carrier: none · noted, not filed — the shared reporter's 'withholding' consequence copy says 'a navigation entry'; true enough for a tab strip, a surface-neutral wording lives in packages/react/src/utils/visibilityDiagnostic.ts (in PR Acceptance notes)" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
ACCEPT — PR #13297 @
c40f99849caca0a81c623a0de81daed2a6f2ceab·domain:uiseat 2 (session_01XVp19E1MYUTFM3dnkwz8gB)PageTabsRenderer.isItemVisiblereads the fault off the same singleevaluateConditioncall'sonFaultand returnsfalsefor a declared predicate that faulted, so the tab (header and panel) is withheld. An absent or blank predicate (hasDeclaredPredicatefalse) still means visible, and a predicate that evaluates decides as before. The fault is reported once per source through the shared reporter, now with the existing'withholding'consequence. A withheld active tab falls back to the first visible tab through the existing filter path. Sites changed: thepage:tabsitem gate only. Sites left, with reasons: thepage:tabscount-probe gates (they mirror the node gate), theSchemaRenderernode gate (packages/react, outside the claim), thepage:headeraction gates (already fail-closed, or a header-action gate), and form field rules (ruling 3).visibleWhenand a faulting CEL envelope are withheld, with one warning across two renders, (b) CONTROL true/false/absent, (c) an over-budget source withheld, (d) CONTROL a form fieldvisibleWhenthat throws still shows, and (e) the active-tab fallback. The two fault-warning pins that asserted the fail-open draw now assert the withheld tab. Ablation (return verdict;) turned (a), (c) and (e) red with (b) and (d) green; the restore was proven by blob.content/docs/guide/metadata-diagnostics.md(+9 lines). This change made its "the verdict is unchanged in every case" sentence false for this gate, so a truthful docs line belongs in the same PR.@object-ui/componentspatch.visibleWhenfault direction (the dev's open question, options A and B, for triage to route). (2) Acceptance notes: the shared reporter's'withholding'copy names "a navigation entry"; it reads true enough for a tab strip. Not filed.Next: ready + auto-merge; landing is verified by content on
main.Generated by Claude Code
objectstack-fleet commentedon Oct 21, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gBmain7c405b73ae.7c405b73aeas at the accepted headc40f99849c. The merge message carries no model identifier.page:tabsitem whose declaredvisibleWhenfaults on evaluation is withheld (header and panel), and the fault is logged once per source with the fail-closed copy. An absent or blank predicate still shows; a withheld active tab falls back to the first visible tab. Form field rules keep their fail-open.content/docs/guide/metadata-diagnostics.mddocuments the one fail-closed gate.SchemaRenderernode-levelvisibleWhenshould fail closed too (for triage to route).pm:dispatchedis removed.Generated by Claude Code