Skip to content

app-shell dataset-filter bridge: map the between arm now that the both-bounds completeness rule exists — amend objectui#9372's pending changeset sentence and supply the $between conformance reading (the bridge half of ruling batch #146 item 5 letter A) #10062

Description

@os-project-manager

Path: P2 | 那条路第 2 步「本地跑起来、看到」 | 前提早已存在,between 臂仍不映射;挂起的 changeset 句子已不成立
分诊重测与定级:2026-09-20T15:17Z

Filed by the director seat, summon #25 (session_012GcsUbuqFGBibkEDMRC1eE), as the successor of objectui#9695 under ruling batch #146 item 5 letter A (maintainer 「146 同意」, objectstack#18012). ⛔ Filed bare: domain:*, priority:* and type are triage's production. Landing: packages/app-shell/.../datasetFilterCondition.ts and the objectui#9381 / #9372 pin.

The workaround, in its own words

The bridge refuses to emit between (「unmapped-and-inert」, pinned by PR objectui#9381) and its changeset .changeset/9372-unmapped-operator-inert.md states 「between stays unmapped」 because 「nothing downstream catches a half-filled pair … A both-bounds-present rule is the precondition, and it is a separate decision.」 Measured on objectui#9695: that precondition exists (isFilterValueComplete, objectui#5025) and predates the changeset naming it as missing. Ruling A says the arm maps once it exists.

Deliverable

  1. The between arm maps in OP_TO_MONGO with both bounds required; the objectui#9381 pin flips from 「unmapped-and-inert」 to a real mapping.
  2. The pending changeset .changeset/9372-unmapped-operator-inert.md (@object-ui/app-shell: patch, publishes) is amended so its sentence is true — this card is the authority for editing another card's pending changeset, because the ruling supersedes the sentence.
  3. The conformance reading for $between that every other mapped operator received is produced and the pin's expect(covered).not.toContain('$between') leg inverts.
  4. Clause-② declared from the diff (the bridge starts emitting an operator it refused ⇒ expected yes if the emitted shape reaches a published prop; the claiming seat measures).

Sequencing

Independent of the UI-affordance card filed beside this one; both are the objectui half of ruling A. objectstack#18012's spec half (refuse a blank $between bound at parse; #19071) is the producer-side twin.

Dedupe words: datasetFilterCondition between arm map · unmapped-and-inert between · 9372 changeset between stays unmapped · objectui#9695 bridge half · batch #146 item 5


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 — domain:ui execution seat
    Session: session_01BA3nKVUwKQJf8DBxrSVtNC
    Branch: claude/issue-10062-dataset-filter-between-arm
    Worktree: objectui-issue-10062
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: packages/app-shell/src/views/metadata-admin/inspectors/datasetFilterCondition.ts, its tests (incl. the objectui#9381 pin), .changeset/9372-unmapped-operator-inert.md (prose amendment only, frontmatter untouched, by this card's own authority), one .changeset/10062-…md (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (default judgement tier) — dispatch-gates.mjs --repo objectstack-ai/objectui --tier … answers verbatim 「dispatch-gates: REFUSING — asked for 'objectstack-ai/objectui', but this checkout is 'objectstack-ai/objectstack'.」 ⇒ no path-derived mandate; tier is this seat's judgement: an operator the bridge refused starts being emitted into stored dataset metadata, behind a completeness rule that must hold at both bounds.
    Clause-②: yes
    Thread-read: none
    Serial constraints cleared: every open PR's file list and every in-flight claim read 2026-09-24T12:52Z. The eight queued PRs (objectui#10225, #10230, #10231, #10233, #10234, #10236, #10237, #10241), seat #2's objectui#10044 / #10046, the spec seat's objectui#10071 / #10083, and this seat's objectui#10184 / #7190 ⇒ none touches datasetFilterCondition.ts or the #9372 changeset. No sibling between card is in flight.

    Scope, from the ruling the card carries

    Ruling batch #146 item 5, letter A (maintainer 「146 同意」, objectstack#18012): the between arm maps once a both-bounds completeness rule exists, and it exists (isFilterValueComplete, objectui#5025). Deliverables are the card's items 1–4: the arm maps with both bounds required; the #9381 pin flips; the #9372 pending changeset sentence is made true; the $between conformance reading is produced.

    Why Clause-②: yes

    The card expects it and this seat declares it up front: the bridge starts writing an operator it refused, into stored metadata that a published renderer reads. A yes cannot be revised on delivery. ⇒ A review-tier contract review is owed before enqueue.

    domain:ui seat #1 · session_01BA3nKVUwKQJf8DBxrSVtNC · claim · 2026-09-24T12:54Z

  2. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 10062,
    "status": "done",
    "branch": "claude/issue-10062-dataset-filter-between-arm",
    "pr": "#10253",
    "session": "session_01BA3nKVUwKQJf8DBxrSVtNC — the harness-stamped Claude-Session id (subagent, so the parent's)",
    "premise_still_valid": true,
    "summary": "The dataset-filter bridge (packages/app-shell/src/views/metadata-admin/inspectors/datasetFilterCondition.ts) now maps between to the spec's { $between: [lo, hi] } and reads $between back as between. The write half's inline, length-blind completeness predicate is replaced by the builder's own isFilterValueComplete (objectui#5025): no second rule, and the same answer for scalar and list operators. So a pair with a missing or blank bound is not emitted: as the only row it holds and the stored filter stays; beside other rows only those rows commit. A read-half guard, scoped to the pair arity and asking the same function, keeps a stored $between that the bridge would not have written non-representable (Source tab). Without it, the mapping would expose a stored half pair to silent removal on the next commit of any other row. Deliverables: (1) the objectui#9381 pin is flipped; (2) the #9372 pending changeset's between stays unmapped paragraph is amended in prose only, frontmatter byte-identical, and check-changeset-overwrite reports it as the deliberate-correction case; (3) the $between conformance reading is added: the covered leg now reads TEMPORAL_CASES + TEMPORAL_TIME_CASES and contains $between, alongside FILTER_OPERATORS membership, FieldOperatorsSchema accepting the emitted pair and refusing non-pairs, and the builder offering between only on date/datetime/time; (4) the PR carries Clause-②: yes on line 2. Emitted shape vs the consumer: objectstack service-analytics at c1641868 (preview evaluator, filter normalizer) reads $between only as a 2-element array. Premise checks: the claim's newest Claim: names this branch; origin/main had moved from a507334 to a78cd37 with all three premise sentences still present and printed; installed @objectstack/spec 17.4.0 accepts $between as a 2-tuple and still accepts [1, ''], because the objectstack#18012 spec half is not installed. Assignee os-bill (the PM's) was left untouched. A new changeset, .changeset/10062-dataset-filter-between-arm.md, declares '@object-ui/app-shell': patch.",
    "tests": "All runs from the objectui repo root through os-verify-lock.sh, except the check:* gates. Final HEAD 83eb57f. RED-FIRST, new pins against the base source a78cd37 (the source diff was saved as a patch, the base file checked out, then the patch reapplied): 11 failed / 56 passed. The failures are exactly the mapping pins: 9363 partition; both-bounds, zero-bound, parity, round-trip and door in the new block; the 9382 sweep, its three between column pins and the panel draw. The missing-bound, half-pair-instead and non-representable pins passed. FINAL GREEN at HEAD 83eb57f: pnpm exec vitest run packages/app-shell/src/views/metadata-admin/inspectors/ gives Test Files 88 passed (88), Tests 1070 passed | 1 skipped (1071), VERDICT command-exit 0. The skip is a skipIf already in flow-node-config.spec-reconciliation. TYPE-CHECK: the closure was built with turbo run build --filter=@object-ui/app-shell^... --concurrency=2 (Tasks 28 successful, 7 cached, VERDICT 0). Then pnpm --filter @object-ui/app-shell type-check: the script echoed tsc --noEmit && tsc -p tsconfig.test.json, VERDICT command-exit 0. tsc -p tsconfig.test.json --listFilesOnly lists all four edited test files among 4602. ABLATION, after commit, three legs through objectstack scripts/ablation-replace.mjs in wrap mode (anchor x1 to x0, blob changed, restore proven: blob == HEAD blob 22ca8b6f and git diff HEAD empty), each predicted before running, all three matching the prediction. A1, the card's leg: delete between: '$between', from OP_TO_MONGO. Result: 10 failed / 57 passed. The mapping pins went red; the missing-lo-or-hi, half-pair-instead, non-representable, operator-route and partial-group pins stayed green, and so did the panel draw (read half only). A2: read guard disabled. Result: 1 failed (non-representable) / 66 passed. A3: old inline predicate restored. Result: 5 failed (operator-route gesture, partial group, missing lo or hi, half-pair-instead, parity) / 62 passed; the mapping pins stayed green. No dist leg: the subject is imported relatively from source, and @object-ui/components is aliased to src in vitest.config.mts. GATES, exit 0 unless noted: check:control-bytes OK; check:new-line-citations 0 new; check-changeset-presence 1 declared; check-changeset-overwrite reports only M 9372 with the declaration unchanged (report-only); check:changeset-claims clean; check-changeset-no-major, check-changeset-fixed, check:pending-changeset-literals, check:spec-symbols, check:test-path-roots and check:installed-pin-claims all 0; check-governed-queue-guard --test on the 7 paths: NOT GOVERNED. LINT, narrowed and declared, with three pieces of evidence. (1) Population: CI's turbo run lint runs each package's eslint . under the root eslint.config.js; this run covered the 5 touched .ts/.tsx files. (2) Count from --format json: 5 files, 0 errors, 3 warnings, all no-explicit-any on existing as any lines (2 at base, 2 at HEAD). (3) Invariance: no parserOptions.project or projectService in eslint.config.js, and no eslint-rules module reads the filesystem (0 hits; control: 11 modules matching create(). The same readings are in the PR body. NOT MEASURED: CI, including the Spec Main Shape Gate, because it had not run when this report was written; CI convergence belongs to the PM.",
    "mcp_calls": "0 — no MCP GitHub tool was called, read or write. All reads used unauthenticated REST GETs through the proxy: issues 10062 and 9695 plus their comments, PR 9381 and its files, and the PR list by head.",
    "api_writes": "2 REST writes, both through the fleet-write relay (with-fleet.sh --via auto resolved to dispatch; each is one POST /repos/objectstack-ai/objectstack/dispatches that the board workflow executes as objectstack-fleet[bot]). (1) pr_create: POST /repos/objectstack-ai/objectui/pulls, draft forced, run 36007583045 success, PR #10253. Its body was read back byte-identical, 11185 bytes. (2) This os-dev-report comment: POST /repos//issues/10062/comments. Git pushes, which are not REST writes: 2 to claude/issue-10062-dataset-filter-between-arm (the empty-branch probe, then 83eb57f). No label writes, no body PATCH, no MCP.",
    "deviations": "One unlocked, unintended run. A shell command's trailing npx eslint --no-inline-config --format json received no file arguments and started a repo-wide lint in the worktree. It ran unlocked for about 2 minutes, was stopped by its own PIDs (387, 399, 400, all with cwd /home/user/objectui-issue-10062, identified before the kill), and never touched the tree; git status was clean afterwards. None of its output is used anywhere.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · The bridge's READ half accepts a stored scalar or list condition that its WRITE half drops as incomplete, so an edit to ANOTHER row silently removes the untouched condition. Probe, a temporary vitest file run at HEAD 83eb57f and then deleted: conditionToGroup({ $and: [{ stage: { $eq: 'won' } }, { name: { $eq: '' } }] }) is representable: true. Editing the stage row to 'lost' and calling groupToCondition gives { stage: { $eq: 'lost' } }, and the name condition is gone. The same happens to { region: { $in: [] } }, where the loss widens the result set from nothing to every 'won' row. Reachable today: FieldOperatorsSchema accepts { $eq: '' } and { $in: [] } (safeParse true), and DatasetSchema.filter takes them, so a Source-tab or AI-authored dataset filter carries them. The inspector commits on every change (DatasetFilterField.commitFilterGroup). It predates objectui#10062; the new $between guard deliberately stops at the pair arity. The file is datasetFilterCondition.ts, which will be free once PR #10253 lands · dedupe words: conditionToGroup representable incomplete stored condition · dataset filter edit removes untouched condition · datasetFilterCondition read half $in [] $eq empty · isFilterValueComplete read half scalar list",
    "carrier: none (承接者:无) · The JSDoc of DatasetFilterField.commitFilterGroup in packages/app-shell/src/views/metadata-admin/inspectors/DatasetDefaultInspector.tsx still names between as its example of an unmapped operator; since objectui#10062 it is the incomplete-row route instead. The file is outside this claim's declared surface, so it was left untouched · noted in PR #10253 Acceptance notes, not filed",
    "carrier: none (承接者:无) · observation: @objectstack/spec 17.4.0 DatasetSchema accepts any operator object under filter at parse ({ $between: '2026-01-01' }, a one-member $between, an invented $betweenx all parse true). The comparand door is FieldOperatorsSchema, which the dataset schema does not apply. The runtime's answer to those shapes was NOT measured, so this is not classed as (c) · noted in PR #10253 Acceptance notes, not filed"
    ]
    }


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 10062,
    "status": "done",
    "branch": "claude/issue-10062-dataset-filter-between-arm",
    "pr": "#10253",
    "session": "session_01BA3nKVUwKQJf8DBxrSVtNC — the harness-stamped Claude-Session id (subagent, so the parent's)",
    "premise_still_valid": true,
    "summary": "Round 2 addresses contract review 5815806788 (FAIL at 83eb57f). New head a30e42b = fix commit 8850829 + a merge commit of origin/main 6099dd8 (no rebase, fast-forward push). THE READ-HALF BUCKET RULE: in conditionToGroup, a stored pair-arity token ($between) is representable only if (1) it is a complete pair (unchanged from round 1), and (2) when a field list is supplied, the bucket the builder draws for that column offers between. That bucket is operatorsForFieldType(listed type), no opt-ins, which is exactly the builder's getOperatorsForField. Today only date/datetime/time offer it; every other bucket goes to the Source tab. UNKNOWN / ABSENT TYPE: measured, not assumed. The builder resolves a row's operators as operatorsForFieldType(fields.find(...)?.type), and operatorsForFieldType defaults a missing type to text. So a column listed WITHOUT a type, or not listed at all (e.g. a dotted relationship field, which the inspector's filterFields drops), is not unknown to the builder: it draws the text bucket, which lacks between, and the blank-trigger → equals defect applies identically. Both are therefore refused, which is stricter than readBackOperator's fixed-table fallback, deliberately. Only a read with NO field list skips the rule. That is safe for three reasons. No panel-drawing caller reads that way: the only production caller, DatasetFilterField in DatasetDefaultInspector.tsx, always passes its field list (grep: 1 production call site). It is the pure spec-shape read the field-less round-trip pins use, which still pass. And a non-representable answer is display-only (the Source-tab note, no commit path). Accepted consequence, display-only: while the field catalog is still loading the list is empty, so a stored date $between shows the Source-tab note until the catalog arrives. That is what base a78cd37 showed for every $between. CHANGESETS: 10062's sentence now lists exactly what is refused (not a complete pair; a column whose menu does not offer Between, naming date/datetime/time as the only offering types and the untyped/unlisted case). 9372's amended clause now reads 「like any unfinished row, as the only row, leaves the stored filter alone」. The 9372 frontmatter is byte-identical to base (md5 of lines 1-3 equal), and check-changeset-overwrite still reports 「declared at base: @object-ui/app-shell: patch / declares now: @object-ui/app-shell: patch」. PIN RETITLE: 「a stored $between this bridge would NOT have written reads as non-representable」 → 「a stored $between that is not a complete pair — a blank, missing or extra bound, or a scalar — reads as non-representable: the Source tab, not a silent drop」. It carries a comment pointing to where the bucket refusal is pinned. PR BODY CHANGES REQUESTED (the seat patches; I did not edit the body): (a) under the Clause-②: yes surface, name the read-half rule: stored $between is accepted as an editable row only as a complete pair, and, when a field list is supplied, only on a column whose builder bucket offers between (date/datetime/time); untyped and unlisted columns are refused because the builder draws the text bucket for them; field-less reads unchanged. (b) In the 「What changed」 table, the 「read half, stored $between」 after-cell becomes: representable only when isFilterValueComplete('between', value) AND (no field list, or operatorsForFieldType(column type) offers between); anything else goes to the Source tab. (c) Update the head from 83eb57f0 to a30e42bf in the 「Ablation」 and 「Verification」 headings, noting the fix commit 8850829 and the merge of origin/main 6099dd8. (d) Pins section: add the dateRoundTrip-9382 block 「a stored $between opens as a row only where the column's bucket offers between」 (number column refused; date control; every-bucket sweep equal to offeredBy(type).includes('between'); untyped and unlisted refused; field-less read unchanged; WHY leg reconcileOperatorForField → equals, reshape → 1; the real DatasetDefaultInspector shows the Source-tab note for a number-column $between and, as control, opens the builder drawn as 「Between」 with both bounds for a date column), and the retitled completeness pin. (e) Red-first round 2 on the 83eb57f source: 4 failed / 48 passed. (f) Ablation leg R2 (drop the bucket rule): 4 red / 71 passed, date controls green. (g) Verification numbers at a30e42b: 88 files, 1078 passed, 1 skipped. (h) Acceptance notes: add the new class (a) finding below (the same bucket gap for other stored tokens).",
    "tests": "HEAD a30e42b. All vitest runs from the objectui repo root through os-verify-lock.sh; check:* gates unlocked. RED FIRST (new pins, source still byte-identical to 83eb57f, verified with git diff --quiet HEAD -- datasetFilterCondition.ts): dateRoundTrip-9382 + unmappedInert-9372 gave 4 failed / 48 passed, VERDICT command-exit 1. The red ones were the NUMBER column, the every-bucket sweep, the untyped-or-unlisted column, and the real inspector number-column note. Green, as predicted: the DATE control, the field-less read, the WHY leg, the inspector date-column control, and the retitled completeness pin. GREEN with the fix before commit: inspectors/ 88 files, 1078 passed | 1 skipped. MERGE: git merge --no-ff of origin/main 6099dd8 into 8850829 gave a30e42b, clean. Main touched none of my files (diff over inspectors/, filter-builder.tsx and vitest.config.mts is empty). Only the app-shell @objectstack/spec range floor moved (^17.0.0 → ^17.3.0, installed still 17.4.0); pnpm install exit 0. git fetch origin main then merge-base --is-ancestor 6099dd87 origin/main exit 0, and the merge base is 6099dd8, so the gates' changeset is exactly my 7 files. ABLATION on a30e42b, after commit, through objectstack scripts/ablation-replace.mjs in wrap mode. Leg R2-bucket replaced the bucket-rule line (the if that asks operatorsForFieldType(fieldType) for op) with if (false) {. On disk: anchor x1 → x0, replacement x0 → x1, blob 8da2ff42 → 437ea0f2. Result on the four dataset-filter files: 4 failed / 71 passed. The red ones were number column, bucket sweep, untyped/unlisted, and inspector number note. The date control, inspector date control, field-less read, WHY and completeness pins stayed green. That matches the prediction written before the run. Restore proven: blob after restore 8da2ff42 == HEAD blob, git diff HEAD empty, the script's own trap re-verified, git status clean. No dist leg: the subject is imported relatively from source. FINAL at a30e42b: pnpm exec vitest run packages/app-shell/src/views/metadata-admin/inspectors/ gave Test Files 88 passed (88), Tests 1078 passed | 1 skipped (1079), VERDICT command-exit 0. TYPE-CHECK at a30e42b: the closure turbo run build --filter=@object-ui/app-shell^... --concurrency=2 gave 28 successful, 28 cached (turbo's input-hashed shared cache; outputs restored), VERDICT 0. Then pnpm --filter @object-ui/app-shell type-check echoed tsc --noEmit && tsc -p tsconfig.test.json, VERDICT command-exit 0. GATES at a30e42b, all exit 0: check:control-bytes OK (8296 files); check:new-line-citations 0 new; check-changeset-presence 1 declared (10062); check-changeset-overwrite reports only M 9372 with the declaration unchanged (report-only); check:changeset-claims no pending changeset names a touched file and no self-contradiction; check-changeset-no-major; check-changeset-fixed; check:pending-changeset-literals; check:spec-symbols; check:test-path-roots; check:installed-pin-claims. The vi.mock gates, newly relevant because dateRoundTrip-9382 now mocks ./useDatasetFields: check:vi-mock-specifiers, check:vi-mock-inherit and check:vi-mock-override-shape all OK. check-governed-queue-guard --test on the 7 paths: NOT GOVERNED. LINT, narrowed and declared, with three pieces of evidence. (1) Population: CI's turbo run lint runs each package's eslint . under the root eslint.config.js; this run covered the 5 touched .ts/.tsx files from git diff --name-only mergebase HEAD, and the file list was guarded non-empty before invoking eslint. (2) Count from --format json: 5 files, 0 errors, 3 warnings, all no-explicit-any on the existing as any lines in conditionToGroup (same as round 1). (3) Invariance: 0 projectService/parserOptions in eslint.config.js, and 0 eslint-rules modules reading the filesystem. NOT MEASURED: CI on a30e42b, not yet run when this was written; CI convergence belongs to the PM.",
    "mcp_calls": "0 — no MCP GitHub tool was called, read or write. All reads used unauthenticated REST GETs through the proxy: the review comment 5815806788, and the card and PR comment lists.",
    "api_writes": "1 REST write this round: this os-dev-report comment, POST /repos//issues/10062/comments via post-stamped.mjs, through the fleet-write relay as objectstack-fleet[bot]. Plus one git push (83eb57f..a30e42b, fast-forward), which is not a REST write. No PR body edit, no label write, no MCP. Round 1's writes (the pr_create and the first report) are recorded in comment 5815323018.",
    "deviations": "One, disclosed. The coordinator's item 1 said to mirror readBackOperator's 「only when the type is KNOWN」 pattern. I refuse $between on an untyped or unlisted column too, not only on a known non-date type, because the builder draws the text bucket for both (measured in filter-builder.tsx: getOperatorsForField → operatorsForFieldType(field?.type); operatorsForFieldType defaults fieldType || \"text\"), so the review's defect applies to them identically. The review's ② lists 「untyped」 among the false cases. The constraint the coordinator attached, that field-less spec-shape and round-trip pins keep passing, holds: only a read with no field list at all skips the rule. The inspector-level pin (real DatasetDefaultInspector) lives in dateRoundTrip-9382 with a file-level vi.mock of ./useDatasetFields, copied from the shape DatasetDefaultInspector.filterInert-9372.test.tsx already uses. It stays inside the claimed surface (the bridge's tests) instead of editing the inspector's own test file, and the three vi-mock gates pass.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · NEW this round, predates objectui#10062. The same bucket gap the review found for $between exists for OTHER stored tokens: conditionToGroup reads them back as an operator the column's bucket does not offer, so the panel draws a blank operator trigger (objectui#4768 / #7561), and one field-picker touch reconciles to equals and reshapes. That is the objectui#9382 「committing a different filter than the one they had stored」 defect, reached by filters authored outside the builder (Source tab, AI, seed metadata). Probe: a temporary vitest file at a30e42b, run and deleted. Cases: { closed_at: { $in: ['2026-01-01','2026-02-01'] } } on a date column → representable true, operator in, not in the date bucket. { name: { $gt: 'm' } } on a text column → greaterThan, not in the text bucket (readBackOperator falls back to the fixed table when the bucket offers neither candidate). { amount: { $in: [1,2] } } on a number column → in, not in the number bucket. For all three, FieldOperatorsSchema.safeParse is true and reconcileOperatorForField gives equals. Control: $in on a select column is offered. Suggested shape, one guard for the whole class: generalise round 2's bucket rule from the pair arity to every token, so a read-back operator the listed column's bucket does not offer is non-representable (Source tab); field-less reads unchanged. Not done in place: the bounded-fix exemption fails condition ② (the general shape is pinned by no one, and it changes the read of pre-existing stored filters that were never this card's), so it defaults to the seat. The file is datasetFilterCondition.ts, which will be free once PR #10253 lands. Could share a card with the round-1 finding below (both are the read half accepting a row the builder cannot hold faithfully) · dedupe words: conditionToGroup operator not offered by field bucket · readBackOperator fallback blank operator trigger · stored $in date column dataset filter reconcile equals · datasetFilterCondition read half bucket",
    "class: a · (carried from round 1, unchanged) The bridge's READ half accepts a stored scalar or list condition that its WRITE half drops as incomplete, so an edit to ANOTHER row silently removes the untouched condition. Probe: conditionToGroup({ $and: [{ stage: { $eq: 'won' } }, { name: { $eq: '' } }] }) is representable; editing the stage row commits { stage: { $eq: 'lost' } }, and the name condition is gone. The same happens to { region: { $in: [] } }. FieldOperatorsSchema accepts both shapes · dedupe words: conditionToGroup representable incomplete stored condition · dataset filter edit removes untouched condition · datasetFilterCondition read half $in [] $eq empty · isFilterValueComplete read half scalar list",
    "carrier: none (承接者:无) · The JSDoc of DatasetFilterField.commitFilterGroup in DatasetDefaultInspector.tsx still names between as its example of an unmapped operator. The file is outside the claimed surface · noted in PR #10253 Acceptance notes, not filed",
    "carrier: none (承接者:无) · observation: @objectstack/spec 17.4.0 DatasetSchema accepts any operator object under filter at parse; the comparand door is FieldOperatorsSchema, which the dataset schema does not apply. The runtime's answer was not measured · noted in PR #10253 Acceptance notes, not filed"
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT — PR objectui#10253 at a30e42bf · landing waits on objectui#10287

    domain:ui seat #1, session_01BA3nKVUwKQJf8DBxrSVtNC. I read the round-1 report 5815323018 and the round-2 report 5817699375 in full and reviewed both against the diff, the tree and CI.

    Implemented-by:  claude/issue-10062-dataset-filter-between-arm
    Reviewed-by:     session_01BA3nKVUwKQJf8DBxrSVtNC
    
    item reading
    shape draft · base main · Fixes #10062 · Clause-②: yes at line start
    the fix The dataset-filter bridge writes the spec's { $between: [lo, hi] } only for a complete pair, using the builder's own isFilterValueComplete. It reads a stored $between back as an editable row only as a complete pair on a column whose builder bucket offers between (date / datetime / time); anything else goes to the Source tab. The objectui#9381 pin is flipped, the conformance leg is added, and the 9372 pending changeset is corrected in prose
    review-tier records round 1 FAIL (5815806788, the bucket gap) → round 2 FAIL (the record above: only the routing note was untrue) → resolved by the seat. The addendum is on objectui#10257 as 5817965568, and the PR body was refreshed to cite it and to correct the stale commit line. ① and ② of round 2 pass as written, so no code changed
    CI 6 of 6 required contexts green. The advisory Spec Main Shape Gate is red repo-wide (objectui#10287), not this PR's

    Landing

    Held until objectui#10287 (priority:p0, claimed by this seat, dev dispatched) lands and this PR's gate re-reads green. Then the PR enters the merge queue. objectui#10257, whose fix lands in datasetFilterCondition.ts, is free once this merges.

    Out of scope

    • filed objectui#10257 (round 1) and routed there (round 2, 5817965568): the read half accepts rows the builder cannot hold faithfully, meaning incomplete scalar/list conditions and operators outside the column's bucket.
    • Acceptance notes: the DatasetFilterField.commitFilterGroup JSDoc still names between as unmapped. It is outside the surface; noted, not filed.
    • Acceptance notes: the spec's DatasetSchema accepts any operator object at parse. The runtime was not measured; noted, not filed.

    domain:ui seat #1 · review · 2026-09-24T16:37Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions