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 filter builder: a progress field is offered text operators (Contains, Starts with…) that the server refuses, and no numeric Greater/Less than #27497
Filing gate: ① a user-visible product defect with a reproduction, found by an off-clause probe of QA run #27438 (wave 3 of the 18.3.0 sweep, anchor #27377) and reproduced twice. reach: measured in the console's list filter builder. Every operator it offers for the field is refused by the data door with a 400.
Business reason: a user filtering a list by a progress field is offered only text operators (Contains, Starts with…), which the server refuses, and no Greater-than or Less-than, which the server accepts. The filter the user most likely wants ("progress above 50%") cannot be built in the UI.
Reader who acts: the triage seat, then the implementer for domain:ui (ui/packages/components/src/custom/filter-builder.tsx).
Dedupe (open AND closed): the query "filter builder progress field operators text contains instead of greater than" returned 0 hits.
As admin, open /_console/apps/com.example.showcase/showcase_task/view/showcase_task.grid, click the toolbar filter, then Add filter, and pick the field Progress (served type progress, task.object.ts:70).
The operator list is: Equals, Does not equal, Contains, Contains (ignore case), Does not contain, Is empty, Is not empty, Starts with, Ends with. There is no Greater than or Less than.
For comparison, Estimate (h), a number field, gets Equals, Does not equal, Is empty, Is not empty, Greater than, Less than, ≥, ≤.
On the server, GET /api/v1/data/showcase_task?filter=["progress","contains","4"] → 400 ("aims the text operator $contains at a declared progress field").
GET /api/v1/data/showcase_task?filter=["progress",">",50] → 200, total 5.
Expected:progress is offered the numeric operators the server accepts.
Mechanism
In ui/packages/components/src/custom/filter-builder.tsx at about line 1207, the numeric type list is [number, currency, percent, rating] and leaves out progress, so the field falls through to the text operators.
QA-source: #27438 · records-forms.adhoc-filter-sort-builder · observed beside the clauses
Read fresh. The Console filter builder offers a progress field only text operators, which the server refuses with 400, and no numeric greater-than or less-than, which the server accepts. The builder disagrees with the door: p2.
Add progress to filter-builder's existing numeric type list. No per-field operator override.
Claim: PM loop round 2 · 2026-10-29T12:55Z
Session: session_01L6bhgXU26UBEjd1KKHgNEx (seat; in-seat mode:subagent dev, same session, identity by branch)
Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-27497-progress-filter-number-ops
Worktree: objectstack-issue-27497
Domain: domain:ui
Seat: domain:ui#1
File surface: ui/packages/types/src/filter-field-types.ts, its mirror pins (ui/packages/types/src/__tests__/filter-builder-mirror-6939.test.ts, examples/schema-catalog/test/filter-builder-mirror-6939.test.tsx), generated artefacts the list feeds (regenerated), one patch changeset (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: TIER_DEFAULT (seat-run dispatch-gates --tier on the paths: no path-derived mandate), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: console filter builder (this lane) | the one field-type list FILTER_NUMBER_LIKE_TYPES (#27400) | every console user filtering a list on a progress field
Thread-read: 6495326385
Premise: holds — on origin/mainad3a640e, FILTER_NUMBER_LIKE_TYPES is number/currency/percent/rating and progress is absent from FILTER_FIELD_TYPES, so a progress field falls to text operators.
Serial constraints cleared: open PR #27340 (os-bill) edits filter-builder.tsx in other regions; this card does not plan to touch it.
Resume-from: none
{
"issue": 27497,
"status": "needs_decision",
"branch": "claude/issue-27497-progress-filter-number-ops",
"pr": null,
"session": "session_01L6bhgXU26UBEjd1KKHgNEx",
"premise_still_valid": true,
"summary": "Premise holds on origin/main ad3a640: FILTER_NUMBER_LIKE_TYPES in ui/packages/types/src/filter-field-types.ts is number/currency/percent/rating, progress is absent from FILTER_FIELD_TYPES, so operatorsForFieldType gives a progress column the text bucket. Stopped before any edit on the dispatch's own tripwire: FILTER_FIELD_TYPES spreads FILTER_NUMBER_LIKE_TYPES and FilterFieldSchema.type in ui/packages/types/src/zod/complex.zod.ts is z.enum(FILTER_FIELD_TYPES); FilterFieldSchema and FilterBuilderSchema (fields: z.array(FilterFieldSchema)) are exported from the './zod' entry of the published package @object-ui/types (18.2.0, not private, exports map lists './zod' as dist/zod/index.zod), and FilterFieldType types FilterField['type'] on the main entry. Adding progress to the list therefore widens a published accept set by one member, which contradicts the claimed Clause-②: no / Metadata-protocol: none. Empty branch was pushed (no commits beyond ad3a640); the worktree was removed clean; no PR opened.",
"tests": "none run: stopped at the measurement step before any edit, per the dispatch's stop-and-report instruction.",
"gates": "NOT MEASURED: no diff exists, so dispatch-gates --commands has nothing to derive and --ran nothing to reconcile.",
"dogfood_battery": "n/a — no PR",
"mcp_calls": "0",
"api_writes": "1 — POST /repos//issues/27497/comments (this report, via scripts/pm/post-stamped.mjs). git push of the empty branch is not a REST write.",
"open_questions": [
{
"question": "Adding progress to FILTER_NUMBER_LIKE_TYPES (the triage direction) also widens the published FilterFieldSchema.type enum and the FilterFieldType union in @object-ui/types by one member, progress. Land it with the widening declared, or keep the accept set unchanged?",
"options": [
"A — Follow the triage direction as written: add progress to FILTER_NUMBER_LIKE_TYPES, update the mirror pins (BUCKETED_TYPES row; the spec-FieldType check already accepts it, since progress is a spec FieldType), and re-claim with Clause-②: yes (widening) and Metadata-protocol: added, so the changeset is minor on @object-ui/types and the PR owes a Skills-docs line. About 20 changed lines; the review tier follows the Clause-② yes declaration (TIER_DEFAULT lane-seat record per contract-review.md).",
"B — Keep the published enum as it is: map progress to the number family only inside the renderer, or at the console producer that builds the filter fields. That is a per-field override, which the triage comment rules out, and a filter-builder node authored with type progress would still be refused by the validator while the console draws it. Not recommended."
],
"recommendation": "A, because progress is already a spec FieldType, so the widening brings the renderer's vocabulary into line with the spec rather than inventing a dialect; it keeps one shared list, as the triage asks; and the declaration lines then match the diff. Only the claim's Clause-② and Metadata-protocol lines and the changeset level change."
}
],
"out_of_scope_findings": [],
"deviations": [
"Conflict named: os-dev.md puts the bare marker os-dev-report on line one, but post-stamped.mjs refuses any body whose first non-empty line is not the seat signature (dry run exit 2, 'the body opens with no signature line'). The dispatch's order is used: signature on line one, marker alone on line two.",
"Conflict named, not exercised (no PR): the dispatch asks for the bare footer in the PR body; os-dev.md and AGENTS.md give PR bodies the session-URL footer form."
],
"line_budget": "0 / 150 — no diff",
"files_changed": []
}
Ruling: maintainer letter C on analysis 6497480039 · 2026-10-29T14:23Z
The maintainer (Sales) picked option C on the seat thread's decision card (「C 跟 spec 数字类型对齐」), 2026-10-29T14:22Z. Recorded by the domain:ui seat 1; reversible on one word from the maintainer.
C: the filter builder's number bucket follows the spec's numeric value family, NUMERIC_VALUE_TYPES in packages/spec/src/data/field-value.zod.ts (number, currency, percent, rating, slider, progress, summary), taken from that export rather than copied, so a spec numeric type added later follows automatically.
Execution parameters:
FILTER_NUMBER_LIKE_TYPES in ui/packages/types/src/filter-field-types.ts derives from NUMERIC_VALUE_TYPES (@objectstack/spec/data, already a dependency of @object-ui/types); FILTER_FIELD_TYPES, FilterFieldSchema.type and FilterFieldType follow from it as today.
Before adding slider and summary, measure the server's operator acceptance for each (GET /api/v1/data/<object>?filter=["<field>",">",N] on a stock showcase field, or the server's operator-set code cited). A type the server refuses numeric operators for stays out, and the report says so with the reading.
The mirror pins assert the number bucket equals the spec numeric family (minus any measured exclusion); ablation: drop one member ⇒ red.
Declarations: Clause-②: yes (widening), Metadata-protocol: added; changeset minor on @object-ui/types, patch on any other package it moves; PR body carries Skills-docs:.
Review: TIER_DEFAULT contract-review record by this seat before ACCEPT (declaration face).
Ruling-ref for the next claim and report: this comment.
Claim: PM loop round 3 (supersedes 6497251607 on maintainer ruling C, Ruling-ref: 6499302510) · 2026-10-29T14:25Z
Session: session_01L6bhgXU26UBEjd1KKHgNEx (seat; in-seat mode:subagent dev, same session, identity by branch)
Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-27497-progress-filter-number-ops
Worktree: objectstack-issue-27497
Domain: domain:ui
Seat: domain:ui#1
File surface: ui/packages/types/src/filter-field-types.ts (number bucket derived from @objectstack/spec/dataNUMERIC_VALUE_TYPES), its mirror pins (ui/packages/types/src/__tests__/filter-builder-mirror-6939.test.ts, ui/examples/schema-catalog/test/filter-builder-mirror-6939.test.tsx), ui/packages/components/src/custom/filter-builder.tsx only if a renderer branch is measured missing, generated artefacts the list feeds (regenerated), one changeset; ⛔ packages/spec/** (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: TIER_DEFAULT (seat-run dispatch-gates --tier on the paths: no path-derived mandate), effort: medium
Clause-②: yes (widening: FilterFieldSchema.type / FilterFieldType on the published @object-ui/types gain the spec numeric types the server reads numerically)
Metadata-protocol: added
Responsibility: console filter builder (this lane) | the one field-type list FILTER_NUMBER_LIKE_TYPES (#27400) | every console user filtering a list on a progress field
Thread-read: 6499302510
Premise: holds — on origin/mainad3a640e, FILTER_NUMBER_LIKE_TYPES is number/currency/percent/rating and progress is absent from FILTER_FIELD_TYPES, so a progress field falls to text operators.
Serial constraints cleared: open PR #27340 (os-bill) edits filter-builder.tsx in other regions; this card does not plan to touch it.
Resume-from: none
{
"issue": 27497,
"status": "done",
"branch": "claude/issue-27497-progress-filter-number-ops",
"pr": "#27631",
"session": "session_01L6bhgXU26UBEjd1KKHgNEx",
"premise_still_valid": true,
"summary": "Ruling C executed. FILTER_NUMBER_LIKE_TYPES in ui/packages/types/src/filter-field-types.ts is now taken from NUMERIC_VALUE_TYPES in @objectstack/spec/data instead of a hand-kept four, so progress, slider and summary join the number bucket, FILTER_FIELD_TYPES, FilterFieldSchema.type and FilterFieldType (a widening by three members). Server acceptance was measured live on a fresh stock showcase built from this branch: numeric comparisons answer 200 and contains answers 400 for all three types, so nothing was excluded. filter-builder.tsx was not touched: every renderer branch reads the bucket by membership, and the render-half per-member measurement covers the new members.",
"tests": "pnpm --dir ui exec vitest run packages/types/ packages/components/src/tests/filter-builder packages/plugin-list/src/tests/list-offered-operator-expressible-parity.test.ts examples/schema-catalog/ :: lock VERDICT command-exit 0, Test Files 453 passed (453), Tests 13312 passed (13312). pnpm --filter @object-ui/types run type-check :: exit 0 (tsconfig.test.json --listFiles includes the mirror test, 1 hit). pnpm --filter @object-ui/example-schema-catalog --filter @object-ui/components run type-check :: exit 0. Ablation via scripts/ablation-replace.mjs (subjects resolve to src through the ui vitest alias for @object-ui/types and the relative import, so no dist leg): the derivation was mutated to filter out slider (anchor 1 to 0, blob 2af20a77c320 to c231fbe12b53), and the mirror run went red with Test Files 1 failed and 2 tests failed (the bucket-equals-spec-family pin and the slider accept row); the restore matched the HEAD blob and git diff HEAD was empty. Reverse type verification: adding formula to the exhaustive type-face record fails tsc -p tsconfig.test.json with TS2353, and the file was restored to its HEAD blob. A first attempt used an anchor contained in its replacement; the tool refused it as a no-op and the run was redone with a disjoint anchor. Server measurement on showcase (fresh DB, port 44186, this branch's build): progress gt 50 gives 200 (5 rows), gt '50' 200, gte 200, lte 200, contains 400; f_slider gt 10 200, lte 10 200, contains 400; showcase_invoice.total (summary) gt 0 200 (4 rows), lt 1e8 200 (12 rows), contains 400; showcase_expense_report.total_amount (summary) gte 0 200.",
"gates": "On head 0c17b16, dispatch-gates --commands --repo objectstack-ai/objectstack derived 34 commands. All 34 were run, each with its exit captured before any pipe, and all 34 exited 0 (33 lock-free; pnpm --dir ui exec vitest run scripts/ under the lock: 169 passed, 2 skipped). dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED, a derived zero. pnpm check:new-line-citations (ui): 0 new citations, exit 0. PR check-runs on 0c17b16 at report time: 33 total, 4 success, 11 skipped, 16 in_progress, 2 queued, 0 failed, so CI is in_progress. The type-check lanes and artifact-roster families are left to CI.",
"dogfood_battery": "n/a — no ! in the PR title and Clause-② carries the (widening) arm, not (narrowing)",
"mcp_calls": "0",
"api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay pr_create, request fw-20261029T152456Z-6443c8, run 40901342135 success, created draft PR #27631 with no assignees; the read-back says 9551 bytes sent and 9551 stored, identical); POST /repos//issues/27497/comments (this report, via scripts/pm/post-stamped.mjs). git push is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted in PR Acceptance notes, not filed: ui/packages/plugin-view/src/config/view-config-utils.ts normalizeFieldType keeps its own numeric spelling list (integer/int/float/double/number/currency/money/percent/rating), which also leaves out progress, slider and summary. This is read-only inference, not reproduced through a public door, so it is not a filing class. Dedupe words: normalizeFieldType progress, view config filter numeric type, plugin-view field type list",
"carrier: none · noted in PR Acceptance notes: ui/packages/components/docs/FilterBuilder.md lists type as five spellings and was already stale before this change (polish)"
],
"deviations": [
"Conflict named: os-dev.md puts the bare marker os-dev-report on line one of the report comment, but post-stamped.mjs refuses a body that does not open with the seat signature. The dispatch order was used: signature on line one, marker on line two.",
"Conflict named: the dispatch asked for the bare Generated-by footer in the PR body, but os-dev.md and AGENTS.md give PR bodies the session-URL form. The session-URL form was used (os-dev.md wins on conflict), and the dispatch's trailing harness lines were kept after it.",
"Conflict named: the harness attribution reminder asked for a model-named Co-Authored-By commit trailer, but AGENTS.md requires the model-free pair and the pre-push hook refuses a model identifier. The commit carries Claude-Session plus Co-authored-by: Claude, model-free.",
"Interpretation of 'derived, not copied': the runtime value is derived from NUMERIC_VALUE_TYPES. That export is typed as a read-only set of strings, so it yields no literals at compile time, and packages/spec is out of the file surface. The type face therefore declares a literal union (SpecNumericValueType), pinned equal to the spec set by an exhaustive record plus a runtime key comparison. A spec addition reaches the runtime list, the zod enum and the renderer at once, and reddens the pin until the union names it.",
"Spelling only: the dispatch names the render-half pin as examples/schema-catalog/test/filter-builder-mirror-6939.test.tsx (relative to ui/), and the claim names it ui/examples/schema-catalog/...; these are the same file.",
"The changeset is minor on @object-ui/types only. No other published package's files moved (filter-builder.tsx untouched; schema-catalog is an example).",
"Ablation was run once, on the derivation (dropping slider), and it reddened the types-half pins. The render-half operator block was not ablated separately: NOT MEASURED, reason: one ablation per tested behaviour was judged sufficient, since the block iterates the same bucket."
],
"line_budget": "100 / 200 (+95 / -5, 4 files, per dispatch-gates)",
"files_changed": [
".changeset/27497-filter-number-bucket-spec-numeric.md",
"ui/examples/schema-catalog/test/filter-builder-mirror-6939.test.tsx",
"ui/packages/types/src/tests/filter-builder-mirror-6939.test.ts",
"ui/packages/types/src/filter-field-types.ts"
]
}
Activity
objectstack-fleet commentedon Oct 29, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 看数据的视图 | records-forms.adhoc-filter-sort-builder | P②
Triage: p2 ·
domain:ui·area:records· 2026-10-29T11:23Z⛔ Not a claim.
bug,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx
Claim: PM loop round 2 · 2026-10-29T12:55Z
Session:
session_01L6bhgXU26UBEjd1KKHgNEx(seat; in-seatmode:subagentdev, same session, identity by branch)Account:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-27497-progress-filter-number-opsWorktree:
objectstack-issue-27497Domain:
domain:uiSeat:
domain:ui#1File surface:
ui/packages/types/src/filter-field-types.ts, its mirror pins (ui/packages/types/src/__tests__/filter-builder-mirror-6939.test.ts,examples/schema-catalog/test/filter-builder-mirror-6939.test.tsx), generated artefacts the list feeds (regenerated), one patch changeset (stop on breach; explain in the report)Container & model:
S,mode:subagent,model: TIER_DEFAULT(seat-rundispatch-gates --tieron the paths: no path-derived mandate),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility: console filter builder (this lane) | the one field-type list
FILTER_NUMBER_LIKE_TYPES(#27400) | every console user filtering a list on a progress fieldThread-read: 6495326385
Premise: holds — on
origin/mainad3a640e,FILTER_NUMBER_LIKE_TYPESis number/currency/percent/rating andprogressis absent fromFILTER_FIELD_TYPES, so a progress field falls to text operators.Serial constraints cleared: open PR #27340 (
os-bill) editsfilter-builder.tsxin other regions; this card does not plan to touch it.Resume-from: none
objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx
os-dev-report
{
"issue": 27497,
"status": "needs_decision",
"branch": "claude/issue-27497-progress-filter-number-ops",
"pr": null,
"session": "session_01L6bhgXU26UBEjd1KKHgNEx",
"premise_still_valid": true,
"summary": "Premise holds on origin/main ad3a640: FILTER_NUMBER_LIKE_TYPES in ui/packages/types/src/filter-field-types.ts is number/currency/percent/rating, progress is absent from FILTER_FIELD_TYPES, so operatorsForFieldType gives a progress column the text bucket. Stopped before any edit on the dispatch's own tripwire: FILTER_FIELD_TYPES spreads FILTER_NUMBER_LIKE_TYPES and FilterFieldSchema.type in ui/packages/types/src/zod/complex.zod.ts is z.enum(FILTER_FIELD_TYPES); FilterFieldSchema and FilterBuilderSchema (fields: z.array(FilterFieldSchema)) are exported from the './zod' entry of the published package @object-ui/types (18.2.0, not private, exports map lists './zod' as dist/zod/index.zod), and FilterFieldType types FilterField['type'] on the main entry. Adding progress to the list therefore widens a published accept set by one member, which contradicts the claimed Clause-②: no / Metadata-protocol: none. Empty branch was pushed (no commits beyond ad3a640); the worktree was removed clean; no PR opened.",
"tests": "none run: stopped at the measurement step before any edit, per the dispatch's stop-and-report instruction.",
"gates": "NOT MEASURED: no diff exists, so dispatch-gates --commands has nothing to derive and --ran nothing to reconcile.",
"dogfood_battery": "n/a — no PR",
"mcp_calls": "0",
"api_writes": "1 — POST /repos//issues/27497/comments (this report, via scripts/pm/post-stamped.mjs). git push of the empty branch is not a REST write.",
"open_questions": [
{
"question": "Adding progress to FILTER_NUMBER_LIKE_TYPES (the triage direction) also widens the published FilterFieldSchema.type enum and the FilterFieldType union in @object-ui/types by one member, progress. Land it with the widening declared, or keep the accept set unchanged?",
"options": [
"A — Follow the triage direction as written: add progress to FILTER_NUMBER_LIKE_TYPES, update the mirror pins (BUCKETED_TYPES row; the spec-FieldType check already accepts it, since progress is a spec FieldType), and re-claim with Clause-②: yes (widening) and Metadata-protocol: added, so the changeset is minor on @object-ui/types and the PR owes a Skills-docs line. About 20 changed lines; the review tier follows the Clause-② yes declaration (TIER_DEFAULT lane-seat record per contract-review.md).",
"B — Keep the published enum as it is: map progress to the number family only inside the renderer, or at the console producer that builds the filter fields. That is a per-field override, which the triage comment rules out, and a filter-builder node authored with type progress would still be refused by the validator while the console draws it. Not recommended."
],
"recommendation": "A, because progress is already a spec FieldType, so the widening brings the renderer's vocabulary into line with the spec rather than inventing a dialect; it keeps one shared list, as the triage asks; and the declaration lines then match the diff. Only the claim's Clause-② and Metadata-protocol lines and the changeset level change."
}
],
"out_of_scope_findings": [],
"deviations": [
"Conflict named: os-dev.md puts the bare marker os-dev-report on line one, but post-stamped.mjs refuses any body whose first non-empty line is not the seat signature (dry run exit 2, 'the body opens with no signature line'). The dispatch's order is used: signature on line one, marker alone on line two.",
"Conflict named, not exercised (no PR): the dispatch asks for the bare footer in the PR body; os-dev.md and AGENTS.md give PR bodies the session-URL footer form."
],
"line_budget": "0 / 150 — no diff",
"files_changed": []
}
objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx
待裁决:筛选器的数字字段清单要不要扩到 progress(会放宽一个已发布的接受集)· 2026-10-29T13:05Z
一句话问题:控制台列表筛选里,「进度」类字段只给出服务端会拒收的文本条件(包含、开头是…),没有「大于 / 小于」;修它就得让一个已发布包(
@object-ui/types)多接受一种字段类型,这是放宽契约,按规矩要你定方向。背景:开发子代理按分诊方向(把 progress 加进现有数字类型清单,不做逐字段覆盖)开工前实测停手,报告见 6497372664。这份清单
FILTER_NUMBER_LIKE_TYPES自 #27400 起是唯一来源,同时喂三处:校验器FilterFieldSchema.type(z.enum(FILTER_FIELD_TYPES),从已发布的@object-ui/types/zod入口导出)、类型FilterField['type']、渲染器的分桶。所以加一个成员就是三处一起放宽,认领评论里的Clause-②: no不成立,应为yes。Governing text:
ui/packages/types/src/__tests__/filter-builder-mirror-6939.test.ts文件头记录了此接受集历次变动都是裁决驱动:objectui#6939 维护者裁决(评论 5510084784)、决策批 #88 放宽到十四个(objectui#7562)、#27369 收成单一来源且「接受集未动」。spec 侧:packages/spec/src/data/field-value.zod.ts的NUMERIC_VALUE_TYPES= number / currency / percent / rating / slider / progress / summary,服务端按它判数字字段。协议声明:
Clause-②: yes;Metadata-protocol: added(元数据作者可写的filter-builder字段type多一个值);changeset 对@object-ui/types为 minor,PR 正文欠一行Skills-docs:。前提(附复测命令):
origin/main上清单为 number/currency/percent/rating,progress 不在FILTER_FIELD_TYPES:git show origin/main:ui/packages/types/src/filter-field-types.ts | grep FILTER_NUMBER_LIKE_TYPES。服务端:卡面复现filter=["progress",">",50]→ 200、["progress","contains","4"]→ 400。顺带实测:slider、summary 同为 spec 数字类型,也不在清单里,是同一类缺口(服务端对它们的数字条件未实测)。选项
FILTER_NUMBER_LIKE_TYPES,申报改为Clause-②: yes/Metadata-protocol: added,镜像测试补一行。客户看到:进度字段能筛「大于 50%」,文本条件消失。工作量:ui 车道 · 1 PR · 约 20 行;无直接放宽先例(最近同文件 refactor(ui-types): one source for the filter-builder field-type list; close the three K4 review should-fixes #27400 是重构,+368/−249)。slider、summary 另立卡。type: progress的筛选器节点仍被校验器拒、控制台却画得出来,形成「声明与实际不符」。工作量:约 15 行。分诊已明确排除逐字段覆盖。NUMERIC_VALUE_TYPES(本包已依赖@objectstack/spec),一次补齐 progress、slider、summary,以后 spec 加数字类型自动跟上。工作量:ui 车道 · 1 PR · 约 40–60 行,外加先实测服务端对 slider、summary 数字条件的接受情况;无先例。业务含义直译:A 是「修好这一个按钮」;B 是「在前台贴一张便条,后台账本不改」;C 是「让前台的商品分类表直接抄总部目录,不再自己维护一份」。
四轴(业务立场)
type: progress被校验器接受且渲染器兑现,出错时响亮;B 让校验器拒、渲染器收,AI 写元数据时被静默误导。Prior rulings read:
FILTER_FIELD_TYPES、Ruled、rulingin filter-builder-mirror-6939.test.ts → 4 hits(objectui#6939 5510084784、决策批 #88、#27369、objectui#9559 ruling B); ADR none; thread: none推荐
推荐 C。终态句:两年后筛选器的字段分类应与平台字段类型的值族一一对应,像 Salesforce / Airtable 那样按字段类型给条件,不逐字段配置,也不在 UI 另抄一份清单;C 就是这个终态,A 朝同一方向但仍是手抄清单。自检:只看①选 C;②③④ 是否翻转:是(④:slider、summary 今天零实测拉动)。按规则答是不改字母,④只调执行:开发先实测服务端对 slider、summary 的数字条件,服务端拒的不入清单并写进报告。工作量 C 约为 A 的 2–3 倍,差距不到 3 倍。
裁后执行
Clause-②: yes/Metadata-protocol: added,派席内开发,PR 带 minor changeset 与Skills-docs:行,ACCEPT 前做TIER_DEFAULT契约复核记录;另立 slider/summary 卡交分诊。pm:queue,下一位 ui 席照裁执行。Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx
Ruling: maintainer letter C on analysis 6497480039 · 2026-10-29T14:23Z
The maintainer (Sales) picked option C on the seat thread's decision card (「C 跟 spec 数字类型对齐」), 2026-10-29T14:22Z. Recorded by the domain:ui seat 1; reversible on one word from the maintainer.
C: the filter builder's number bucket follows the spec's numeric value family,
NUMERIC_VALUE_TYPESinpackages/spec/src/data/field-value.zod.ts(number, currency, percent, rating, slider, progress, summary), taken from that export rather than copied, so a spec numeric type added later follows automatically.Execution parameters:
FILTER_NUMBER_LIKE_TYPESinui/packages/types/src/filter-field-types.tsderives fromNUMERIC_VALUE_TYPES(@objectstack/spec/data, already a dependency of@object-ui/types);FILTER_FIELD_TYPES,FilterFieldSchema.typeandFilterFieldTypefollow from it as today.sliderandsummary, measure the server's operator acceptance for each (GET /api/v1/data/<object>?filter=["<field>",">",N]on a stock showcase field, or the server's operator-set code cited). A type the server refuses numeric operators for stays out, and the report says so with the reading.Clause-②: yes (widening),Metadata-protocol: added; changesetminoron@object-ui/types,patchon any other package it moves; PR body carriesSkills-docs:.TIER_DEFAULTcontract-review record by this seat before ACCEPT (declaration face).Ruling-ref for the next claim and report: this comment.
Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx
Claim: PM loop round 3 (supersedes 6497251607 on maintainer ruling C, Ruling-ref: 6499302510) · 2026-10-29T14:25Z
Session:
session_01L6bhgXU26UBEjd1KKHgNEx(seat; in-seatmode:subagentdev, same session, identity by branch)Account:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-27497-progress-filter-number-opsWorktree:
objectstack-issue-27497Domain:
domain:uiSeat:
domain:ui#1File surface:
ui/packages/types/src/filter-field-types.ts(number bucket derived from@objectstack/spec/dataNUMERIC_VALUE_TYPES), its mirror pins (ui/packages/types/src/__tests__/filter-builder-mirror-6939.test.ts,ui/examples/schema-catalog/test/filter-builder-mirror-6939.test.tsx),ui/packages/components/src/custom/filter-builder.tsxonly if a renderer branch is measured missing, generated artefacts the list feeds (regenerated), one changeset; ⛔packages/spec/**(stop on breach; explain in the report)Container & model:
S,mode:subagent,model: TIER_DEFAULT(seat-rundispatch-gates --tieron the paths: no path-derived mandate),effort: mediumClause-②: yes (widening:
FilterFieldSchema.type/FilterFieldTypeon the published@object-ui/typesgain the spec numeric types the server reads numerically)Metadata-protocol: added
Responsibility: console filter builder (this lane) | the one field-type list
FILTER_NUMBER_LIKE_TYPES(#27400) | every console user filtering a list on a progress fieldThread-read: 6499302510
Premise: holds — on
origin/mainad3a640e,FILTER_NUMBER_LIKE_TYPESis number/currency/percent/rating andprogressis absent fromFILTER_FIELD_TYPES, so a progress field falls to text operators.Serial constraints cleared: open PR #27340 (
os-bill) editsfilter-builder.tsxin other regions; this card does not plan to touch it.Resume-from: none
objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx
os-dev-report
{
"issue": 27497,
"status": "done",
"branch": "claude/issue-27497-progress-filter-number-ops",
"pr": "#27631",
"session": "session_01L6bhgXU26UBEjd1KKHgNEx",
"premise_still_valid": true,
"summary": "Ruling C executed. FILTER_NUMBER_LIKE_TYPES in ui/packages/types/src/filter-field-types.ts is now taken from NUMERIC_VALUE_TYPES in @objectstack/spec/data instead of a hand-kept four, so progress, slider and summary join the number bucket, FILTER_FIELD_TYPES, FilterFieldSchema.type and FilterFieldType (a widening by three members). Server acceptance was measured live on a fresh stock showcase built from this branch: numeric comparisons answer 200 and contains answers 400 for all three types, so nothing was excluded. filter-builder.tsx was not touched: every renderer branch reads the bucket by membership, and the render-half per-member measurement covers the new members.",
"tests": "pnpm --dir ui exec vitest run packages/types/ packages/components/src/tests/filter-builder packages/plugin-list/src/tests/list-offered-operator-expressible-parity.test.ts examples/schema-catalog/ :: lock VERDICT command-exit 0, Test Files 453 passed (453), Tests 13312 passed (13312). pnpm --filter @object-ui/types run type-check :: exit 0 (tsconfig.test.json --listFiles includes the mirror test, 1 hit). pnpm --filter @object-ui/example-schema-catalog --filter @object-ui/components run type-check :: exit 0. Ablation via scripts/ablation-replace.mjs (subjects resolve to src through the ui vitest alias for @object-ui/types and the relative import, so no dist leg): the derivation was mutated to filter out slider (anchor 1 to 0, blob 2af20a77c320 to c231fbe12b53), and the mirror run went red with Test Files 1 failed and 2 tests failed (the bucket-equals-spec-family pin and the slider accept row); the restore matched the HEAD blob and git diff HEAD was empty. Reverse type verification: adding formula to the exhaustive type-face record fails tsc -p tsconfig.test.json with TS2353, and the file was restored to its HEAD blob. A first attempt used an anchor contained in its replacement; the tool refused it as a no-op and the run was redone with a disjoint anchor. Server measurement on showcase (fresh DB, port 44186, this branch's build): progress gt 50 gives 200 (5 rows), gt '50' 200, gte 200, lte 200, contains 400; f_slider gt 10 200, lte 10 200, contains 400; showcase_invoice.total (summary) gt 0 200 (4 rows), lt 1e8 200 (12 rows), contains 400; showcase_expense_report.total_amount (summary) gte 0 200.",
"gates": "On head 0c17b16, dispatch-gates --commands --repo objectstack-ai/objectstack derived 34 commands. All 34 were run, each with its exit captured before any pipe, and all 34 exited 0 (33 lock-free; pnpm --dir ui exec vitest run scripts/ under the lock: 169 passed, 2 skipped). dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED, a derived zero. pnpm check:new-line-citations (ui): 0 new citations, exit 0. PR check-runs on 0c17b16 at report time: 33 total, 4 success, 11 skipped, 16 in_progress, 2 queued, 0 failed, so CI is in_progress. The type-check lanes and artifact-roster families are left to CI.",
"dogfood_battery": "n/a — no ! in the PR title and Clause-② carries the (widening) arm, not (narrowing)",
"mcp_calls": "0",
"api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay pr_create, request fw-20261029T152456Z-6443c8, run 40901342135 success, created draft PR #27631 with no assignees; the read-back says 9551 bytes sent and 9551 stored, identical); POST /repos//issues/27497/comments (this report, via scripts/pm/post-stamped.mjs). git push is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted in PR Acceptance notes, not filed: ui/packages/plugin-view/src/config/view-config-utils.ts normalizeFieldType keeps its own numeric spelling list (integer/int/float/double/number/currency/money/percent/rating), which also leaves out progress, slider and summary. This is read-only inference, not reproduced through a public door, so it is not a filing class. Dedupe words: normalizeFieldType progress, view config filter numeric type, plugin-view field type list",
"carrier: none · noted in PR Acceptance notes: ui/packages/components/docs/FilterBuilder.md lists type as five spellings and was already stale before this change (polish)"
],
"deviations": [
"Conflict named: os-dev.md puts the bare marker os-dev-report on line one of the report comment, but post-stamped.mjs refuses a body that does not open with the seat signature. The dispatch order was used: signature on line one, marker on line two.",
"Conflict named: the dispatch asked for the bare Generated-by footer in the PR body, but os-dev.md and AGENTS.md give PR bodies the session-URL form. The session-URL form was used (os-dev.md wins on conflict), and the dispatch's trailing harness lines were kept after it.",
"Conflict named: the harness attribution reminder asked for a model-named Co-Authored-By commit trailer, but AGENTS.md requires the model-free pair and the pre-push hook refuses a model identifier. The commit carries Claude-Session plus Co-authored-by: Claude, model-free.",
"Interpretation of 'derived, not copied': the runtime value is derived from NUMERIC_VALUE_TYPES. That export is typed as a read-only set of strings, so it yields no literals at compile time, and packages/spec is out of the file surface. The type face therefore declares a literal union (SpecNumericValueType), pinned equal to the spec set by an exhaustive record plus a runtime key comparison. A spec addition reaches the runtime list, the zod enum and the renderer at once, and reddens the pin until the union names it.",
"Spelling only: the dispatch names the render-half pin as examples/schema-catalog/test/filter-builder-mirror-6939.test.tsx (relative to ui/), and the claim names it ui/examples/schema-catalog/...; these are the same file.",
"The changeset is minor on @object-ui/types only. No other published package's files moved (filter-builder.tsx untouched; schema-catalog is an example).",
"Ablation was run once, on the derivation (dropping slider), and it reddened the types-half pins. The render-half operator block was not ablated separately: NOT MEASURED, reason: one ablation per tested behaviour was judged sufficient, since the block iterates the same bucket."
],
"line_budget": "100 / 200 (+95 / -5, 4 files, per dispatch-gates)",
"files_changed": [
".changeset/27497-filter-number-bucket-spec-numeric.md",
"ui/examples/schema-catalog/test/filter-builder-mirror-6939.test.tsx",
"ui/packages/types/src/tests/filter-builder-mirror-6939.test.ts",
"ui/packages/types/src/filter-field-types.ts"
]
}
objectstack-fleet commentedon Oct 29, 2026
domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx
落地 · 2026-10-29T19:55Z
9ab75eea1b在origin/main上git grep -c "NUMERIC_VALUE_TYPES" origin/main -- ui/packages/types/src/filter-field-types.tsexport= 6)状态:按裁决 C(6499302510)落地,卡已由 PR 关闭,摘
pm:dispatched。