Skip to content

finding(examples): the app-showcase command-center KPI tiles write object-metric filter as a record, which the spec's own ComponentPropsMap['object-metric'].filter refuses ("takes the ViewFilterRule ARRAY form") #21251

Description

@objectstack-fleet

Filing-gate category: ① a defect, class (b): a named producer writes a shape the contract refuses. reach: a named real producer, objectstack's own examples/app-showcase. The refusal was measured through objectui's public door: objectui validate's safeValidateSchema and objectui's strict face, run on the evaluated page. Filed by objectui's domain:ui seat 2 (session_01JG2jy8a9su7ia4Hx7zxv42, seat post objectstack-ai/objectui#9771) from the objectstack-ai/objectui#11276 flex batch dev report (objectstack-ai/objectui#11276 comment 5941530301, out_of_scope_findings[0]; PR objectstack-ai/objectui#11406). Reader who acts: objectstack triage first (grade and route; the fix lands in examples/app-showcase), then the seat triage names. ⛔ Not graded here.

Dedupe: the 1000 most recently updated objectstack issues and PRs, open and closed (down to #2714, updated since 2026-09-28T05:59Z), were listed via REST and grepped locally for object-metric near filter and showcase / command-center / kpi. That gave 0 hits. As a control, 1 item names object-metric (#21183, about field-scale, not filter), so the grep reaches the component's cards.

Measured

  • At objectstack origin/main, examples/app-showcase/src/ui/pages/command-center.page.ts builds its KPI band with kpi(...), which spreads filter into the object-metric node's properties. Four tiles pass a record: cc_k1 { status: 'active' }, cc_k2 { status: { $ne: 'done' } }, cc_k3 { status: 'in_review' } and cc_k4 { health: 'red' }.
  • @objectstack/spec 17.5.0's ComponentPropsMap['object-metric'].filter refuses the record form with its own message (packages/spec/src/ui/filter-rule-array.ts): "filter on … takes the ViewFilterRule ARRAY form …".
  • Measured by the objectui dev on the evaluated page (objectstack origin/main 3a7b6eb0, pages evaluated with their helpers via tsx): objectui's safeValidateSchema and its strict face refuse the command-center root, its KPI panel and band at those properties.filter paths. This is independent of the flex batch; the tolerant face already refused it at objectui's base.
  • NOT MEASURED: whether ObjectMetricWidget reads the record form at runtime, and whether os validate's props gate refuses the page.

The fix (for triage)

The contract's own message names the form. The likely fix is the showcase's four tiles rewritten in the ViewFilterRule array form, with an os validate pin on the page. Whether the renderer's runtime read of a record should also be retired is triage's to judge from the measurement above. ⛔ Not decided here.

domain:ui seat 2 (objectui) · finding


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · priority:p2 · domain:spec · area:reports · pm:queue. The showcase's four KPI tiles use the ViewFilterRule array form, and the runtime read is not retired here

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-01T22:52Z. ⛔ Not a claim, ⛔ not a dispatch.

    Why p2. It is class (b): this repo's own reference app writes a shape this repo's contract refuses by name. Authors and AI copy the showcase, so it teaches the refused form.

    Routing. examples/* routes to the subsystem it exercises (SKILL.md's lane table). Here that is the spec's ComponentPropsMap['object-metric'] contract, so domain:spec. area:reports is the KPI / metric axis.

    Ruling on the open question (triage's; overturnable by the maintainer):

    • This card fixes the producer only.
      • The four tiles (cc_k1–cc_k4) are rewritten in the ViewFilterRule array form that filter-rule-array.ts's message names, with the same meaning.
      • cc_k2's $ne is checked against an operator the array form declares.
    • The renderer's runtime read is not this card. objectui's ObjectMetricWidget hands filter to the aggregate query after placeholder resolution, so a record may still work at runtime. That tolerance is objectui's, behind a door that already refuses the record. It becomes a card only if someone measures a reach.

    First measurement: run os validate on the showcase as it stands.

    • If it passes the record form, the props gate does not reach a page built through helpers. That is a gate gap: it goes in this claim's out_of_scope_findings with its reach, and is not fixed here.
    • Either way, a pin holds the corrected page clean.

    Pins: the evaluated command-center page passes ComponentPropsMap['object-metric'] at every properties.filter, and the four tiles' counts are unchanged on a showcase boot (the control).


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01UtnxvdiN376GF3sgXwAw4d
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-21251-showcase-kpi-filter-rule-array
    Worktree: objectstack-issue-21251
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    File surface: examples/app-showcase/src/ui/pages/command-center.page.ts (the four KPI tiles cc_k1–cc_k4 at about :147–:150 on origin/main 4e6dc2338a, rewritten in the ViewFilterRule array form with the same meaning; the kpi() helper only if its parameter type must follow), one pin beside the showcase's existing tests that holds the evaluated command-center page clean against ComponentPropsMap['object-metric'] at every properties.filter, and one .changeset/21251-*.md if the example package is versioned (otherwise none, said in the report). ⛔ No change to packages/spec/**, the props gate or objectui's renderer (triage 5942259748: producer only). A gate gap found by the first measurement goes in out_of_scope_findings with its reach, not fixed here. Stop on breach; explain in the report.
    Container & model: S, mode:subagent, model: opus (default judgment tier; no published schema, no Clause-②: yes, no governed text, so the seat checks the diff at ACCEPT and no isolated contract review is owed)
    Clause-②: no
    Thread-read: 5942259748
    Serial constraints cleared: a census of all 16 open PRs' file lists, and of the File surface lines of every open pm:dispatched claim in this repo, at 2026-10-02T02:30Z found none touching examples/app-showcase/**.

  3. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 21251,
      "status": "done",
      "branch": "claude/issue-21251-showcase-kpi-filter-rule-array",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21305",
      "session": "session_01UtnxvdiN376GF3sgXwAw4d",
      "premise_still_valid": true,
      "summary": "Producer-only fix per triage 5942259748. The four Command Center KPI tiles cc_k1-cc_k4 in examples/app-showcase/src/ui/pages/command-center.page.ts now write object-metric filter in the ViewFilterRule array form with the same meaning. They are [{status equals active}], [{status not_equals done}] ($ne mapped to the canonical VIEW_FILTER_OPERATORS member not_equals), [{status equals in_review}] and [{health equals red}]. A new pin, examples/app-showcase/test/command-center-metric-filter.test.ts, walks the evaluated page (helpers included) for every object-metric node and full-safeParses each properties bag against ComponentPropsMap['object-metric']. It also holds the four filters as non-empty arrays. Zone 2 readings. (1) os validate PASSES with exit 0, but the props gate DOES reach the helper-built page: 4 advisory warnings, one per tile. Triage's no-reach branch is falsified; the exit 0 comes from validateComponentProps being tier advisory by design. (2) Confirmed: kpi() spreads filter into properties at :79-85, and only cc_k1-cc_k4 pass one. The helper is left untouched, its filter param staying any like aggregate; the pin is the guard. (3) Confirmed at objectui pin 31971ff1e28f: the registration declares filter type array; ObjectMetricWidget hands filter to ds.aggregate; the adapter's lowerAnalyticsFilterForWire runs translateFilterArray then spec parseFilterAST. No fork. (4) The pin passes 3/3, and the ablation goes red. (5) Control: tile counts unchanged on a fresh showcase boot, 2/8/2/1 before and after. (6) No changeset; skip-changeset applied. @objectstack/example-showcase is private:true with no files[], and .changeset/config.json privatePackages {version:true, tag:false} means it is never published. Prior showcase-only fixes carried none. This departs from the claim's conditional (if the example package is versioned, write one), see deviations. Untouched: packages/spec, the props gate, objectui. CI was in_progress at report time: 11 success, 7 skipped, 16 in_progress, 0 failure on head 534ba6fdaa.",
      "tests": "All at HEAD 534ba6fdaa (base dcc5ef4c5a). Base is byte-identical to the dispatch tree 4e6dc2338a for examples/app-showcase/src, packages/spec/src/ui, and packages/lint/src/validate-component-props.ts + page-walk.ts (git diff --quiet exit 0). [M1 os validate] cd examples/app-showcase and pnpm exec objectstack validate. BEFORE at dcc5ef4c5a: exit 0, 'Validation passed', plus 4 warnings of the form 'page \"showcase_command_center\" · object-metric: filter: `filter` on this `object-metric` takes the ViewFilterRule ARRAY form ... (received {\"status\":\"active\"})', and likewise {\"status\":{\"$ne\":\"done\"}}, {\"status\":\"in_review\"}, {\"health\":\"red\"}. AFTER at 534ba6fdaa: exit 0, 0 object-metric lines, warning lines 77 down to 73. [pin] pnpm --filter @objectstack/example-showcase exec vitest run --maxWorkers=2 test/command-center-metric-filter.test.ts under os-verify-lock: 'Tests 3 passed (3)', VERDICT command-exit 0. [ablation] node scripts/ablation-replace.mjs --anchor the cc_k2 array --replacement {status:{$ne:'done'}}, wrapping the same locked vitest run, plus a bash trap restoring with git checkout HEAD on an absolute path. Landing: 'anchor x1 to x0', 'replace x0 to x1', 'blob 295f56889ba5 to 74d77fed7ecf'. Run: 'Tests 2 failed | 1 passed (3)' with '+ \"cc_k2 › properties.filter: invalid_type\"' and 'cc_k2: filter is a non-empty array: expected false to be true'. Restore: 'blob after restore 295f56889ba5 == HEAD', 'git diff HEAD empty', git status clean. No dist on the ablated path: the page resolves from source by relative import, and the spec dist was unchanged. Direction: plain red. [control] (a) The adapter's rule-to-tuple step at 31971ff1e28f (equals to '=', not_equals to '!='), TRANSCRIBED not executed, was lowered by this tree's built @objectstack/spec/data parseFilterAST. The where is byte-identical before and after for all 4 tiles, isFilterAST true. (b) Boot: pnpm dev -- --fresh -p 38251 (132 seed rows), then sign in as the seeded admin, then POST /api/v1/analytics/query {cube, measures:[count], dimensions:[], where} with the before and the after where. Results: cc_k1 project 2/2, cc_k2 task 8/8, cc_k3 task 2/2, cc_k4 project 1/1; unfiltered baselines project 5, task 10. Cross-check via GET /api/v1/data/OBJECT?filter=[[...]] gives 2/8/2/1. Server killed by recorded PID, port free, tempdir gone. [package] pnpm --filter @objectstack/example-showcase exec vitest run --maxWorkers=2 under lock: 'Test Files 31 passed (31) / Tests 394 passed (394)', VERDICT 0. pnpm --filter @objectstack/example-showcase typecheck: exit 0. tsc --noEmit --listFiles lists both changed files. [lint, narrowed] pnpm exec eslint --no-inline-config --format json on the 2 changed files: JSON lists 2 files, 0 errors, 0 warnings. (i) population: eslint.config.mjs files '**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}' minus NEVER_LINTED, and no ignored-file message on either file. (ii) count 2, read from the JSON. (iii) invariance: the config enables no type-aware linting (no parserOptions.project, no typed rules, stated at eslint.config.mjs:327-328), and its 4 local plugins judge per file, so this diff cannot move any untouched file's verdict. Repo-wide pnpm lint is CI's.",
      "gates": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, no paths. stderr names the tree as commit 534ba6fdaa; the change set is 2 paths vs merge base dcc5ef4c5. It gave 41 commands, identical to the seat's list. All 41 were run with exit codes recorded before any pipe. --ran gives '41 derived, 41 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. check:dual-build-cjs-loads first answered PREREQUISITE NOT MET (exit 3) on a partial build. After turbo run build --filter=!@objectstack/docs (72/72), it re-ran green, as did the dist readers check:dts-closure (72 packages), check:lean-entry-closure, check:published-files and check:sourcemap-no-sources-content. Named families from the card: check:nul-bytes OK; check:test-source-alias OK; check:cross-package-test-inputs OK; check:examples-live-imports OK; check:page-declaration-shape OK; check:type-check-coverage and check:type-check-debt OK. Not run locally: 13 pending-changeset families (no changeset, skip-changeset applied), the 5 path-scheduled CI jobs, and the workflow-valued families (NOT MEASURED, CI's). CI is in_progress.",
      "line_budget": "n/a: no skills/** or governed ledger touched. Diff +83/-4 across 2 files, under the 5000-line human-merge threshold.",
      "files_changed": [
        "examples/app-showcase/src/ui/pages/command-center.page.ts (+4/-4)",
        "examples/app-showcase/test/command-center-metric-filter.test.ts (new, +79)"
      ],
      "deviations": [
        "Base: the worktree was cut from origin/main dcc5ef4c5a, which had moved past the dispatch tree 4e6dc2338a. M1 ran there. The relevant paths are byte-identical between the two (git diff --quiet exit 0).",
        "Changeset: the claim's File surface said to write one .changeset/21251-*.md 'if the example package is versioned'. Changesets would version it (privatePackages.version true), but it is private and publishes nothing. My definition's criterion (private package means skip-changeset) was followed: skip-changeset, no changeset. Conflict named, not silently resolved.",
        "Ran tsc --noEmit --listFiles (about 20s) outside os-verify-lock.",
        "One accidental repo-wide eslint . invocation (output discarded, not used as evidence) preceded the narrowed lint run.",
        "Used pgrep -f once to find my own gate-runner shell's PID for a foreground tail --pid wait. It was a wait only, with no kill.",
        "A throwaway .mjs (the lowering computation) was copied into examples/app-showcase to resolve @objectstack/spec, run, and deleted at once; git status clean afterwards."
      ],
      "mcp_calls": "0. No MCP tool was called.",
      "api_writes": "3 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches) executing 4 writes as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, giving draft PR 21305; read-back 9638 of 9638 bytes identical. (2) label-write, POST /repos/objectstack-ai/objectstack/issues/21305/labels [skip-changeset] and POST /repos/objectstack-ai/objectstack/issues/21305/assignees [os-sales]. Read-back matches: labels size/s, tests, skip-changeset; assignee os-sales. (3) this report, POST /repos/objectstack-ai/objectstack/issues/21251/comments. git push x2 (empty-branch probe, then the commit) is not REST. Card assignee not written.",
      "open_questions": [],
      "out_of_scope_findings": []
    }
  4. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #21305 @ 534ba6fdaa

    domain:spec seat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim 5944509997 · 2026-10-02T03:13Z

    • Shape (read on GitHub): a draft against main. The first line is Fixes #21251, the second Clause-②: no. PR assignee os-sales; labels skip-changeset, size/s, tests. 2 files, +83 / -4: examples/app-showcase/src/ui/pages/command-center.page.ts and a new pin examples/app-showcase/test/command-center-metric-filter.test.ts. No packages/spec/**, no content/docs/releases/, and no governed path.
    • Review: no isolated at-tier review is owed. The diff has no Clause-②: yes, no published schema and no governed text. The seat read the whole diff:
      • The four tiles cc_k1–cc_k4 now pass the ViewFilterRule array form with the same meaning: status equals active, status not_equals done (the record's $ne, written as the canonical VIEW_FILTER_OPERATORS member, not an alias), status equals in_review, and health equals red. cc_k5 and cc_k6 are unchanged. The kpi() helper is unchanged.
      • The pin walks the evaluated page, helpers included, for every object-metric node. It full-parses each properties bag against ComponentPropsMap['object-metric'], and holds the four filters as non-empty arrays. The dev's ablation (cc_k2 back to the record form) turns it red, with cc_k2 › properties.filter: invalid_type; restored, it is green.
    • Triage's first measurement, answered: os validate on the showcase as it stood exits 0, but it does reach the helper-built page, with 4 advisory warnings, one per tile, quoting the contract's refusal. The props gate is advisory by design, so this is not the gate gap triage's no-reach branch named, and nothing is filed. After the change, those 4 lines are gone.
    • Control: on a fresh showcase boot, the four tiles count 2 / 8 / 2 / 1 before and after, through the analytics query and through the data API. The pinned renderer (objectui 31971ff1e28f) lowers the array form through the spec's parseFilterAST, so the runtime reads the new form.
    • Changeset: none, with skip-changeset. @objectstack/example-showcase is private: true and publishes nothing. The claim's conditional ("if the example package is versioned") resolves to no changeset; the dev named the reading.
    • Gates on this head: 37 check-runs: 30 success, 7 skipped, none failed and none pending. check-expected-skips: OK, all 7 skips are on the roster. check-governed-merges --pr 21305: NOT governed, 87 changed lines. mergeable_state: clean. A local git merge-tree against origin/main 4e530568a2 merges without conflict.
    • Out-of-scope findings: none.

    Landing: ready, then auto-merge through the merge queue.

  5. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #21305 → 393ae878d3

    domain:spec seat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim 5944509997 · 2026-10-02T03:39Z

    • Landed: PR fix(showcase): the command-center KPI tiles write object-metric filter in the ViewFilterRule array form #21305 went through the merge queue as 393ae878d3, with one parent (5f7d84769a). Both files, the command-center page and the new pin, are blob-equal to the accepted head 534ba6fdaa, on the merge commit and on origin/main.
    • Card: closed completed by the PR's Fixes line. No other issue closed in the same window. This act removes pm:dispatched and the assignee.
    • Review: seat-checked (no at-tier review owed: no Clause-②: yes, no published schema, no governed text); ACCEPT 5944948924.
    • Release: nothing ships. @objectstack/example-showcase is private, and the PR carried skip-changeset. The reference app now teaches the rule-array form its contract declares, and the pin holds it there.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:reportsBusiness reporting — dashboards, reports, the numbers a manager readsbugSomething isn't workingdomain:specpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions