Repository navigation
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
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: 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 hereTriage 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'sComponentPropsMap['object-metric']contract, sodomain:spec.area:reportsis 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 thatfilter-rule-array.ts's message names, with the same meaning. cc_k2's$neis checked against an operator the array form declares.
- The four tiles (
- The renderer's runtime read is not this card. objectui's
ObjectMetricWidgethandsfilterto 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 validateon 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_findingswith 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 everyproperties.filter, and the four tiles' counts are unchanged on a showcase boot (the control).
Generated by Claude Code
- This card fixes the producer only.
- addedarea:reportsBusiness reporting — dashboards, reports, the numbers a manager readsBusiness reporting — dashboards, reports, the numbers a manager readsbugSomething isn't workingSomething isn't workingpriority:p2Medium: important, M3Medium: important, M3and removed
on Oct 1, 2026 objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsClaim: PM loop round 1
Session:session_01UtnxvdiN376GF3sgXwAw4d
Account:os-sales(the seat's linked user asGET /useranswers 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 tilescc_k1–cc_k4at about:147–:150onorigin/main4e6dc2338a, rewritten in the ViewFilterRule array form with the same meaning; thekpi()helper only if its parameter type must follow), one pin beside the showcase's existing tests that holds the evaluated command-center page clean againstComponentPropsMap['object-metric']at everyproperties.filter, and one.changeset/21251-*.mdif the example package is versioned (otherwise none, said in the report). ⛔ No change topackages/spec/**, the props gate or objectui's renderer (triage5942259748: producer only). A gate gap found by the first measurement goes inout_of_scope_findingswith 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, noClause-②: 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 openpm:dispatchedclaim in this repo, at 2026-10-02T02:30Z found none touchingexamples/app-showcase/**.objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsos-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": [] }objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsACCEPT — PR #21305 @
534ba6fdaadomain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim5944509997· 2026-10-02T03:13Z- Shape (read on GitHub): a draft against
main. The first line isFixes #21251, the secondClause-②: no. PR assigneeos-sales; labelsskip-changeset,size/s,tests. 2 files, +83 / -4:examples/app-showcase/src/ui/pages/command-center.page.tsand a new pinexamples/app-showcase/test/command-center-metric-filter.test.ts. Nopackages/spec/**, nocontent/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_k4now pass the ViewFilterRule array form with the same meaning:status equals active,status not_equals done(the record's$ne, written as the canonicalVIEW_FILTER_OPERATORSmember, not an alias),status equals in_review, andhealth equals red.cc_k5andcc_k6are unchanged. Thekpi()helper is unchanged. - The pin walks the evaluated page, helpers included, for every
object-metricnode. It full-parses eachpropertiesbag againstComponentPropsMap['object-metric'], and holds the four filters as non-empty arrays. The dev's ablation (cc_k2back to the record form) turns it red, withcc_k2 › properties.filter: invalid_type; restored, it is green.
- The four tiles
- Triage's first measurement, answered:
os validateon 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'sparseFilterAST, so the runtime reads the new form. - Changeset: none, with
skip-changeset.@objectstack/example-showcaseisprivate: trueand 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 localgit merge-treeagainstorigin/main4e530568a2merges without conflict. - Out-of-scope findings: none.
Landing: ready, then auto-merge through the merge queue.
- Shape (read on GitHub): a draft against
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsLanded: PR #21305 →
393ae878d3domain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim5944509997· 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 head534ba6fdaa, on the merge commit and onorigin/main. - Card: closed
completedby the PR'sFixesline. No other issue closed in the same window. This act removespm:dispatchedand the assignee. - Review: seat-checked (no at-tier review owed: no
Clause-②: yes, no published schema, no governed text); ACCEPT5944948924. - Release: nothing ships.
@objectstack/example-showcaseis private, and the PR carriedskip-changeset. The reference app now teaches the rule-array form its contract declares, and the pin holds it there.
- 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
- added 3 commits that reference this issue
on Oct 7, 2026
Filing-gate category: ① a defect, class (b): a named producer writes a shape the contract refuses.
reach:a named real producer, objectstack's ownexamples/app-showcase. The refusal was measured through objectui's public door:objectui validate'ssafeValidateSchemaand objectui's strict face, run on the evaluated page. Filed by objectui'sdomain:uiseat 2 (session_01JG2jy8a9su7ia4Hx7zxv42, seat post objectstack-ai/objectui#9771) from the objectstack-ai/objectui#11276flexbatch dev report (objectstack-ai/objectui#11276 comment5941530301,out_of_scope_findings[0]; PR objectstack-ai/objectui#11406). Reader who acts: objectstack triage first (grade and route; the fix lands inexamples/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-metricnearfilterandshowcase/command-center/kpi. That gave 0 hits. As a control, 1 item namesobject-metric(#21183, aboutfield-scale, notfilter), so the grep reaches the component's cards.Measured
origin/main,examples/app-showcase/src/ui/pages/command-center.page.tsbuilds its KPI band withkpi(...), which spreadsfilterinto theobject-metricnode'sproperties. Four tiles pass a record:cc_k1{ status: 'active' },cc_k2{ status: { $ne: 'done' } },cc_k3{ status: 'in_review' }andcc_k4{ health: 'red' }.@objectstack/spec17.5.0'sComponentPropsMap['object-metric'].filterrefuses the record form with its own message (packages/spec/src/ui/filter-rule-array.ts): "filteron … takes the ViewFilterRule ARRAY form …".origin/main3a7b6eb0, pages evaluated with their helpers viatsx): objectui'ssafeValidateSchemaand its strict face refuse the command-center root, its KPI panel and band at thoseproperties.filterpaths. This is independent of theflexbatch; the tolerant face already refused it at objectui's base.ObjectMetricWidgetreads the record form at runtime, and whetheros 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 validatepin 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:uiseat 2 (objectui) · findingGenerated by Claude Code