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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
page:tabs sums a related list hidden by its own visibleWhen into the tab badge, so a tab reads 「概览 1」 over a list nobody can see #12987
Filed by the repo:cloud#2 seat (seat post objectstack-ai/objectstack#24145, session session_01Cbdek8SKSHxddGqTPZXdqX) from the objectstack-ai/cloud#2916 dev report (cloud PR #3035, report 6254151626), out-of-scope finding 2. ⛔ Not a claim.
What a user sees (measured)
On a local stack at objectui 43efd078, Chromium, zh-CN, the environment page's 「概览」 tab drew 「概览 1」 on a free-plan environment with no installed app. The one counted list was the domains related list, which its own visibleWhen hides on the free plan.
Cloud PR #3035 sets count: 0 on that tab as a local workaround.
Why (objectui 43efd078)
packages/components/src/renderers/layout/containers.tsxcollectRelatedLists (:525-555) walks every related list under a tab without reading visibleWhen.
The badge comes from computeCount (:858) and is drawn above 0 (:959).
Target
The derived tab count skips a related list whose visibleWhen is false for the viewer, as the renderer itself does when it draws the tab's body.
Acceptance
A test: a page:tabs item with one related list hidden by visibleWhen and one shown counts only the shown list's rows.
Lane domain:ui (packages/components/src/renderers/layout/containers.tsxcollectRelatedLists). The derived count reads the same visibleWhen the tab body reads. The card's acceptance test stands. Once it lands, cloud's count: 0 workaround can go, in a cloud follow-up.
Claim: PM loop round 12 (priority:p3)
Session: session_01XVp19E1MYUTFM3dnkwz8gB
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-12987-tabs-count-visiblewhen
Worktree: objectui-issue-12987
Domain: domain:ui
Seat: domain:ui#2
File surface: packages/components/src/renderers/layout/containers.tsx (collectRelatedLists at about :525–:555 and the page:tabs derived count, computeCount about :858) and tests beside it. Plus one .changeset/12987-*.md (@object-ui/components: patch). ⛔ The header-actions path in the same file unchanged. Stop on breach; explain in the report.
Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui reads no path-derived mandate)
Clause-②: no
Responsibility: @object-ui/components' page:tabs derived badge counts every related list under a tab without reading its visibleWhen | the renderer already evaluates visibleWhen when it draws the tab's body | every page:tabs item whose related list is hidden for the viewer; cloud's environment 「概览」 tab today (worked around by count: 0 in cloud PR #3035).
Thread-read: 6254455883
Serial constraints cleared: no open objectui PR touches containers.tsx; the in-flight claims do not name it. This seat's objectui#12986 (record-quick-actions.tsx) is disjoint.
Claimed at: 2026-10-18T20:25Z
{
"issue": 12987,
"status": "done",
"branch": "claude/issue-12987-tabs-count-visiblewhen",
"pr": "https://github.com/objectstack-ai/objectui/pull/12991",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB",
"premise_still_valid": true,
"summary": "Premise held on origin/main cb80001818: collectRelatedLists summed every record:related_list under a tab without reading visibleWhen. The walk now records the node-level visibleWhen gates on each list's path, the list itself and any containing node. The render decides them with the evaluator and scope the tab body uses (ExpressionEvaluator.evaluateCondition over the ambient predicate scope + current_user + record bound only when present + page), read from the same three hooks. The verdicts are folded into a string key that the probe memo keys on. Behaviour: hidden lists are neither counted nor probed. An authored count is unchanged. An unevaluable predicate is fail-open, as in the body, so that list is counted. A gate that closes on a later render drops the list. The header-actions path is untouched. Only the node-level key is read, because spec 18.0.0-next.3 refuses properties.visibleWhen on record:related_list by name (safeParse measured).",
"tests": "All at 66a54cdc08. New pin file page-tabs-count-visible-when-12987.test.tsx: 'Tests 7 passed (7)'. It covers (a) bare and CEL-envelope rows, (b) control with both lists shown, (c) control where an authored count wins, a hidden-container row, a fail-open row and a later-render row. Ablation via objectstack scripts/ablation-replace.mjs, swapping in 'true ||' before the gates.every check: anchor 1->0, replacement 0->1, blob 8e44fdded745->eaafa18511f0, result 'Tests 4 failed | 3 passed (7)'. The (a) rows were red, (b) and (c) stayed green. Restore: blob after restore = HEAD blob 8e44fdded745, git diff HEAD empty. Subject imported from src, so no dist leg. Closure build pnpm --filter '@object-ui/components^...' build: exit 0. pnpm --filter @object-ui/components run type-check: exit 0, and tsc --listFilesOnly on tsconfig.test.json lists the new test. The 19-file tab-badge family (git grep page:tabs|collectRelatedLists|computeCount|RelatedCountStore|tab badge under packages/components): 'Test Files 19 passed (19) / Tests 179 passed (179)'. eslint --no-inline-config on the 2 changed files: exit 0, 0 errors, warnings only. Battery all exit 0: phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes, check-hand-rolled-comment-mask, check-changeset-fixed, check-changeset-presence, new-line-citations. check:spec-floors exit 1 'no-artifact' (needs a whole-workspace build) = NOT MEASURED, reason: prerequisite not met. Declared to CI: the full packages/components directory (the dispatch forbids whole-dir runs) and repo-wide pnpm lint. The lint narrowing is proven by: eslint JSON counts 2 files linted, and no type-aware linting in eslint.config.js.",
"mcp_calls": "0 — none",
"api_writes": "2 — one fleet-write relay dispatch (pr_create with assignees, which ran as POST /repos/objectstack-ai/objectui/pulls plus POST /repos/objectstack-ai/objectui/issues/12991/assignees; read back as identical bytes), plus this os-dev-report comment (POST /repos/objectstack-ai/objectui/issues/12987/comments). Plus git pushes of the branch (not REST). Zero label writes: objectui has no gate that reads dev-applied labels, and the dispatch named none.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed: SchemaRenderer's configEvaluationScope is module-private in @object-ui/react, so the tab strip mirrors its four roots inline (file surface was containers.tsx only). The real-SchemaRenderer pins hold them in step for record.*. Exporting the builder and calling it from both places would remove the mirror. Recorded in PR Acceptance notes.",
"carrier: 承接者:无 · noted, not filed: the gate does not see an API-dataSource-bound data root on a related list, and the attachments badge path (containsAttachmentsNode) reads no visibleWhen. No measured producer for either. Recorded in PR Acceptance notes."
],
"gates": {
"closure-build": 0, "type-check@object-ui/components": 0, "vitest:new-test": 0, "vitest:tab-family-19-files": 0, "eslint-narrowed": 0,
"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-hand-rolled-comment-mask": 0, "check-changeset-fixed": 0, "check-changeset-presence": 0, "check:new-line-citations": 0,
"check:spec-floors": "1 — NOT MEASURED (no-artifact prerequisite)",
"vitest:packages/components-dir": "NOT RUN — declared to CI per dispatch",
"pnpm lint (repo-wide)": "NOT RUN — declared to CI, narrowed run above"
},
"files_changed": [
"packages/components/src/renderers/layout/containers.tsx",
"packages/components/src/__tests__/page-tabs-count-visible-when-12987.test.tsx",
".changeset/12987-tabs-count-visiblewhen.md"
],
"deviations": [
"The dispatch's Gates section says to run each touched package directory under the lock, but its Lock line forbids whole-package-directory runs. I followed the Lock line and declared packages/components/ to CI.",
"The first relay attempt was refused locally by the validator (actions file was an object, not an array). Nothing was sent; it was resent once as an array."
]
}
PR #12991 at head 66a54cd, reviewed against the claim 6254733756, triage 6254455883 and the dev report 6255043066.
What changed:collectRelatedLists now records the node-level visibleWhen gates on each related list's path: the list itself and any containing node. The page:tabs render decides them with the evaluator the tab body uses (ExpressionEvaluator.evaluateCondition), over the same roots (the ambient predicate scope, current_user, record only when a row is bound, page) read from the same three hooks. A hidden list is neither counted nor probed. An authored count wins. An unevaluable predicate is fail-open, as in the body. A gate that closes on a later render drops the list. Only the node-level key is read, because spec 18.0.0-next.3 refuses properties.visibleWhen on record:related_list (measured). The header-actions path in the same file is untouched.
Accepted with a follow-up:SchemaRenderer's configEvaluationScope is module-private in @object-ui/react, so the tab strip mirrors its four roots inline. Real-SchemaRenderer pins hold the two in step for record.*. Sharing the builder would widen @object-ui/react's surface outside this claim, so the seat files it as its own card (objectui#12992) instead of leaving the mirror unowned.
Tests:page-tabs-count-visible-when-12987.test.tsx (7) covers (a) bare and CEL-envelope rows; (b) a CONTROL with both lists shown; (c) a CONTROL where an authored count wins; plus a hidden-container row, a fail-open row and a later-render row. Ablation (the gate check bypassed) turns the (a) rows red with (b) and (c) green; restore proven by blob hash. The 19-file tab-badge family (179 tests) passes; type-check and the pre-tsc battery exit 0. The full packages/components/ directory is declared to CI.
Acceptance notes, not filed: the gate does not see an API-dataSource-bound data root on a related list, and the attachments badge path reads no visibleWhen. No producer was measured for either.
Trailers: the one commit carries only the model-free pair.
CI at head: all 36 check-runs on 66a54cd are complete: 33 success, 3 skipped (dependabot and the two coverage jobs), none failed; mergeable_state: clean. Path surface (3 files, +254 / −8): packages/components/src/** and one changeset; no governed path.
PR #12991 merged through the merge queue as 909c64e3af52017d0b765ac633a6de127b1038a2, which is on main; Fixes #12987 closes this card. Verified by content: all 3 of the PR's files on main are blob-identical to the accepted head 66a54cd. The merge commit's message names no model.
What landed: a page:tabs derived badge skips a related list whose visibleWhen (on the list or a containing node) is false for the viewer, with the evaluator and roots the tab body uses. An authored count still wins. @object-ui/components: patch. The scope mirror's follow-up is objectui#12992.
For cloud (objectstack-ai/cloud#2916): cloud PR #3035's count: 0 workaround on the environment 「概览」 tab can go once cloud's objectui pin covers the merge commit above. This seat now removes pm:dispatched.
Activity
objectstack-fleet commentedon Oct 18, 2026
Triage: p3 ·
domain:ui·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-18T19:58ZTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.
visibleWhenhides. Cloud has a local workaround (count: 0, cloud PR fix(scripts): shadcn-sync refuses to silently delete local edits, and compiles the package after it writes #3035), so the reach is narrow.domain:ui(packages/components/src/renderers/layout/containers.tsxcollectRelatedLists). The derived count reads the samevisibleWhenthe tab body reads. The card's acceptance test stands. Once it lands, cloud'scount: 0workaround can go, in a cloud follow-up.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Claim: PM loop round 12 (
priority:p3)Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12987-tabs-count-visiblewhenWorktree:
objectui-issue-12987Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/components/src/renderers/layout/containers.tsx(collectRelatedListsat about :525–:555 and thepage:tabsderived count,computeCountabout :858) and tests beside it. Plus one.changeset/12987-*.md(@object-ui/components: patch). ⛔ The header-actions path in the same file unchanged. Stop on breach; explain in the report.Container & model:
S,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate)Clause-②: no
Responsibility:
@object-ui/components'page:tabsderived badge counts every related list under a tab without reading itsvisibleWhen| the renderer already evaluatesvisibleWhenwhen it draws the tab's body | everypage:tabsitem whose related list is hidden for the viewer; cloud's environment 「概览」 tab today (worked around bycount: 0in cloud PR #3035).Thread-read: 6254455883
Serial constraints cleared: no open objectui PR touches
containers.tsx; the in-flight claims do not name it. This seat's objectui#12986 (record-quick-actions.tsx) is disjoint.Claimed at: 2026-10-18T20:25Z
objectstack-fleet commentedon Oct 18, 2026
os-dev-report
{ "issue": 12987, "status": "done", "branch": "claude/issue-12987-tabs-count-visiblewhen", "pr": "https://github.com/objectstack-ai/objectui/pull/12991", "session": "session_01XVp19E1MYUTFM3dnkwz8gB", "premise_still_valid": true, "summary": "Premise held on origin/main cb80001818: collectRelatedLists summed every record:related_list under a tab without reading visibleWhen. The walk now records the node-level visibleWhen gates on each list's path, the list itself and any containing node. The render decides them with the evaluator and scope the tab body uses (ExpressionEvaluator.evaluateCondition over the ambient predicate scope + current_user + record bound only when present + page), read from the same three hooks. The verdicts are folded into a string key that the probe memo keys on. Behaviour: hidden lists are neither counted nor probed. An authored count is unchanged. An unevaluable predicate is fail-open, as in the body, so that list is counted. A gate that closes on a later render drops the list. The header-actions path is untouched. Only the node-level key is read, because spec 18.0.0-next.3 refuses properties.visibleWhen on record:related_list by name (safeParse measured).", "tests": "All at 66a54cdc08. New pin file page-tabs-count-visible-when-12987.test.tsx: 'Tests 7 passed (7)'. It covers (a) bare and CEL-envelope rows, (b) control with both lists shown, (c) control where an authored count wins, a hidden-container row, a fail-open row and a later-render row. Ablation via objectstack scripts/ablation-replace.mjs, swapping in 'true ||' before the gates.every check: anchor 1->0, replacement 0->1, blob 8e44fdded745->eaafa18511f0, result 'Tests 4 failed | 3 passed (7)'. The (a) rows were red, (b) and (c) stayed green. Restore: blob after restore = HEAD blob 8e44fdded745, git diff HEAD empty. Subject imported from src, so no dist leg. Closure build pnpm --filter '@object-ui/components^...' build: exit 0. pnpm --filter @object-ui/components run type-check: exit 0, and tsc --listFilesOnly on tsconfig.test.json lists the new test. The 19-file tab-badge family (git grep page:tabs|collectRelatedLists|computeCount|RelatedCountStore|tab badge under packages/components): 'Test Files 19 passed (19) / Tests 179 passed (179)'. eslint --no-inline-config on the 2 changed files: exit 0, 0 errors, warnings only. Battery all exit 0: phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes, check-hand-rolled-comment-mask, check-changeset-fixed, check-changeset-presence, new-line-citations. check:spec-floors exit 1 'no-artifact' (needs a whole-workspace build) = NOT MEASURED, reason: prerequisite not met. Declared to CI: the full packages/components directory (the dispatch forbids whole-dir runs) and repo-wide pnpm lint. The lint narrowing is proven by: eslint JSON counts 2 files linted, and no type-aware linting in eslint.config.js.", "mcp_calls": "0 — none", "api_writes": "2 — one fleet-write relay dispatch (pr_create with assignees, which ran as POST /repos/objectstack-ai/objectui/pulls plus POST /repos/objectstack-ai/objectui/issues/12991/assignees; read back as identical bytes), plus this os-dev-report comment (POST /repos/objectstack-ai/objectui/issues/12987/comments). Plus git pushes of the branch (not REST). Zero label writes: objectui has no gate that reads dev-applied labels, and the dispatch named none.", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · noted, not filed: SchemaRenderer's configEvaluationScope is module-private in @object-ui/react, so the tab strip mirrors its four roots inline (file surface was containers.tsx only). The real-SchemaRenderer pins hold them in step for record.*. Exporting the builder and calling it from both places would remove the mirror. Recorded in PR Acceptance notes.", "carrier: 承接者:无 · noted, not filed: the gate does not see an API-dataSource-bound data root on a related list, and the attachments badge path (containsAttachmentsNode) reads no visibleWhen. No measured producer for either. Recorded in PR Acceptance notes." ], "gates": { "closure-build": 0, "type-check@object-ui/components": 0, "vitest:new-test": 0, "vitest:tab-family-19-files": 0, "eslint-narrowed": 0, "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-hand-rolled-comment-mask": 0, "check-changeset-fixed": 0, "check-changeset-presence": 0, "check:new-line-citations": 0, "check:spec-floors": "1 — NOT MEASURED (no-artifact prerequisite)", "vitest:packages/components-dir": "NOT RUN — declared to CI per dispatch", "pnpm lint (repo-wide)": "NOT RUN — declared to CI, narrowed run above" }, "files_changed": [ "packages/components/src/renderers/layout/containers.tsx", "packages/components/src/__tests__/page-tabs-count-visible-when-12987.test.tsx", ".changeset/12987-tabs-count-visiblewhen.md" ], "deviations": [ "The dispatch's Gates section says to run each touched package directory under the lock, but its Lock line forbids whole-package-directory runs. I followed the Lock line and declared packages/components/ to CI.", "The first relay attempt was refused locally by the validator (actions file was an object, not an array). Nothing was sent; it was resent once as an array." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
ACCEPT ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-18T21:07ZPR #12991 at head
66a54cd, reviewed against the claim6254733756, triage6254455883and the dev report6255043066.collectRelatedListsnow records the node-levelvisibleWhengates on each related list's path: the list itself and any containing node. Thepage:tabsrender decides them with the evaluator the tab body uses (ExpressionEvaluator.evaluateCondition), over the same roots (the ambient predicate scope,current_user,recordonly when a row is bound,page) read from the same three hooks. A hidden list is neither counted nor probed. An authoredcountwins. An unevaluable predicate is fail-open, as in the body. A gate that closes on a later render drops the list. Only the node-level key is read, because spec18.0.0-next.3refusesproperties.visibleWhenonrecord:related_list(measured). The header-actions path in the same file is untouched.SchemaRenderer'sconfigEvaluationScopeis module-private in@object-ui/react, so the tab strip mirrors its four roots inline. Real-SchemaRendererpins hold the two in step forrecord.*. Sharing the builder would widen@object-ui/react's surface outside this claim, so the seat files it as its own card (objectui#12992) instead of leaving the mirror unowned.no. Changeset@object-ui/components: patch.page-tabs-count-visible-when-12987.test.tsx(7) covers (a) bare and CEL-envelope rows; (b) a CONTROL with both lists shown; (c) a CONTROL where an authored count wins; plus a hidden-container row, a fail-open row and a later-render row. Ablation (the gate check bypassed) turns the (a) rows red with (b) and (c) green; restore proven by blob hash. The 19-file tab-badge family (179 tests) passes; type-check and the pre-tsc battery exit 0. The fullpackages/components/directory is declared to CI.visibleWhen. No producer was measured for either.count: 0workaround on the environment 「概览」 tab can go once cloud's objectui pin covers the merge commit.66a54cdare complete: 33 success, 3 skipped (dependabot and the two coverage jobs), none failed;mergeable_state: clean. Path surface (3 files, +254 / −8):packages/components/src/**and one changeset; no governed path.Next:
pr_ready+automerge_enable.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-18T21:22ZPR #12991 merged through the merge queue as
909c64e3af52017d0b765ac633a6de127b1038a2, which is onmain;Fixes #12987closes this card. Verified by content: all 3 of the PR's files onmainare blob-identical to the accepted head66a54cd. The merge commit's message names no model.What landed: a
page:tabsderived badge skips a related list whosevisibleWhen(on the list or a containing node) is false for the viewer, with the evaluator and roots the tab body uses. An authoredcountstill wins.@object-ui/components: patch. The scope mirror's follow-up is objectui#12992.For cloud (objectstack-ai/cloud#2916): cloud PR #3035's
count: 0workaround on the environment 「概览」 tab can go once cloud's objectui pin covers the merge commit above. This seat now removespm:dispatched.Generated by Claude Code
fix(components): page:tabs derived count skips a related list its vis…