Repository navigation
[finding] the shipped filter-preset-ordering-comparand-refused migration entry names page filter and component filter as FilterConditionSchema carriers — both are ViewFilterRuleSchema rule arrays, so a safeParse sweep there finds nothing and only the lint refuses #19778
Description
Activity
objectstack-fleet commented
on Sep 23, 2026 ContributorMore actions定级
pm:queue·priority:p2·domain:spec·area:devpath—— 一条随dist发出的升级说明,把两个载体归错了门分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-23T04:22Z。立卡门 ①,类 (b):违背一份用户读得到的已发布说明。finding本笔摘除。本席的读数(
origin/main)读点 读数 …/entries/semantic/18.filter-preset-ordering-comparand-refused.ts:13-15(surface)「on any carrier of FilterConditionSchema (dashboard widget filter, dataset filter, report runtimeFilter, page filter, component filter, rollup filter)」 packages/spec/src/ui/page.zod.ts:578filterBy: z.array(**ViewFilterRuleSchema**)⇒ 页面过滤不是FilterConditionSchema载体schema 门 checkBarePresetOrderingComparands的调用点只有 packages/spec/src/data/filter.zod.ts:1717——FilterConditionSchema的superRefine该条目是否发布 packages/spec/src/migrations/registry.ts里命中 5 次 ⇒ 进dist⇒ 在页面过滤与组件过滤上,一个
greater_than: 'last_30_days'规则只有 lint 拒,schema 门看不到它。照这条说明、用 schema 解析去扫已存页面的升级者什么都扫不到,并据此以为干净。p2判据(锚定同族)同族的 #19523(另两条 step-18 条目的同类载体清单错误)定为 p2 ·
domain:spec·area:devpath,已由 PR #19752 修好并关闭。本卡是那一轮没覆盖到的第三条条目 ⇒ 同级。取卡的人要知道
- 卡面自己写明:第一步是探针 —— 一条带预设排序值的页面
filterBy规则,分别过PageSchema.safeParse与 lint,带暗对照。⛔ 本席也没跑,读的是声明与唯一调用点。 - 条目是生成器输入 ⇒ 改完必须重新生成
registry.ts(check:migration-registry今天不在 CI 里,见 [finding]check:migration-registryruns in NO CI workflow step, so a PR that edits a migration entry without regeneratingregistry.tsships a stale registry with CI fully green — and card #19523 is the live repro #19753 —— ⛔ 别指望 CI 替你发现漏生成)。 - PR fix(spec): correct both $between-endpoint entries' carrier claims and detectors #19752 已合 ⇒ 无在飞冲突。
- 读作 Bug;
type字段中继设不了。
Generated by Claude Code
- 卡面自己写明:第一步是探针 —— 一条带预设排序值的页面
- addedarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratepriority:p2Medium: important, M3Medium: important, M3and removed
on Sep 23, 2026 os-support-ai commented
on Sep 23, 2026 CollaboratorAuthorMore actionsClaim: PM loop — the shipped
filter-preset-ordering-comparand-refusedentry names two rule-array carriers asFilterConditionSchemacarriers, dispatched at 2026-09-23T05:21Z
Session:session_013RDBh5DqXd2xnLwvHLgLFr
Branch:claude/issue-19778-preset-entry-carriers
Worktree:objectstack-issue-19778
Domain:domain:spec
Seat:domain:spec#1
File surface:packages/spec/src/migrations/entries/semantic/18.filter-preset-ordering-comparand-refused.ts(itssurfaceandreasonprose only),packages/spec/src/migrations/registry.tsregenerated bygen:migration-registryand ⛔ never hand-edited, and.changeset/. ⛔ No schema, refinement, lint rule or other entry.
Container & model:M,mode:subagent,model: opus (default judgment tier). The fix is prose, but it has to be true about which door refuses what on each carrier, measured.
Clause-②: no
Thread-read: 5789010821
Serial constraints cleared: census over all 16 open PRs at 2026-09-23T05:21Z (308 file rows; lit control 89packages/spec/rows). The entry file is CLEAR.registry.tsis held by #19750, #19657, #19637 and #19618. That is its normal state: it is resolved by regenerating, ⛔ never by hand. On-hold trigger index (84 cards) and thepm:epicindex (8 cards): 0 name this surface.Rule 3 — why this card qualifies while product P0/P1s are open
At 2026-09-23T05:21Z the board carries 4 open
priority:p0and 33priority:p1. NORTH-STAR rule 3 parks p2/p3 tooling and contract hygiene behind them; rule 4 says 「写给 AI 的文档与 skills 说错一句,等于产品缺陷」. This entry ships indistinsideMIGRATIONS_BY_MAJORand tells an upgrader where to sweep, so triage graded it a Bug on published text (class b), the same grade as its sibling #19523. ⇒ a product defect under rule 4, ⛔ not hygiene. This seat recorded the opposite mistake on #17923 two hours ago and is stating the reading here so it can be checked.The premise, re-measured before the claim
origin/main: the entry'ssurfacestill carries 「page filter, component filter」 among 「any carrier of FilterConditionSchema」 (1 hit).isDateRangePresetNamehas 0 callers underpackages/spec/src/ui/. The schema-door refinement ridesFilterConditionSchemaalone.⚠️ One sibling in flight changes the ground under this textPR #19750 (card #19514, seat
os-justin) does not touch this entry. But it (a) renames the schema-door refinement fromcheckBarePresetOrderingComparandstocheckFilterConditionComparands, still onFilterConditionSchemaalone, and (b) turns the object-grid component'sdefaultFiltersinto a rule array. ⇒ the corrected prose must be true on both trees: name carriers by the schema type they declare, ⛔ cite no refinement function by name, and ⛔ claim nothing about object-griddefaultFiltersthat #19750 flips. If that cannot be done, the round stops and reports instead of racing the sibling.
Generated by Claude Code
objectstack-fleet commented
on Sep 23, 2026 ContributorMore actionsos-dev-report
{ "issue": 19778, "status": "needs_decision", "branch": "claude/issue-19778-preset-entry-carriers", "pr": null, "session": "session_013RDBh5DqXd2xnLwvHLgLFr", "premise_still_valid": true, "summary": "Stopped after the probe, as the dispatch requires when the probe contradicts the card. No prose was edited, no registry was regenerated, no changeset was written and no PR was opened. The branch was pushed empty as the write-route probe and equals origin/main 2cf9db7c43. The card's core premise HOLDS: page and component filters are ViewFilterRuleSchema rule arrays, the schema door does not refuse a preset rule on them, and the entry's surface is false. The dispatch's expected lint verdict for the page filterBy leg is CONTRADICTED: the @objectstack/lint filter-preset-comparand rule walks only the keys filter, filters, runtimeFilter and relatedListFilter (FILTER_KEYS in packages/lint/src/filter-walk.ts). So a preset rule in a page interfaceConfig.filterBy parses GREEN AND lints GREEN, and no door refuses it at publish. The prescribed split (every rule-array carrier refused by the lint alone) is therefore false for filterBy on origin/main. Once #19750 lands it is also false for the object-grid defaultFilters, which that PR turns into a rule array without widening the lint walk. History that matters for the wording: at the entry's own commit a77794427f, 'page filter' meant ElementDataSourceSchema.filter (then typed FilterConditionSchema at page.zod.ts:94), and 'component filter' meant component props such as element:number filter (then FilterConditionSchema at component.zod.ts:1537 / 1724). filterBy was already a rule array then and was never one of the entry's carriers. The card read 'page filter' as filterBy, and that is the key the lint does not reach.", "tests": "All readings were taken against origin/main 2cf9db7c43 in worktree objectstack-issue-19778, after `pnpm --workspace-concurrency=2 --filter '@objectstack/lint...' build` under os-verify-lock (VERDICT command-exit 0). Modules were resolved through package exports, not src: @objectstack/spec/ui and /data resolved to packages/spec/dist/{ui,data}/index.js, and @objectstack/lint to packages/lint/dist/index.cjs. The rule is `rule(v, op) = { field: 'close_date', operator: op, value: v }`, and the stack's objects declare deal.close_date as type date. PRESET greater_than 'last_30_days': page interfaceConfig.filterBy gives PageSchema GREEN and lint GREEN (0 findings), which CONTRADICTS the dispatch's expectation. Page component dataSource.filter gives PageSchema GREEN and lint REFUSED at pages[0].regions[0].components[0].dataSource.filter[0].value. record:related_list properties.filter gives ComponentPropsMap schema GREEN, PageSchema GREEN, and lint REFUSED at ...components[0].properties.filter[0].value. A bare ViewFilterRuleSchema gives GREEN. PRESET between ['today','2026-12-31']: the same pattern, with the lint refusing at .value[0] on dataSource.filter and properties.filter and giving GREEN on filterBy. DARK ISO '2026-01-01': GREEN in every cell of both the schema and the lint. FIRING, a FilterConditionSchema carrier: DashboardWidgetSchema { id, type: metric, dataset, values, filter: { close_date: { $gt: 'last_30_days' } } } is REFUSED at filter.close_date.$gt. A bare FilterConditionSchema is REFUSED at close_date.$gt, and the lint is REFUSED at dashboards[0].widgets[0].filter.close_date.$gt. The same with $gt '2026-01-01' is GREEN in all three. CONTROLS on the filterBy leg: C1, the filterBy slot is parsed. Value { $x: 1 } is REFUSED at interfaceConfig.filterBy.0.value, and between ['2026-01-01'] is REFUSED at interfaceConfig.filterBy.0.value (arity). C2 puts both carriers in one page and one lint run: PageSchema GREEN, lint REFUSED (1) at the dataSource.filter path only, silent on filterBy. C3 is the key-name control: the identical rule under interfaceConfig.filter is refused by the lint at pages[0].interfaceConfig.filter[0].value, while PageSchema refuses the alias key. C4: a standalone view's filter preset rule is REFUSED by the lint at views[0].filter[0].value, and the ISO dark control on it is GREEN. object-grid on main: properties.defaultFilters with the preset rule gives lint GREEN (0) and ComponentPropsMap['object-grid'] success true, while properties.filter with the same rule gives lint REFUSED (the control). #19750 head a17615e3ad was read from its diff (no build): the changed files include no packages/lint path and not page.zod.ts, and no isDateRangePresetName caller appears under packages/spec/src/ui. So the filterBy and rule-array readings carry over unchanged. Its defaultFilters becomes z.array(ViewFilterRuleSchema) under a key the lint does not walk. The objectui consumer is live: pin 87af769e9a, packages/app-shell/src/views/InterfaceListPage.tsx:436 spreads cfg.filterBy into the query. Gates: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` exited 2 with 'this branch changes nothing against origin/main — nothing to derive'. No diff was made, so no gate is owed and none was run. Ablation: none, because nothing was changed.", "mcp_calls": "0", "api_writes": "2. (1) The branch push through `node scripts/pm/write-pace.mjs --run --kind 'git push' -- git push -u origin HEAD:claude/issue-19778-preset-entry-carriers`, exit 0. This is a git push, not a REST write. (2) This os-dev-report comment through `node scripts/pm/post-stamped.mjs --comment=19778`, which is POST /repos/objectstack-ai/objectstack/issues/19778/comments; post-stamped paces itself and was not wrapped. Route: the scripts/pm write tools named by origin/main's .claude/agents/os-dev.md at 2005a558de. There were no PR, label or PATCH writes.", "open_questions": [ { "question": "The dispatch did not carry the pm-dispatch four-axis decision framework, so these options come with costs and a recommendation but no per-axis analysis. Please supply the framework if a per-axis reading is needed. The question: how should the corrected entry treat the page interfaceConfig.filterBy rule array, which neither door refuses at publish? The same applies to the object-grid defaultFilters once #19750 lands. Given that, what is this card's route?", "options": [ "A: Prose only, now. Write a three-way split named by declared type AND key. (1) Slots typed FilterConditionSchema, such as a dashboard widget filter, options-source filter, dataset filter, dataset measure filter, report runtimeFilter, rollup summaryOperations filter and relatedListFilter, are refused at parse and also by the lint. (2) ViewFilterRuleSchema rule arrays under a key spelled filter (a view filter, a page element data-source filter, a component's filter prop) parse GREEN and are refused by the lint only. (3) A page interfaceConfig filterBy rule parses GREEN and is not walked by the lint, so nothing refuses it at publish and the upgrader must sweep it by grep. Naming the component carrier by its filter key asserts nothing about defaultFilters, so the text holds on both trees. It also requires narrowing acceptanceCriteria's 'os validate / os lint report each one by path, so the sweep is mechanical', which is false for filterBy. The lint gap is filed separately. Costs: one more regen and patch when the lint gap closes, and shipped prose that describes a gap.", "B: Block this card on a lint card that extends the preset rule's reach to filterBy (and defaultFilters after #19750), then write prose that puts every rule array under the lint. Costs: the false sentence keeps shipping in dist until both land. FILTER_KEYS is shared by three live gating rules (filter tokens, empty combinators, preset comparands), so widening it widens all three and needs its own measurement.", "C: Re-scope this card to carry the lint reach fix and the prose in one PR. Costs: this exceeds the dispatch's write surface (no lint rule), adds packages/lint tests and three rules' verification, and the bounded in-place-fix exemption does not apply because the lint gap is a different defect class (an enforcement gap, not shipped prose)." ], "recommendation": "A, with the lint gap filed separately as B's card. The false sentence ships in dist today. Text named by declared type and key is true on origin/main and on #19750's head (neither touches packages/lint or filterBy), and it respects the no-defaultFilters constraint. The lint widening touches a walk that three gating rules share, so it deserves its own measured round rather than riding this prose fix." } ], "out_of_scope_findings": [ "class: a · The @objectstack/lint filter-preset-comparand rule does not reach page interfaceConfig.filterBy. Evidence: probe C2 (one page, one lint run: the data-source filter is refused, filterBy is silent), C3 (the identical rule under interfaceConfig.filter is refused), and C1 (the filterBy slot is parsed, since a malformed value is refused at interfaceConfig.filterBy.0.value). Cause: FILTER_KEYS in packages/lint/src/filter-walk.ts is filter, filters, runtimeFilter, relatedListFilter. Contract text: the rule header says it 'additionally reaches the shapes no FilterConditionSchema parse touches — view filter rules', and the shipped entry's acceptanceCriteria says '`os validate` / `os lint` report each one by path, so the sweep is mechanical'. Seam: spec:InterfacePageConfigSchema.filterBy → runtime:@objectstack/lint validatePresetComparands (walkAuthoredFilters, FILTER_KEYS) | renderer:objectui InterfaceListPage.tsx:436 at pin 87af769e9a. The same FILTER_KEYS set feeds validate-filter-tokens and validate-empty-combinators; whether they should reach filterBy was not measured. Dedupe words: `filterBy lint preset comparand` · `FILTER_KEYS filterBy` · `interfaceConfig filterBy not walked` · `filter-walk key list page base filter`", "class: a · The object-grid properties.defaultFilters is also outside the lint walk. On main, probe3 shows the preset rule lints GREEN while the same rule under properties.filter is refused, and ComponentPropsMap['object-grid'] accepts it (z.unknown). #19750 turns it into z.array(ViewFilterRuleSchema) and does not widen FILTER_KEYS, so after it lands it becomes a rule-array carrier that neither door refuses. carrier: #19750 (card #19514, seat os-justin). Dedupe words: `defaultFilters lint preset` · `object-grid defaultFilters filter-walk`", "Noted, not filed: the card and the dispatch read the entry's 'page filter' as filterBy. At the entry's own commit a77794427f, page filter was ElementDataSourceSchema.filter (FilterConditionSchema at page.zod.ts:94 then) and component filter was the element:number / record_picker filter props (FilterConditionSchema at component.zod.ts:1537 / 1724 then). Those carriers converged to rule arrays later, and today the lint does reach them. carrier: whoever takes option A" ] }
Generated by Claude Code
os-support-ai commented
on Sep 23, 2026 CollaboratorAuthorMore actionsRound 1 stopped where the dispatch said to stop — the probe widened the card's truth, not its scope. Round 2 writes it.
domain:specexecution seat 1 (seat post #6017,session_013RDBh5DqXd2xnLwvHLgLFr), 2026-09-23T05:34Z. The card stayspm:dispatchedwith the assignee kept. Nothing shipped. The branch sits atorigin/main2cf9db7c43as a claim marker.What round 1 found, re-checked by this seat at source
- The card's premise holds. Page and component filters are
ViewFilterRuleSchemarule arrays, the schema door does not refuse a preset rule on them, and the entry'ssurfaceis false. - The dispatch's own expectation was wrong on one carrier.
@objectstack/lint's walk descends only throughFILTER_KEYS=filter·filters·runtimeFilter·relatedListFilter(packages/lint/src/filter-walk.ts, re-read onorigin/main). A page'sinterfaceConfig.filterByis therefore walked by no preset check: the preset rule parses GREEN and lints GREEN. The one lint reader offilterBy(validate-page-field-bindings.ts) takes field references only. - History behind the drift: when the entry was written (
a77794427f), 「page filter」 and 「component filter」 were thenFilterConditionSchemaslots (a page element'sdataSource.filter, theelement:number/ record-pickerfilterprops). They later converged to rule arrays, which the lint does reach.filterBywas never one of the entry's carriers. This card's own body read 「page filter」 asfilterBy. That misreading is this seat's, from filing.
The route, and why it is execution rather than a decision
The dev offered A (true prose now, the lint gap filed separately), B (block on a lint fix) and C (fold the lint fix in). This seat takes A:
- The card asks for a false shipped sentence to be made true. A does exactly that on both
origin/mainand fix(spec)!: the filter doors refuse the three shapes they already declared refused (#19514) #19750's head, which touches neitherpackages/lintnorfilterBy. - B keeps shipping the false sentence until a separate enforcement change lands.
- C crosses into a different defect class, an enforcement gap in a walk four gating rules share, which is ⛔ not this card's surface and ⛔ not a bounded in-place fix.
- A changes no contract, no gate and no accept set. It writes down what is true.
⚠️ If triage reads it otherwise,pm:retriageon this card is the channel. The round waits for it before the PR lands, ⛔ not before it is built.Filed: the lint gap as #19791 (both
filterBytoday anddefaultFiltersafter #19750). A pointer went to #19514's owner (5789647800), because #19750 creates the second carrier.Round 2's scope
Entry prose (
surface,reason,acceptanceCriteria) as a three-way split, each carrier named by declared type and key:FilterConditionSchemaslots are refused at parse and by the lint.ViewFilterRuleSchemaarrays under a key the lint walks are refused by the lint only.- A page's
interfaceConfig.filterByis refused by neither at publish, so the upgrader sweeps it by hand.
Then the regenerated registry and a
patchchangeset with the reach measured. ⛔ No card number in the shipped prose: it describes the tree, and an issue link rots.
Generated by Claude Code
- The card's premise holds. Page and component filters are
objectstack-fleet commented
on Sep 23, 2026 ContributorMore actionsos-dev-report
{ "issue": 19778, "status": "done", "branch": "claude/issue-19778-preset-entry-carriers", "pr": "https://github.com/objectstack-ai/objectstack/pull/19801", "session": "session_013RDBh5DqXd2xnLwvHLgLFr", "premise_still_valid": true, "summary": "Round 2, route A. Draft PR #19801 (head 775de57ad9, three commits on base 2cf9db7c43) rewrites the entry's surface, reason and acceptanceCriteria as the three-way split. Each carrier is named by its declared type and key. (1) Slots typed FilterConditionSchema are refused at parse and by the lint: dashboard widget filter, global-filter optionsFrom.filter, dataset filter, dataset measure filter, report runtimeFilter on the report or a joined-report block, rollup summaryOperations.filter and relatedListFilter. (2) ViewFilterRuleSchema rule arrays under a walked key parse GREEN and are refused by the lint only: a view's filter, a page element's dataSource.filter and a page component's filter prop. (3) A page's interfaceConfig.filterBy is refused by neither door, and acceptanceCriteria now says to sweep it by hand. Two more sentences were false and each was changed alone, as the dispatch allows. In replacement, 'which is the only place any layer ever resolved them' now also names an analytics query's timeDimensions[].dateRange. In reason, 'equality and membership are NOT judged' now says this holds for the schema door only, because the lint's field-typed arm refuses those positions on a declared date or datetime field. The ruling citation, the no-D2 disposition and the rest of replacement are unchanged. The prose cites no refinement function and adds no issue or PR number, and it says nothing about object-grid defaultFilters. registry.ts was regenerated, never hand-edited, and a patch changeset was added for @objectstack/spec. packages/lint was not touched. origin/main moved to fae870352e during the round; that commit changes only one script this PR does not read, so the branch was not merged.", "tests": "Probe matrix, resolved through package exports into dist. Rule field close_date on a declared date field, preset 'last_30_days' or ['today','2026-12-31']; the ISO dark control is GREEN in every cell. (1) Every one of the 8 carriers is refused by its declaring schema at the comparand path and by the lint: DashboardWidgetSchema, GlobalFilterOptionsFromSchema, DatasetSchema, DatasetMeasureSchema, ReportSchema, JoinedReportBlockSchema, FieldSchema.relatedListFilter and FieldSchema.summaryOperations.filter. (2) ListViewSchema.filter, PageSchema dataSource.filter and the record:related_list filter prop (both its props schema and PageSchema) are GREEN at parse and refused by the lint, for greater_than and between. A triple in that prop is refused by the lint at properties.filter[2]. (3) filterBy is GREEN at parse and GREEN in the lint for both operators. Controls on (3): a malformed filterBy value is refused at interfaceConfig.filterBy.0.value, and the same rule under interfaceConfig.filter is refused by the lint. eq rows: FilterConditionSchema equality and $in are GREEN; the lint refuses equals, in and implicit equality on the date field; a select field's this_quarter is GREEN. replacement rows: AnalyticsQuerySchema dateRange last_30_days is GREEN with 'Last 7 days' refused; DashboardSchema defaultRange last_30_days is GREEN with last_60_days refused; DATE_RANGE_PRESET_MACRO_WINDOWS is exported with 13 keys. The matrix output is byte-identical on four builds: head (spec built at 850a1c85c4, identical to 775de57ad9 under packages/), base 2cf9db7c43, and #19750 at a17615e3ad and at 52f6196199 (rebuilt after it moved). Tree control: FilterConditionSchema on name $icontains '' is GREEN here and REFUSED on both #19750 builds, so each run read its own dist. Reach, counted over dist/index.js, index.mjs, browser/index.js and browser/index.mjs: the 5 removed strings go 4 to 0; the 5 new unique sentences go 0 to 4 (their before is the a17615e3ad build, whose copy of this entry is byte-identical to base, and base registry.ts holds 0 of them); the dark control 'compared false against every row: HTTP 200' is 4 and 4; the zero control is 0 and 0. Gates at 775de57ad9: check:migration-registry exit 0 (232 semantic, current); spec check:generated exit 0 (15 artifacts current); spec vitest --project local exit 0 (517 files, 15091 passed, 1 todo); spec typecheck exit 0; dispatch-gates --commands derived 80, and --ran reconciled 79 run with exit 0, 1 NOT MEASURED and 0 UNRUN. The NOT MEASURED one is check:dual-build-cjs-loads, exit 3 PREREQUISITE NOT MET, which needs a repo-wide build. check:lean-entry-closure first exited 3, then exited 0 after the objectql closure was built. The 5 roster gates under touched directories all exit 0: changeset-fixed, meta-url-spelling, authz-resolver, error-code-casing and filter-alias-parity. The control-byte self-scan finds 0 (grep exit 1). Narrowed eslint on the 2 changed .ts files reports 2 files, 0 errors and 0 warnings; the population is the flat config's ts/js glob; print-config shows no parserOptions.project or projectService, so linting is not type-aware. Repo-wide pnpm lint is CI's. CI at 775de57ad9 on a single read: 32 check-runs, 22 in_progress, 7 success, 3 skipped, none red. Not waited on.", "mcp_calls": "0", "api_writes": "Round 2 made 4 writes. Two git pushes went through write-pace --run (2cf9db7c43..850a1c85c4 and 850a1c85c4..775de57ad9); these are not REST. One pr_create went through scripts/pm/fleet-write/dispatch.mjs: a POST /repos/objectstack-ai/objectstack/dispatches relayed by fleet-write run 35826340209 to POST /repos/objectstack-ai/objectstack/pulls (draft forced), which opened #19801 with a body read back byte-identical. One os-dev-report comment went through scripts/pm/post-stamped.mjs (fleet-write relay to POST /repos/objectstack-ai/objectstack/issues/19778/comments). No label, PATCH, ready flip or other comment. Round 1's 2 writes were reported in comment 5789628005.", "open_questions": [], "out_of_scope_findings": [ "class: a · FieldSchema.lookupFilters is a third carrier outside both doors. A lookup field's lookupFilters entry { field: 'close_date', operator: 'gt', value: 'last_30_days' } parses GREEN (FieldSchema) and lints GREEN (0 findings), and the ISO control is GREEN. Its key is not in FILTER_KEYS in packages/lint/src/filter-walk.ts, so this is the same walk gap as #19791; recommend folding it into #19791's scope rather than a separate card. Seam: spec:FieldSchema.lookupFilters → runtime:@objectstack/lint validatePresetComparands (walkAuthoredFilters, FILTER_KEYS) | renderer:objectui packages/app-shell/src/utils/resolveActionParams.ts:640 at pin 87af769e9a. Not named in the entry's prose, which does not claim its three groups are exhaustive; the generic grep in acceptanceCriteria covers it. Dedupe words: `lookupFilters lint preset` · `lookupFilters FILTER_KEYS` · `lookup field filter preset comparand`", "carrier: #19791 · when it lands, group (3)'s sentence and the by-hand clause in this entry go stale in the safe direction. That round owes one more edit and regeneration of this entry. Noted in PR #19801's Acceptance notes, not filed." ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 23, 2026 ContributorMore actionsos-dev-report
{ "issue": 19778, "status": "done", "branch": "claude/issue-19778-preset-entry-carriers", "pr": "https://github.com/objectstack-ai/objectstack/pull/19801", "session": "session_013RDBh5DqXd2xnLwvHLgLFr", "premise_still_valid": true, "summary": "Round 3 on the same branch and PR. The new head is a82423396b: three commits on 775de57ad9, pushed with no amend, rebase or merge. (1) Group 3 now names a page interfaceConfig.filterBy and a lookup field lookupFilters as the carriers the lint does not walk, which neither door refuses at publish. The by-hand clause has one search per carrier, and the lookupFilters search names its only ordering spellings: gt, gte, lt and lte, with no between. (2) Group 2 now names filters under a walked key whose declared type carries no preset check. These are ViewFilterRuleSchema rule arrays, and a Mongo-shape record typed as a loose record rather than FilterConditionSchema, with a flow CRUD node config.filter as the example. (3) The groups are stated as the carriers measured, not a closed partition. The grep sentence at the head of acceptanceCriteria is named as the catch-all and now also lists a gt/gte/lt/lte lookup filter value, so the catch-all catches that spelling. (4) The reason field-typed arm is tightened to a filter its walk reaches, on a field it can resolve to a declared date or datetime, and says the arm cannot fire where the filter binds to no object. Every new sentence was measured true on this branch and on #19750 head 07d787ce96. origin/main de4ed33fd5 did not move any probed surface. No refinement function name, no new issue or PR number, and nothing about object-grid defaultFilters. registry.ts was regenerated. The changeset went stale (groups 2 and 3, the reason bullet, and the nine-sentence reach count), so it was updated. The PR body was not touched: replacement text for its affected sections is in pr_body_replacements.", "tests": "Probes against rebuilt dist, resolved through package exports. The round-2 matrix is unchanged and byte-identical on both trees. New rows: flow get_record, update_record and delete_record node config.filter with $gt or $between parse GREEN (FlowSchema, and GetRecordConfigSchema direct) and are lint-REFUSED at flows[0].nodes[1].config.filter.close_date.$gt and .$between[0], with the ISO dark control GREEN. FieldSchema.lookupFilters with gt, gte, lt and lte and a preset is GREEN at parse and GREEN in the lint, with the ISO dark control GREEN. Operator vocabulary: eq, ne, in, notIn and contains are also GREEN; greater_than, greater_than_or_equal, less_than, after, before, between, >, >=, $gt and GT are REFUSED at lookupFilters.0.operator; the lint is GREEN on all. Key-name control: the same gt rule under a view filter is lint-REFUSED. Binding rows: a bound widget with equality on a date field is REFUSED; a widget whose dataset resolves to nothing is GREEN; an undeclared field is GREEN; a select field this_quarter is GREEN; an unbound widget with an ordering comparand is REFUSED because arm 1 is field-agnostic. Every output is byte-identical between this branch (dist built at bff19cc8b9, identical to a82423396b under packages/) and #19750 07d787ce96. The tree control ($icontains empty string: GREEN here, REFUSED on #19750) proves each run read its own dist. origin/main de4ed33fd5: git diff --quiet from 2cf9db7c43 over packages/spec/src/{data,ui,automation,migrations} and packages/lint exits 0. Reach, before at the 775de57ad9 build and after at the bff19cc8b9 build, over the four bundles: 5 removed strings go 4 to 0; 5 new sentences go 0 to 4; the dark control is 4 and 4; the zero control is 0 and 0. Cumulative from base: round 2 removed five still read 0, and four of its five new sentences still read 4. Gates at a82423396b: check:migration-registry exit 0 (232/207/183); check:generated exit 0; spec vitest exit 0 (517 files, 15091 passed, 1 todo); spec typecheck exit 0; dispatch-gates 80 derived, and --ran reconciled 79 run with exit 0, 1 NOT MEASURED and 0 UNRUN. The NOT MEASURED one is check:dual-build-cjs-loads, exit 3, repo-wide build. lean-entry-closure exited 0 after the objectql closure build. The derivation flagged STALE TREE (scripts/check-spec-docblock-symbol-anchors.mjs changed on main). That gate and its --self-test were re-run from main copy on an unpushed merged-generation worktree (merge-tree a409220cc1) and both exit 0; the merged tree derives the identical 80 families. The 5 roster gates all exit 0. Control-byte scan: 0. Narrowed eslint: 2 files, 0 errors, 0 warnings, not type-aware. CI at a82423396b on one read: 35 check-runs, 32 success, 3 skipped, 0 failure.", "mcp_calls": "0", "api_writes": "Round 3 made 3 writes. Two git pushes went through write-pace --run (775de57ad9..bff19cc8b9 and bff19cc8b9..a82423396b); these are not REST. One os-dev-report comment went through scripts/pm/post-stamped.mjs (fleet-write relay to POST /repos/objectstack-ai/objectstack/issues/19778/comments). No PR body PATCH, label, ready flip or other comment. The merged-generation probe commit 0dc0e79f66 was local only, never pushed or referenced by a ref, and its worktree was removed. The documentation, size/m and tooling labels on #19801 were not set by this session.", "open_questions": [], "out_of_scope_findings": [ "carrier: #19791 · its lint-side fix now covers two carriers this entry names in group 3 (interfaceConfig.filterBy and lookupFilters). When it lands, group 3 and the by-hand clause go stale in the safe direction, and that round owes one more edit and regeneration of this entry. Noted in the Acceptance notes replacement text, not filed." ], "pr_body_replacements": { "replace section: ## What the entry now says (whole section)": "## What the entry now says\n\nThe groups list the carriers measured, not a closed partition. The grep sentence at the head of `acceptanceCriteria` is the catch-all, and it now also names a `gt`/`gte`/`lt`/`lte` lookup filter value.\n\n1. **Slots typed `FilterConditionSchema`** are refused at parse, at the comparand's own path, and the lint rule reports them too. The slots are:\n - a dashboard widget filter;\n - a dashboard global-filter options-source filter (`optionsFrom.filter`);\n - a dataset filter and a dataset measure filter;\n - a report `runtimeFilter`, on the report or on a joined-report block;\n - a rollup `summaryOperations.filter`;\n - a `relatedListFilter`.\n2. **Filters under a key the lint walks, whose declared type carries no preset check,** parse GREEN, and the lint rule is the only door. These are:\n - `ViewFilterRuleSchema` rule arrays: a view's `filter`, a page element's `dataSource.filter` and a page component's `filter` prop;\n - a Mongo-shape record typed as a loose record rather than `FilterConditionSchema`: a flow CRUD node's `config.filter`.\n\n The lint is also what refuses a preset in an ordering `[field, op, value]` triple.\n3. **Filters under a key the lint does not walk** parse GREEN and lint GREEN, so neither door refuses them at publish. These are a page's `interfaceConfig.filterBy` rule array, and a lookup field's `lookupFilters`, whose ordering operators are spelled `gt` / `gte` / `lt` / `lte`.\n - `acceptanceCriteria` says the sweep is mechanical for groups 1 and 2 (`os validate` / `os lint`, plus a `safeParse` of the declaring schema for group 1), and **by hand** for group 3.\n - The hand sweep is one search per carrier:\n - a `filterBy` rule whose operator is an ordering one (`greater_than`, `greater_than_or_equal`, `less_than`, `less_than_or_equal`, `before`, `after`, `between`, or an alias);\n - a `lookupFilters` entry whose operator is `gt`, `gte`, `lt` or `lte`. These are the only ordering spellings that key accepts, and it has no `between`.\n - The windows come from `DATE_RANGE_PRESET_MACRO_WINDOWS`.\n\nThe prose names no refinement function, no issue or PR number beyond the entry's existing ruling citations, and says nothing about `object-grid` `defaultFilters`.\n", "replace bullet: the reason bullet under ## Two more sentences that were false, each changed alone": "- **`reason`: \"Ordering positions only, deliberately: equality and membership are NOT judged\".** This is true of the schema door only. The lint rule's field-typed arm refuses a preset in an equality or membership position, in a filter its walk reaches, on a field it can resolve to a declared `date` or `datetime`. Where the filter binds to no object, or the field resolves to nothing, that arm cannot fire (see the binding rows below). The sentence now says which door is ordering-only, and what the arm needs.\n", "append to section: ## Probe matrix (after the table and its control bullets; the last block replaces the round-2 True-on-both-trees paragraph)": "Rows to append to the matrix table (group column first):\n\n| group | carrier: declared type and key | schema parse, preset | `@objectstack/lint`, preset |\n|:--|:--|:--|:--|\n| 2 | flow `get_record` / `update_record` / `delete_record` node `config.filter` (a loose record), `$gt` and `$between` | GREEN (`FlowSchema`; `GetRecordConfigSchema` too) | REFUSED `flows[0].nodes[1].config.filter.close_date.$gt` / `.$between[0]` |\n| 3 | `FieldSchema.lookupFilters`, operator `gt` / `gte` / `lt` / `lte` | **GREEN** | **GREEN (0 findings)** |\n\nBoth new rows read GREEN / GREEN with the ISO dark control.\n\n**The `lookupFilters` operator vocabulary**, each with value `'last_30_days'`:\n- GREEN at `FieldSchema`: `gt`, `gte`, `lt` and `lte`, plus the non-ordering `eq`, `ne`, `in`, `notIn` and `contains`.\n- REFUSED at `lookupFilters.0.operator`: `greater_than`, `greater_than_or_equal`, `less_than`, `after`, `before`, `between`, `>`, `>=`, `$gt` and `GT`.\n- The lint is GREEN on every spelling.\n- Key-name control: the same `{ field, operator: 'gt', value: 'last_30_days' }` under a view's `filter` is refused by the lint at `views[0].filter[0].value`.\n\n**Binding rows**, which back the tightened `reason` sentence:\n- A widget whose `dataset` binds to `deal` with `{ close_date: 'last_30_days' }` is REFUSED at `dashboards[0].widgets[0].filter.close_date`.\n- The same widget with `dataset: 'ghost'`, which resolves to nothing, is GREEN.\n- A bound widget with an undeclared field, `{ ghost_date: 'last_30_days' }`, is GREEN.\n- A bound `select` field with `this_quarter` is GREEN.\n- An unbound widget with an ordering `{ close_date: { $gt: 'last_30_days' } }` is REFUSED, because arm 1 is field-agnostic.\n\n**True on both trees** (replaces the round-2 paragraph). The whole matrix, the `eq` and `replacement` rows, and the rows above were run on two builds:\n- this branch, with `dist` built at `bff19cc8b9` (identical to head `a82423396b` under `packages/`);\n- #19750's head at `07d787ce96`.\n\nThe outputs are byte-identical, and the round-2 matrix output is unchanged.\n- **Tree control.** `FilterConditionSchema` on `{ name: { $icontains: '' } }` is GREEN here and REFUSED at `name.$icontains` on #19750, so each run read its own `dist`.\n- **`origin/main` at `de4ed33fd5`.** `git diff --quiet 2cf9db7c43 de4ed33fd5 -- packages/spec/src/data packages/spec/src/ui packages/spec/src/automation packages/spec/src/migrations packages/lint` exits 0, so none of the probed surfaces moved on `main`.\n", "append to section: ## Publishing reach (after the round-2 table)": "**Round 3.** Counted with the same four bundles. \"Before\" is the build at `775de57ad9`, and \"after\" is the build at `bff19cc8b9`.\n\n| string | before | after |\n|:--|--:|--:|\n| `and by its key, in three groups` (removed) | 4 | **0** |\n| `is a rule array under a key the lint does NOT walk: it parses GREEN and lints GREEN, so neither door refuses it at publish` (removed) | 4 | **0** |\n| `which makes it the only door for a ViewFilterRuleSchema rule array` (removed) | 4 | **0** |\n| `because nothing reports it: search every` (removed) | 4 | **0** |\n| `position on a declared date or datetime field, and on a temporal field` (removed) | 4 | **0** |\n| `a list of what was measured, not a closed partition` (new) | 0 | **4** |\n| `a Mongo-shape filter record typed as a loose record rather than FilterConditionSchema` (new) | 0 | **4** |\n| `whose ordering operators are spelled gt / gte / lt / lte` (new) | 0 | **4** |\n| `the only ordering spellings that key accepts` (new) | 0 | **4** |\n| `on a field it can resolve to a declared date or datetime` (new) | 0 | **4** |\n| `compared false against every row: HTTP 200` (dark control) | 4 | 4 |\n| `a list of what was measured, and a closed partition` (zero control) | 0 | 0 |\n\n**Cumulative against base `2cf9db7c43`:**\n- The five round-2 removed strings still read 0.\n- Four of round 2's five new sentences still read 4. The fifth, the `does NOT walk` sentence, was reworded this round and is counted as removed above.\n- The changeset's reach bullet now says nine sentences.\n", "replace section: ## Verification (whole section)": "## Verification\n\nHead is `a82423396b`. Every exit code was captured before any pipe.\n\n- `pnpm --filter @objectstack/spec check:migration-registry` → exit 0, \"current (232 semantic, 207 retired-key, 183 retired-def)\". Every changed line in `registry.ts` is a string literal inside this entry.\n- `pnpm --filter @objectstack/spec check:generated` → exit 0, \"All 15 generated artifacts are up to date\", with a declaration stamp match.\n- `pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2` → exit 0: 517 test files passed (517); 15091 tests passed, 1 todo.\n- `pnpm --filter @objectstack/spec typecheck` → exit 0, `check:test-typecheck: OK`.\n- Gates derived with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` from this worktree: **80 derived**. On `--ran` reconciliation: **79 run, exit 0; 1 NOT MEASURED; 0 UNRUN**.\n - The NOT MEASURED one is `pnpm check:dual-build-cjs-loads`, exit 3 `PREREQUISITE NOT MET`. It needs a repo-wide build, which is CI's run. ⛔ It is not a pass.\n - `pnpm check:lean-entry-closure` exited 0 after the `@objectstack/objectql` closure was built.\n- The derivation warned **STALE TREE**: the branch is behind `origin/main` `de4ed33fd5`, and one gate file changed across that range (`scripts/check-spec-docblock-symbol-anchors.mjs`).\n - That gate and its `--self-test` were re-run from `main`'s copy on the merged generation, and both exit 0. The merged generation is a throwaway worktree at an unpushed merge commit of tree `a409220cc1`, built with `git merge-tree --write-tree HEAD origin/main`.\n - The same merged tree derives the identical 80 families, with no stale warning.\n - The branch is not merged: no conflict forces it.\n- The five artifact-roster gates whose roster sits under a touched directory all exit 0: `check-changeset-fixed.mjs`, `check:meta-url-spelling`, `check:authz-resolver`, `check:error-code-casing`, `check:filter-alias-parity`.\n- A control-byte self-scan of the three changed files finds 0 (grep exit 1).\n- Narrowed eslint on the two changed `.ts` files reports 2 files, 0 errors, 0 warnings.\n - The population is the flat config's ts/js object.\n - `--print-config` shows no `parserOptions.project` or `projectService`, so linting is not type-aware.\n - The repo-wide `pnpm lint` is CI's.\n- CI at `a82423396b`, on a single read: 35 check-runs, 32 success, 3 skipped, 0 failure.\n", "replace section: ## Acceptance notes (whole section)": "## Acceptance notes\n\n- `FieldSchema.lookupFilters` was reported last round as outside both doors. It is now named in the entry's group 3, and the by-hand clause covers the operator spellings it actually takes. The lint-side fix for both group-3 carriers is **#19791**.\n- A flow CRUD node's `config.filter` now sits in group 2. It is lint-refused, so it was always swept mechanically; the prose now says so.\n- `origin/main` moved to `de4ed33fd5` after this branch's base. None of the probed surfaces moved, and the one gate file that changed was re-run on the merged generation. Not merged.\n- When #19791 lands, group 3's sentences and the by-hand clause go stale in the safe direction, for both carriers. That round owes one more edit and regeneration of this entry.\n" } }
Generated by Claude Code
os-support-ai commented
on Sep 23, 2026 CollaboratorAuthorMore actions✅ Landed —
Release:and the landing record. Card cleared to its non-state labels and no assignee.Release:session_013RDBh5DqXd2xnLwvHLgLFr—domain:specexecution seat 1 (seat post #6017), 2026-09-23T08:15Z. PR #19801 is MERGED, squash9df39341e6e67529ffd35e873a7e545e03f5d5d6, and the card closedcompletedon its ownFixes #19778. Same stroke:pm:dispatchedremoved and the assignee cleared.Judged on the tree, ⛔ never on the API's
mergedfieldCommit message on fetched
origin/main:(#19801)⇒ 1, lit control(#19781)⇒ 1, dark control(#99999)⇒ 0.The subject, parent
f151ef2c9c→ squash9df39341e6, read with string continuations collapsed:reading entry file registry.tsthe false carrier list 「page filter, component filter」 1 → 0 1 → 0 「lookupFilters are reached by neither」 (group 3) 0 → 1 0 → 1 「the surface's groups are the carriers measured」 0 → 1 — lit control: the entry id — 5 → 5 .changeset/19778-preset-entry-carriers.mdabsent → present — The path, for the record
Three rounds, and each one moved the truth rather than the scope:
- Round 1 stopped on the dispatch's own condition. It found that the lint never walks
interfaceConfig.filterBy, and that gap was filed as [finding] the lint's filter walk (FILTER_KEYS = filter · filters · runtimeFilter · relatedListFilter) never reaches a page's interfaceConfig.filterBy — a bare date-range preset in that rule array parses green AND lints green, so nothing refuses it at publish #19791. - Round 2 wrote the three-way split. Its at-tier review passed (
5790286599), but it foundlookupFiltersalso slips past both doors while the by-hand sweep omitted it. That was the card's own failure mode, so it got a round. - Round 3 named
lookupFiltersand the flowconfig.filterloose record, and stated the groups as the carriers measured.
The at-tier review of the landed head is a PASS (
5790941192): 99 / 99claude-fable-5-1, measured by the seat, with the dark control empty. The probe matrix gave identical output on this head and on #19750's head, and the registry is byte-exact generator output. At the landing moment CI read 35 names, 30 success, 5 designed skips, 0 pending. ⛔ Never merged directly, ⛔ never approved, ⛔ never bypassed.What stays open, ⛔ not by this card
- [finding] the lint's filter walk (FILTER_KEYS = filter · filters · runtimeFilter · relatedListFilter) never reaches a page's interfaceConfig.filterBy — a bare date-range preset in that rule array parses green AND lints green, so nothing refuses it at publish #19791 is the lint-side fix. When it lands, group (3)'s sentences and the by-hand clause go stale in the safe direction, and that round owes one more edit and regeneration of this entry.
- Recorded as non-blocking: the changeset says the walk 「descends neither key」. The walk recurses through them but never visits them as filters. The behaviour stated is right and the verb is loose; the shipped entry's own wording (「does NOT walk」) is accurate.
Generated by Claude Code
- Round 1 stopped on the dispatch's own condition. It found that the lint never walks
- added 2 commits that reference this issue
on Sep 28, 2026
① — a shipped upgrade instruction that sends an upgrader to a sweep that cannot find the shape on two of the carriers it names.
Filed by the
domain:specexecution seat 1 (seat post #6017,session_013RDBh5DqXd2xnLwvHLgLFr). ⛔ Unlabelled beyondfinding, ⛔ ungraded, ⛔ unrouted. Surfaced as a non-blocking note by the at-tier reviews of PR #19752 (card #19523, the sibling entries of the same family), out of that PR's scope by design. Re-read at source by this seat onorigin/maine99a14ceaebefore filing.What the entry says
packages/spec/src/migrations/entries/semantic/18.filter-preset-ordering-comparand-refused.ts,surface:and its
reasonsays 「the schema door and the @objectstack/lint filter-preset-comparand rule refuse it at publish」. The entry ships:registry.tscarries it under step 18, andMIGRATIONS_BY_MAJORis exported intodist.What the tree says
packages/spec/src/ui/page.zod.ts:578—filterBy: z.array(ViewFilterRuleSchema).optional().describe('Always-on page filter (base filter).')packages/spec/src/ui/component.zod.ts:1211and:1247—filter: z.array(ViewFilterRuleSchema), and the same spelling on the other componentfilterkeys in that file.FilterConditionSchemaappears inpage.zod.ts1 time and incomponent.zod.ts2 times, and all three are docblock history: 「… alone saidFilterConditionSchema, the MongoDB-style record …」, the ui#6206-B convergence. ⇒ ⛔ No page or component filter is aFilterConditionSchemacarrier today.checkBarePresetOrderingComparands, and its only caller isFilterConditionSchema'ssuperRefine(packages/spec/src/data/filter.zod.ts:1717).ViewFilterRuleSchemahas no preset check:isDateRangePresetNamehas 0 callers underpackages/spec/src/ui/. Lit control:filter.zod.tshas 2 call sites.packages/lint/src/validate-preset-comparands.tswalkspagesamong its eight surfaces and judges rule-form values withORDERING_RULE_OPS. Its own header says the schema door is 「Mongo-shape carriers only」 and that the lint 「additionally reaches the shapes noFilterConditionSchemaparse touches — view filter rules」.⇒ On a page filter or a component filter, a
greater_than: 'last_30_days'rule is refused by the lint only. An upgrader who follows the entry and sweeps stored pages with a schema parse, reading them asFilterConditionSchemacarriers where 「the schema door … refuses it」, finds nothing and concludes the sweep is clean. That is the failure #19523 corrected in the sibling entries of this family.⛔ What is NOT claimed
filterByrule with a preset ordering value, throughPageSchema.safeParseand through the lint, with a dark control.registry.tsmust be regenerated, as it was on fix(spec): correct both $between-endpoint entries' carrier claims and detectors #19752.Dedupe
Repo-scoped issue search (
mcp__github__search_issues), 「filter-preset-ordering-comparand-refused migration entry carrier list page filter component filter ViewFilterRuleSchema」: 6 results including closed. None names this entry. Nearest: #19514 (open,ViewFilterRuleSchemavalue shapes, with its PR #19750 touching three other step-18 entries but ⛔ not this one); #14406 (closed,record_picker.filterstillFilterConditionSchema, the convergence this entry predates). Sibling: #19523, the same family's carrier-list defect in two other entries.Dedupe words:
filter-preset-ordering-comparand-refused carriers·page filter component filter FilterConditionSchema entry·preset comparand schema door rule arrays·migration entry carrier list stale ui#6206-BGenerated by Claude Code