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.

[finding] A record:details section heading is static, so it cannot say how many values a field holds (「N 项功能需要更高套餐 · 查看」) #12192

Closed

Description

@objectstack-fleet

Filed by the repo:cloud#1 seat (session session_01WVbr5J6u8BHh8EyFtcWciH), from the objectstack-ai/cloud#2775 dev report (cloud PR objectstack-ai/cloud#2784). ⛔ Not graded: grading is triage's. ⛔ Not a claim.

The claim

The maintainer accepted the wording 「N 项功能需要更高套餐 · 查看」 for an environment's plan gap (objectstack-ai/cloud#2772 item 3 → objectstack-ai/cloud#2775). A record:details section label is a static string and cannot interpolate the count of a multi-select field's values.

Cloud shipped the metadata half (PR objectstack-ai/cloud#2784):

  • a collapsed plan_gap section headed 「部分功能需要更高套餐」;
  • absent when capabilities_not_on_plan is empty;
  • grey badges.

The count needs objectui.

Where (objectui at cloud's pin d758f2f2)

plugin-detail: DetailSection.tsx / renderers/record-details.tsx. For example, a heading that can interpolate a field's value count, declared in metadata rather than special-cased.

Related observation (no change asked here)

The red the walkthrough reported came from objectui's hash fallback colour: djb2-xor sends approvals to red, hierarchy_security to indigo and mcp to orange. No option declared a colour. Cloud now declares color: gray per option.

Delivery note: cloud's objectui-alone pin bumps stop at 82bb1d5842 until the v18 framework move (pin-pair rule, objectstack-ai/cloud#2709 6116951023).

Dedupe: searched open objectui issues for DetailSection, section heading count and record:details: two unrelated hits (objectui#12188, objectui#12077).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, enhancement · priority:p3 · domain:ui · area:records, into pm:queue. Contract first: the heading's count is a declared key

    Triage seat (seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-12T07:58Z. ⛔ Not a claim, ⛔ not a dispatch.

    • Why p3: the maintainer accepted the wording 「N 项功能需要更高套餐 · 查看」 (objectstack-ai/cloud#2772 item 3). Cloud ships the static heading today, so nothing is broken.
    • Direction:
      • A record:details section heading that can show a field's value count is a new declared capability. Its spec key is objectstack's first: one objectstack card, filed at claim. Then plugin-detail reads it.
      • ⛔ No objectui-only special case for this one section.
    • Not asked here: the hash fallback colour. Cloud now declares color: gray per option.
    • Clause-②: yes (a new accepted key) on the spec half.
  2. added
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    enhancementNew feature or request
    and removed on Oct 12, 2026
  3. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage seat (seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-12T09:18Z. ⛔ Not a claim, ⛔ not a dispatch. priority:p3 → priority:p1.

    • objectstack-ai/cloud#2775 became priority:p1 when the maintainer raised it on 2026-10-12, and it is pm:blocked on this card. Cloud cannot make this change itself, so this card is a wall, and a wall inherits the blocked card's level (filing-gate.md:42).
    • Delivery is unchanged: this reaches cloud with cloud's v18 framework move (objectstack-ai/cloud#2709).
  4. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 7 (as triage 6120157513 directs: contract first; the heading's count is a declared key, its spec key is objectstack's first, filed at claim; ⛔ no objectui-only special case). Claimed BLOCKED: no dev is dispatched until the spec key exists.
    Session: session_016djJF12Qt14ejKR5Vjt4sK
    Account: os-steve
    Branch: claude/issue-12192-section-heading-count (not cut until the spec lands)
    Worktree: objectui-issue-12192
    Domain: domain:ui
    Seat: domain:ui#1
    File surface, on objectui main b231607, for when the spec key lands:

    State: pm:blocked, assigned to this seat. When objectstack-ai/objectstack#23013 lands its key and objectui's @objectstack/spec floor carries it, this card moves to pm:dispatched with an amendment naming the key, and the dev is dispatched. Clause-②: yes is for the new accepted key the renderer reads and mirrors.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Unlock scan: pm:blocked → pm:on-hold (upstream merged, not yet installable) · domain:ui seat 1 · session_01CsyMRsFJBKNEMa4oPY1b5z · 2026-10-12T19:56Z

    Release: domain:ui seat 1 releases its own blocked claim 6121810689 (session_016djJF12Qt14ejKR5Vjt4sK, os-steve), which was never dispatched (no branch cut, no dev). Cause: that session clocked out (brief 6124259821 on the seat post #5560), and this seat's current sitter is this session. Destination: pm:on-hold with the restart line below, and assignee cleared in the same act; whichever seat restarts it claims afresh.

    Why not back to pm:queue.

    Restart-when: an @objectstack/spec release carrying objectstack-ai/objectstack PR #23080 (a22e67b3c1) is published on npm and objectui resolves it: from apps/console, node -e "import('@objectstack/spec/ui').then(m=>process.exit(m.SECTION_HEADING_COUNT_HOLE instanceof RegExp?0:1))" exits 0.

    Dispatch shape on restart (from the claim 6121810689, re-measured then):

    • The section heading in packages/plugin-detail/src/DetailSection.tsx / renderers/record-details.tsx fills the {{ count }} hole of a section that declares count: { field }, counting the field's values on the current record.
    • It reads the spec's exported SECTION_HEADING_COUNT_HOLE; ⛔ no second hole grammar, and ⛔ no plan_gap special case.
    • The @object-ui/types mirror of count follows only if the installed spec shape needs one.
    • Clause-②: yes (a new accepted key reaches the renderer); default tier; an at-tier contract review before the queue.
    • Delivery to cloud stays with objectstack-ai/cloud#2709. Triage's priority:p1 (6121333787) stands, so the restart reads this card first.

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 5, restart. The card's restart condition is met, because objectui main c5406c2 resolves @objectstack/* 18.0.0-next.0 (objectui#12030 landed). Its probe was measured passing on next.0: SECTION_HEADING_COUNT_HOLE is a RegExp. It does not pass on 17.7.0. The dispatch shape is the hold's (6131597175):

    • The section heading fills the {{ count }} hole of a section that declares count: { field }, counting the field's values on the current record.
    • It reads the spec's exported SECTION_HEADING_COUNT_HOLE. ⛔ No second hole grammar, and ⛔ no plan_gap special case.
      Session: session_01CsyMRsFJBKNEMa4oPY1b5z
      Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
      Branch: claude/issue-12192-section-heading-count
      Worktree: objectui-issue-12192
      Domain: domain:ui
      Seat: domain:ui#1
      File surface: read on objectui main c5406c2 at 2026-10-14T02:43Z:
    • packages/plugin-detail/src/DetailSection.tsx and packages/plugin-detail/src/renderers/record-details.tsx;
    • the @object-ui/types mirror of sections[].count, only if the installed spec shape needs one;
    • co-located tests and a changeset.
    • Stop on a breach and explain it in the report.
      Container & model: S, mode:subagent, model: default tier — dispatch-gates --tier --repo objectstack-ai/objectui: "no path-derived mandate"
      Clause-②: yes
      Clause-② reason: a newly declared key (sections[].count) reaches the renderer, which widens what record:details accepts. A contract review is owed before the queue.
      Responsibility: @object-ui/plugin-detail (a record:details section heading cannot show a field's value count) | none | cloud's environment plan-gap section 「N 项功能需要更高套餐 · 查看」 (objectstack-ai/cloud#2775, p1)
      Thread-read: 6131597175
      Serial constraints cleared: read at 2026-10-14T02:43Z on objectui main c5406c2. No open PR touches these files. objectui#11773 and Render a list's declared empty-state link, and let a list page's own emptyState win over its source view's (the objectui half of objectstack#23004 and objectstack#23005) #12270 are dispatched by this seat in the same round on disjoint surfaces.

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 12192,
      "status": "done",
      "branch": "claude/issue-12192-section-heading-count",
      "pr": "https://github.com/objectstack-ai/objectui/pull/12347",
      "session": "session_01CsyMRsFJBKNEMa4oPY1b5z — harness-stamped (subagent = the seat's session)",
      "premise_still_valid": true,
      "summary": "RecordDetailsRenderer (renderers/record-details.tsx, the one place the section heading text is produced) now fills every `{{ count }}` hole of a section that declares `count: { field }`. It fills after pickLocalized and sectionLabel translation, using the spec's exported SECTION_HEADING_COUNT_HOLE (a global copy built from .source), with the number of values the field holds on the record as the viewer may read it. Empty (hasCellValue) counts 0, an array counts its entries, and any other non-empty value counts 1. A section without `count` is unchanged, and the hideEmpty and collapse behaviour is untouched. DetailSection.tsx needed no change. The registry `sections` description, which said the key was unread, now describes the behaviour; @object-ui/types mirrors `count?: { field: string }`; and a README subsection and a minor changeset (plugin-detail and types) were added.",
      "tests": "All at HEAD 1a983e8b7, through os-verify-lock (VERDICT command-exit 0): pnpm --filter @object-ui/plugin-detail type-check (echoes `tsc --noEmit && tsc -p tsconfig.test.json`): exit 0. pnpm --filter @object-ui/types type-check (all three tsc legs): exit 0. pnpm exec vitest run packages/plugin-detail/: \"Test Files 267 passed | 1 skipped (268)\", \"Tests 2597 passed | 14 skipped (2611)\". pnpm exec vitest run packages/types/: \"Test Files 377 passed (377)\", \"Tests 10258 passed (10258)\". The 8 app-shell suites that render or configure record:details sections (grep-derived: RecordDetailView.oneProvenanceFooter-12307, .recordOpenRequests-11699, .sectionHeadingsRenderPath-6190, PageBlockInspector.i18n, PageBlockInspector.sectionName, _shared.labels, block-config-i18n, block-config): \"Test Files 8 passed (8)\", \"Tests 149 passed (149)\". apps/console registry-inputs-spec-parity.test.ts: 255 passed. Closure build `turbo run build --concurrency=2 --filter=@object-ui/plugin-detail^... --filter=@object-ui/plugin-detail`: 12/12 tasks successful. New pins: record-details.sectionHeadingCount-12192.test.tsx (14 tests, real SchemaRenderer and registration) and types record-details-section-count-12192.test.ts. Renderer ablation, after the commit, through objectstack scripts/ablation-replace.mjs (wrap mode plus an EXIT/INT/TERM trap). The anchor `fillSectionHeadingCount(translatedTitle, sectionHeadingCount(data?.[countField]))` was replaced with `translatedTitle /* ABLATION-12192 */`: anchor x1 -> x0, blob 37b474722ad2 -> 19e50f1fa405. Result: \"Tests 11 failed | 3 passed (14)\", the predicted direction (the green 3 are the no-count pair and the absent-when-empty leg). Restored: blob == HEAD 37b474722ad2, `git diff HEAD` empty, marker count 0. No dist build was involved: the test imports ../record-details relatively. Types reverse validation: the anchor `count?: { field: string };` was deleted. `tsc -p tsconfig.test.json` then reported 3 errors in the new pin (TS2339 at the Equal line, TS2353 on the literal, TS2339). Restored: blob == HEAD cde183642e74, diff empty. ESLint, a proven narrowing. (1) Population from eslint.config.js: `**/*.{ts,tsx}` minus build-output ignores; the 5 touched ts/tsx files are inside it. (2) --format json counted 5 files: 0 errors, 62 warnings. Per-file base vs head, as errors/warnings: index.tsx 0/32 = 0/32, record-details.tsx 0/30 = 0/30, record-components.ts 0/0 = 0/0, new tests 0/0. (3) Invariance: no type-aware linting (no parserOptions.project or projectService), and no eslint-rules/* reads another file from disk, so untouched files' verdicts cannot move.",
      "gates": [
        "plugin-detail type-check: pass (exit 0, script name echoed)",
        "types type-check: pass (exit 0)",
        "vitest packages/plugin-detail/: pass (267 files, 2597 tests)",
        "vitest packages/types/: pass (377 files, 10258 tests)",
        "vitest app-shell record:details section suites (8): pass (149 tests)",
        "vitest apps/console registry-inputs-spec-parity: pass (255 tests)",
        "turbo closure build plugin-detail^... + plugin-detail: pass (12/12)",
        "eslint touched files (proven narrowing): pass (0 errors, no new warnings)",
        "node scripts/check-changeset-presence.mjs: pass",
        "pnpm check:changeset-claims: pass",
        "pnpm check:pending-changeset-literals: pass",
        "pnpm check:new-line-citations: pass (0 new citations)",
        "pnpm check:control-bytes: pass",
        "pnpm check:test-path-roots: pass",
        "pnpm check:vi-mock-specifiers: pass",
        "check:i18n-*: not run (no translation key added)",
        "derived from diff, pass: check:spec-symbols, check:phantom-deps, check:self-import, check:unreferenced-sources, check:doc-example-readers, check-doc-json-fences, check-doc-fence-languages, check-doc-component-types, check-doc-example-ids, check-doc-expression-carriage, check-doc-links, check-changeset-no-major, check-changeset-fixed, check-changeset-overwrite, check-type-check-coverage, check-lint-coverage, check-governed-queue-guard --test (NOT GOVERNED)",
        "NOT MEASURED: check:sdui-registration-pins, reason: exit 2, no apps/console/dist build locally (CI builds it)",
        "NOT MEASURED: check:readme-exports, reason: exit 1, population floor (24 packages unbuilt locally); the README addition is a json fence with no import or type",
        "CI on PR head 1a983e8b7: in_progress at report time (10 completed: Line Citation Gate, Changeset Fixed Group Check, Changeset Claim Re-read, Governed Surface Queue Guard, Changeset Declaration, Changeset Overwrite Report, Changeset Bump Policy and label success, 3 skipped; 22 in progress)"
      ],
      "line_budget": "n/a: no skills/** file and no line-ratcheted ledger touched",
      "deviations": [
        "The worktree /home/user/objectui-issue-12192 named in the prompt did not exist at start. I created it from origin/main c5406c2 with branch claude/issue-12192-section-heading-count, pushed the empty branch before any edit, and removed it after the PR (node_modules first, then a plain git worktree remove, which exited 0).",
        "File surface. DetailSection.tsx is unchanged: it draws section.title as given, and the heading text is produced in renderers/record-details.tsx. Two edits sit beyond the two named files, both in @object-ui/plugin-detail. (a) src/index.tsx: the registry `sections` description said \"This renderer does not read it yet\", which this change makes false. (b) README.md gains a subsection (AGENTS.md #2). The @object-ui/types mirror moved because the section entry is a closed type (TS2353 on `count` before).",
        "One MCP call: mcp__claude-code-remote__add_repo (read) for objectstack-ai/cloud, to measure Zone 2 item 3 on cloud PR 2784. It was refused (no access), so cloud's own metadata is NOT MEASURED. The pin models the collapsed, absent-when-empty shape the card body describes.",
        "Attribution. The harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. The commit uses the model-free pair (Claude-Session plus Co-authored-by: Claude), as objectui AGENTS.md and the role file require. The PR body ends with the role file's session-URL footer."
      ],
      "files_changed": [
        ".changeset/12192-section-heading-count.md",
        "packages/plugin-detail/README.md",
        "packages/plugin-detail/src/index.tsx",
        "packages/plugin-detail/src/renderers/record-details.tsx",
        "packages/plugin-detail/src/renderers/__tests__/record-details.sectionHeadingCount-12192.test.tsx",
        "packages/types/src/record-components.ts",
        "packages/types/src/__tests__/record-details-section-count-12192.test.ts"
      ],
      "mcp_calls": "1 — mcp__claude-code-remote__add_repo (objectstack-ai/cloud, access read; refused). GitHub MCP tools: 0 (no write tool).",
      "api_writes": "2 REST writes through the fleet-write relay (objectstack-fleet[bot]). (1) pr_create: dispatch POST /repos/objectstack-ai/objectstack/dispatches, executed as POST /repos/objectstack-ai/objectui/pulls (draft) and POST /repos/objectstack-ai/objectui/issues/12347/assignees [zhuangjianguo]. Read back: 10448 bytes sent and stored identical, assignee listed. (2) This os-dev-report comment: POST /repos/objectstack-ai/objectui/issues/12192/comments via post-stamped. git push x2 (empty branch marker, then 1a983e8b7) is not REST. No label write.",
      "open_questions": [
        {
          "question": "What should a section heading count when `count.field` names a SINGLE-valued field holding a value? The installed spec defines only multi-value fields and \"an absent or empty value counts 0\"; @objectstack/lint refuses a single-valued `count.field` at ERROR (page-section-count-not-multi-value), and the spec does not define the runtime count. This PR counts any non-array, non-empty value as 1, pinned by the `a single value counts 1` test.",
          "options": [
            "A — keep 1. It is one value, it agrees with the one badge SelectCellRenderer draws for a non-array value, and the renderer does not re-judge the declaration lint owns. Cost: none.",
            "B — count 0, so only arrays count. A single-valued field with a value then shows a confident 0. Cost: one line plus the pin.",
            "C — leave the hole unfilled and console.error once per section (the reportUnresolvedSectionGroup shape). This is louder at runtime but adds a second enforcement point beside lint. Cost: about 15 lines plus a reporter test."
          ],
          "recommendation": "A, by the four axes. Business: the only measured producer (cloud plan gap) names a multi-value field, and no producer writes a single-valued one. Long term: lint owns the field judgment at ERROR, and the renderer counting what its own body draws keeps one emptiness definition (hasCellValue). AI mistakes: the authoring-time refusal is the structural guard, and C would duplicate it at runtime. Startup focus: A adds nothing new. Not blocking."
        }
      ],
      "out_of_scope_findings": [
        "class: b · reach: public door — the published @object-ui/types face. `{ sections: [{ label: 'Basics', fields: ['a'], hideEmpty: false }] }` typed RecordDetailsComponentProps is TS2353 on `hideEmpty` (the control `showBorder` on the same literal compiles, measured with tsc against packages/types/src/record-components.ts at 1a983e8b7), while the installed @objectstack/spec 18.0.0-next.0 RecordDetailsProps.safeParse of the same section answers success and RecordDetailsRenderer honours the key (`hideEmpty: s.hideEmpty ?? true`) · Seam: spec:RecordDetailsProps.sections[].hideEmpty → renderer:RecordDetailsRenderer/DetailSection; the mirror omits it (same class as the section-member alignment the 8583 type pin records) · dedupe words: hideEmpty, RecordDetailsComponentProps, sections mirror, TS2353, record-details-section-members",
        "carrier: the objectstack change that moves `.objectui-sha` past this merge · noted, not filed. The spec's RecordDetailsProps.sections[].count describe still carries `[EXPERIMENTAL — not enforced]` and \"until the record detail renderer reads it, the hole renders as literal text\", and the spec's own comment says to drop the marker \"in the change that lands the renderer at the `.objectui-sha` pin\".",
        "carrier: 承接者:无 (the deferred section inspector controls feature) · noted, not filed. packages/app-shell/src/views/metadata-admin/previews/__tests__/block-config.test.ts carries a comment saying the renderer half \"is held on objectui#12192\", which goes stale on merge. Its assertion stays correct, because the `count` designer control is still deferred like the other eight."
      ]
    }

    Generated by Claude Code

  8. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: objectui PR #12347 → main c08c933 · domain:ui seat 1 · session_01CsyMRsFJBKNEMa4oPY1b5z · 2026-10-14T04:11Z

    • Merged through the merge queue at 2026-10-14T04:08Z, on parent c5406c2. This card closed completed by the PR's Fixes #12192. No other card closed at the merge.
    • Landed by content: all 7 of the PR's files on main c08c9332a6 are blob-identical to the accepted head 1a983e8.
      • ACCEPT: 6160494781.
      • Contract review: 6160437402, PASS, at CONTRACT_REVIEW_TIER.
    • pm:dispatched was removed and read back.
    • The changesets ride the next objectui release: @object-ui/plugin-detail minor and @object-ui/types minor.
    • What is on main:
      • A record:details section that declares count: { field } fills the {{ count }} hole in its heading with the number of values that field holds on the current record.
      • The hole grammar is the spec's SECTION_HEADING_COUNT_HOLE, and the fill runs after translation, so en and zh fill alike.
      • An empty or unreadable field counts 0, and a single-valued field holding a value counts 1.
      • A section without count is unchanged, and so is the absent-when-empty behaviour.
      • @object-ui/types mirrors count?: { field: string }.
    • Still open elsewhere:
      • Cloud's plan_gap section carries the hole and count: { field: 'capabilities_not_on_plan' } once it is on v18 (objectstack-ai/cloud#2709).
      • The hideEmpty mirror gap is objectui#12348.

    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:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatenhancementNew feature or requestpriority:p1

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions