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
spec: two ComponentPropsMap entries contradict their ruled authorities — object-grid.data vs ViewDataSchema, element:number.filter vs the platform filter form (rulings ui#6207-A, ui#6206-B) #12039
Filed by the triage seat from two maintainer rulings (2026-08-25 decision-inbox batch adjudication, objectui batch 4; acceptance verbatim: 「同意」). Family-folded: same file surface (the spec-side ComponentPropsMap), same defect class (a props-map entry disagreeing with the authority the platform already ruled), both members ruled — five fold gates pass; per-key criteria below keep them independently checkable.
Today z.array(z.unknown()).optional() ("Static inline rows — bypasses the object query"), while ViewDataSchema — the authority objectui#5090 ruled and pinned the declaration against — is an object discriminated on provider (object/api/value/schema). Measured on @objectstack/spec@17.2.0: { provider: 'value', items: [] } is refused by the props-map entry while being the pinned-legal form.
Ruled: the props-map entry converges onto ViewDataSchema. The bare-array form is the deprecated staticData shortcut (the #4648 carve-out already refuses to publish it). Migration check before landing: sweep generated artifacts/templates for bare-array data authors; if any exist, the change carries their migration note.
Today record | object ("Filter criteria"), while every otherfilter input across the component tree is the platform's array-of-rules form (ViewFilterRule entries — object-grid, object-metric, record:related_list, plugin-list, data-list). The registration declares array; the renderer passes through to adapter.aggregate({ filter }) / adapter.find(object, { $filter }) opaquely.
Ruled: one filter orthography platform-wide — the spec entry aligns to the ViewFilterRule array form. Measurement-first (binding): verify the adapter/runtime behaviour on array filters for this element's read path before editing; if the runtime genuinely cannot take the array form here, STOP and report the fork back to triage — do not land a declaration the runtime refuses (that would recreate the defect in the opposite direction).
Acceptance (per key, independently checkable)
ComponentPropsMap['object-grid'].safeParse({ data: { provider: 'value', items: [] } }) → success; bare-array authored form handled per the migration note.
ComponentPropsMap['element:number'].filter accepts the ViewFilterRule array form (with the runtime measurement recorded in the PR).
Downstream (objectui, not this card): the formatZodError 把 union 分支的拒绝信息压成 "Invalid input" —— #4001 策展的散文在 CLI 路径上到不了作者 #4971 exemption entries object-grid.data:object and element:number.filter:array in registry-inputs-spec-parity.test.ts become deletable — ui#6207/ui#6206 are pm:blocked on this card and their unlock re-verification is exactly that deletion, after a released spec version reaches the objectui pin (consumer-installable criterion).
Clause-②: yes — both keys change the published contract's accept set; contract-review tier applies at dispatch. Serial check at claim: name any in-flight spec cards touching the ComponentPropsMap surface.
Refs: objectui#6207 · objectui#6206 (measurements, reproduce commands, and the ruling comments) · objectui#5090 / PR #5108 · objectui#4971 · objectui#4648.
Status (PM, 2026-08-25, round R6) — Key 1 landed via PR #12120 (merged 13:45Z). Key 2 forked back under ruling ui#6206-B's own measurement-first clause: the pinned runtime refuses the raw ViewFilterRule[] form on this element's aggregate read path (measurement recorded in PR #12120 and the card report above).
Unblock re-verification: when objectui#6302 lands (adapter lowers rule-arrays on the aggregate path) and a spec-consumable pin bump follows, Key 2 re-enters the queue on this card.
Claim: PM loop round R6
Session: session_01NDGG54XF5gbTLdQzCtnaVV
Branch: claude/issue-12039-componentpropsmap-rulings
Worktree: objectstack-issue-12039
Domain: domain:spec
File surface: packages/spec/src/ui/component.zod.ts (object-grid.data + element:number.filter entries) + tests + migrations tail-append + changeset; packages/spec/src/ui/view.zod.ts EXCLUDED (H17: on-hold #8346 declares it Restart-touch — dev stops and reports if it turns out to be needed) (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: claude-fable-5 — dispatch-gates 2026-08-25 @0b04839: "no path-derived mandate … a card changing contract accept/reject behaviour or widening the public surface is claude-fable-5"; clause-② content leg + ruling implementation ⇒ fable mandatory
Clause-②: yes
Serial constraints cleared: registry.ts tail-append shared with batch siblings #11842 (certain) and #11832 (conditional) — same-file serial WAIVED this batch by maintainer instruction (2026-08-25, chat, verbatim: 「并行5个子任务,加快进度」) under the 4-point substitute discipline. component.zod.ts sole writer this batch. Rulings implemented: ui#6207-A and ui#6206-B (maintainer batch adjudication recorded on those cards 2026-08-25 06:24Z; 6206-B's measurement-first precondition is binding and quoted in the dispatch order). No pin assertions on sibling behavior.
{
"issue": 12039,
"status": "needs_decision",
"branch": "claude/issue-12039-componentpropsmap-rulings",
"pr": "https://github.com/objectstack-ai/objectstack/pull/12120",
"premise_still_valid": true,
"summary": "Key 1 (object-grid.data, ui#6207-A) implemented and shipped as a draft PR: ComponentPropsMap['object-grid'].data converged from z.array(z.unknown()) onto ViewDataSchema; ADR-0087 semantic entry object-grid-data-view-data-converged; ruled migration check ran — zero bare-array data authors in examples/, skills/, create-objectstack, content/docs/, spec fixtures; ADR-0122 fallout handled (ObjectGridPropsParsed declared, Iso839 pin deleted with receipt, count 835->834 post-merge). Key 2 (element:number.filter, ui#6206-B) NOT edited: the binding measurement-first precondition measured the pinned runtime REFUSING the raw ViewFilterRule[] form on the element's primary (analytics) read path, so per the ruling's own text this is a fork back to triage — see open_questions. Both premises re-verified at BASE 8d60dcd (data was bare array at component.zod.ts:2266; filter was FilterConditionSchema at :1703); ui#6207/ui#6206 confirmed OPEN with pm:blocked.",
"tests": "Union at final commit 8fcbd6b (after merging origin/main @ a933ed7): spec full suite 'pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2' -> 'Test Files 425 passed (425) / Tests 11340 passed (11340)'; 'check:generated' -> '✓ All 14 generated artifacts are up to date' (check:test-typecheck ✓); spec typecheck green. Key-2 measurement executable: lowerAnalyticsWhere({where:[{field:'stage',operator:'equals',value:'won'}]}) THREW '[analytics] received a 'where' array that is not a filter'; AST tuples accepted -> {stage:'won'}; isFilterAST(ViewFilterRule[]) = false. Type-level reverse verification on rebuilt dist .d.ts: bare-array data -> TS2322 naming the ViewData union (red); provider object -> exit 0 (green). Reverse direction observed as predicted (red), no inversion.",
"gates": {
"head": "8fcbd6b",
"verdict_source": "each gate's own printed verdict line; exit codes captured before any pipe (run-gates scripts write per-gate logs)",
"dispatch_named": {
"check:changeset-gate-self-tests": 0,
"check:cross-package-test-inputs": 0,
"@objectstack/lint check:doc-formula-expressions": 0,
"@objectstack/spec check:empty-state": 0,
"@objectstack/spec check:liveness": 0,
"@objectstack/spec check:strictness-ledger": 0,
"@objectstack/spec check:variant-docs": 0,
"check:merge-driver": 0,
"check:published-files": 0,
"check:query-options-erasure": 0,
"check:slot-lookup": 0,
"check:spec-parsed-alias": 0,
"scripts/check-adr-0087-registration.mjs": 0,
"check:nul-bytes": 0
},
"rederived_additional": {
"note": "node scripts/pm/dispatch-gates.mjs re-run with no paths at 8fcbd6b; stderr line confirms answer taken from objectstack-ai/objectstack at 8fcbd6b",
"all_exit_0": [
"check:doc-anchors",
"check:doc-authoring",
"@objectstack/lint check:doc-security-posture",
"check:docs-audit-scope",
"check:docs-redirects",
"check:published-readme-links",
"check:quick-reference-counts",
"check:react-page-adapter-contract",
"check:role-word",
"check:objectui-changeset",
"check:test-source-alias",
"check:type-source-resolution",
"check:engine-double-contract",
"check:where-matcher",
"check-changeset-no-major",
"check-ci-filter-parity",
"check-cross-package-test-inputs(ci)",
"check-doc-frontmatter",
"check-doc-route-spelling",
"check-docs-section-name",
"check-empty-changeset",
"check-plugin-teardown-shape",
"check-section-landing-index",
"docs-audit/check-affected-docs",
"docs-audit/check-drift-comment",
"release-rehearsal-clone --self-test"
],
"post_workspace_build_exit_0": [
"check-dev-prereqs",
"check:type-check-coverage",
"check:type-check-debt (--re-measure, after turbo build of the 70-task package closure: 70 successful)"
]
}
},
"line_budget": {
"skills_diff": "none — no skills/** file touched",
"branch_delta_vs_origin_main": "9 files changed, 200 insertions(+), 10 deletions(-)"
},
"files_changed": [
".changeset/object-grid-data-view-data-converged.md",
"content/docs/references/ui/component.mdx",
"packages/spec/api-surface/ui.json",
"packages/spec/export-origins/ui.json",
"packages/spec/src/migrations/entries/semantic/18.object-grid-data-view-data-converged.ts",
"packages/spec/src/migrations/registry.ts",
"packages/spec/src/type-alias-convention.pin.test.ts",
"packages/spec/src/ui/component.test.ts",
"packages/spec/src/ui/component.zod.ts"
],
"deviations": [
"Key 2 not implemented — the ruling's own binding fork condition triggered (runtime refuses the array form on the primary read path); reported as needs_decision, not silently flipped.",
"Declared file-surface increment: packages/spec/src/type-alias-convention.pin.test.ts (not in the claim's file list) — forced by the convergence itself: ViewDataSchema's input≠infer removes ObjectGridPropsSchema from the pin family; handled per that file's own prescription (Iso839 deleted with receipt, ObjectGridPropsParsed declared).",
"Declared narrowing: repo-wide farm (pnpm lint et al.) left to CI; local runs = affected package full suite + every dispatch-named gate + every additionally path-derived family from the dispatch-gates recompute.",
"Key-2 runtime measurement executed via tsx on service-analytics src (built dist does not export lowerAnalyticsWhere); the same refusal class is pinned in the package's own filter-array-lowering.test.ts UNLOWERABLE table, and the dependency closure was built first.",
"Two verify-lock runs exceeded the 600s foreground cap and were moved to background by the harness; both completed under the lock with VERDICT command-exit lines read from their logs (no watcher-parked turn end).",
"registry.ts tail-append discipline: entries are per-file (conflict-free by construction); merged origin/main post-sibling-landing, gen:migration-registry re-run produced zero drift; the one real conflict (pin-count self-test) resolved to the joint truth 834 with both receipts kept."
],
"open_questions": [
{
"question": "element:number.filter (ui#6206-B) — Option B's binding measurement failed at the current objectui pin (190fbd01): the renderer passes filter opaquely; ObjectStackAdapter.aggregate sends it RAW as analytics `where`; the platform analytics door refuses non-AST arrays (measured executable: [{field,operator,value}] throws '[analytics] received a `where` array that is not a filter'; AST tuples accepted; both strategies share the door; analytics is foundational — 'the CLI always loads it'). The adapter's rule-array lowering (translateFilterArray) exists but runs only on the find() path, which aggregate() reaches only when analytics is absent — and deliberately not on a server rejection. Landing ViewFilterRule[] in spec now would be the 'declaration the runtime refuses' the ruling forbids. How should Option B be sequenced?",
"options": [
"A. Sequence the consumer first: objectui card — ObjectStackAdapter.aggregate lowers rule-shaped arrays before the wire (reusing translateFilterArray, already used one method over on find()); land, bump .objectui-sha; THEN converge the spec entry to z.array(ViewFilterRuleSchema) as ruled, on a follow-up card Blocked-by the pin bump.",
"B. Converge the spec entry now anyway — forbidden by the ruling's own text (recreates the defect in the opposite direction).",
"C. Flip to the record form (original Option A) — forbidden by the dispatch (不许悄悄倒向 record 形); would need a re-ruling.",
"D. Align the spec entry to the WIRE vocabulary instead (FilterCondition | FilterArray AST) — differs from both ruled options; also needs a re-ruling."
],
"recommendation": "A. Four axes: real business need — measured: designer/registry tier already declares 'array' for every filter input and authors write rule arrays in view configs; the element errors today only because one adapter path skips a lowering it performs elsewhere. Long-term soundness — Option B's one-orthography goal stands; the lowering belongs in the consumer that owns the authoring→wire translation (contract-first: no wire-tier loosening, no spec-side fallback). AI-hard-to-get-wrong — one array orthography platform-wide beats a per-block record exception; the current state (registry says array, spec says record, wire refuses raw arrays on one path) is the maximally confusing state for an AI author. Startup scope — smallest change set: one adapter method reuses existing machinery; no new capability surface, no new spec shape."
}
],
"out_of_scope_findings": []
}
os-dev-report (re-post: the HTML-comment marker on the previous comment was eaten by the body sanitizer; this literal-text form is the scannable copy — same JSON)
{
"issue": 12039,
"status": "needs_decision",
"branch": "claude/issue-12039-componentpropsmap-rulings",
"pr": "https://github.com/objectstack-ai/objectstack/pull/12120",
"premise_still_valid": true,
"summary": "Key 1 (object-grid.data, ui#6207-A) implemented and shipped as a draft PR: ComponentPropsMap['object-grid'].data converged from z.array(z.unknown()) onto ViewDataSchema; ADR-0087 semantic entry object-grid-data-view-data-converged; ruled migration check ran — zero bare-array data authors in examples/, skills/, create-objectstack, content/docs/, spec fixtures; ADR-0122 fallout handled (ObjectGridPropsParsed declared, Iso839 pin deleted with receipt, count 835->834 post-merge). Key 2 (element:number.filter, ui#6206-B) NOT edited: the binding measurement-first precondition measured the pinned runtime REFUSING the raw ViewFilterRule[] form on the element's primary (analytics) read path, so per the ruling's own text this is a fork back to triage — see open_questions. Both premises re-verified at BASE 8d60dcd (data was bare array at component.zod.ts:2266; filter was FilterConditionSchema at :1703); ui#6207/ui#6206 confirmed OPEN with pm:blocked.",
"tests": "Union at final commit 8fcbd6b (after merging origin/main @ a933ed7): spec full suite 'pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2' -> 'Test Files 425 passed (425) / Tests 11340 passed (11340)'; 'check:generated' -> '✓ All 14 generated artifacts are up to date' (check:test-typecheck ✓); spec typecheck green. Key-2 measurement executable: lowerAnalyticsWhere({where:[{field:'stage',operator:'equals',value:'won'}]}) THREW '[analytics] received a 'where' array that is not a filter'; AST tuples accepted -> {stage:'won'}; isFilterAST(ViewFilterRule[]) = false. Type-level reverse verification on rebuilt dist .d.ts: bare-array data -> TS2322 naming the ViewData union (red); provider object -> exit 0 (green). Reverse direction observed as predicted (red), no inversion.",
"gates": {
"head": "8fcbd6b",
"verdict_source": "each gate's own printed verdict line; exit codes captured before any pipe (run-gates scripts write per-gate logs)",
"dispatch_named": {
"check:changeset-gate-self-tests": 0,
"check:cross-package-test-inputs": 0,
"@objectstack/lint check:doc-formula-expressions": 0,
"@objectstack/spec check:empty-state": 0,
"@objectstack/spec check:liveness": 0,
"@objectstack/spec check:strictness-ledger": 0,
"@objectstack/spec check:variant-docs": 0,
"check:merge-driver": 0,
"check:published-files": 0,
"check:query-options-erasure": 0,
"check:slot-lookup": 0,
"check:spec-parsed-alias": 0,
"scripts/check-adr-0087-registration.mjs": 0,
"check:nul-bytes": 0
},
"rederived_additional": {
"note": "node scripts/pm/dispatch-gates.mjs re-run with no paths at 8fcbd6b; stderr line confirms answer taken from objectstack-ai/objectstack at 8fcbd6b",
"all_exit_0": [
"check:doc-anchors",
"check:doc-authoring",
"@objectstack/lint check:doc-security-posture",
"check:docs-audit-scope",
"check:docs-redirects",
"check:published-readme-links",
"check:quick-reference-counts",
"check:react-page-adapter-contract",
"check:role-word",
"check:objectui-changeset",
"check:test-source-alias",
"check:type-source-resolution",
"check:engine-double-contract",
"check:where-matcher",
"check-changeset-no-major",
"check-ci-filter-parity",
"check-cross-package-test-inputs(ci)",
"check-doc-frontmatter",
"check-doc-route-spelling",
"check-docs-section-name",
"check-empty-changeset",
"check-plugin-teardown-shape",
"check-section-landing-index",
"docs-audit/check-affected-docs",
"docs-audit/check-drift-comment",
"release-rehearsal-clone --self-test"
],
"post_workspace_build_exit_0": [
"check-dev-prereqs",
"check:type-check-coverage",
"check:type-check-debt (--re-measure, after turbo build of the 70-task package closure: 70 successful)"
]
}
},
"line_budget": {
"skills_diff": "none — no skills/** file touched",
"branch_delta_vs_origin_main": "9 files changed, 200 insertions(+), 10 deletions(-)"
},
"files_changed": [
".changeset/object-grid-data-view-data-converged.md",
"content/docs/references/ui/component.mdx",
"packages/spec/api-surface/ui.json",
"packages/spec/export-origins/ui.json",
"packages/spec/src/migrations/entries/semantic/18.object-grid-data-view-data-converged.ts",
"packages/spec/src/migrations/registry.ts",
"packages/spec/src/type-alias-convention.pin.test.ts",
"packages/spec/src/ui/component.test.ts",
"packages/spec/src/ui/component.zod.ts"
],
"deviations": [
"Key 2 not implemented — the ruling's own binding fork condition triggered (runtime refuses the array form on the primary read path); reported as needs_decision, not silently flipped.",
"Declared file-surface increment: packages/spec/src/type-alias-convention.pin.test.ts (not in the claim's file list) — forced by the convergence itself: ViewDataSchema's input≠infer removes ObjectGridPropsSchema from the pin family; handled per that file's own prescription (Iso839 deleted with receipt, ObjectGridPropsParsed declared).",
"Declared narrowing: repo-wide farm (pnpm lint et al.) left to CI; local runs = affected package full suite + every dispatch-named gate + every additionally path-derived family from the dispatch-gates recompute.",
"Key-2 runtime measurement executed via tsx on service-analytics src (built dist does not export lowerAnalyticsWhere); the same refusal class is pinned in the package's own filter-array-lowering.test.ts UNLOWERABLE table, and the dependency closure was built first.",
"Two verify-lock runs exceeded the 600s foreground cap and were moved to background by the harness; both completed under the lock with VERDICT command-exit lines read from their logs (no watcher-parked turn end).",
"registry.ts tail-append discipline: entries are per-file (conflict-free by construction); merged origin/main post-sibling-landing, gen:migration-registry re-run produced zero drift; the one real conflict (pin-count self-test) resolved to the joint truth 834 with both receipts kept.",
"Report marker: the previous report comment's HTML-comment marker was stripped by the body sanitizer (read back and confirmed); this comment is the corrected, literal-text-marked copy."
],
"open_questions": [
{
"question": "element:number.filter (ui#6206-B) — Option B's binding measurement failed at the current objectui pin (190fbd01): the renderer passes filter opaquely; ObjectStackAdapter.aggregate sends it RAW as analytics `where`; the platform analytics door refuses non-AST arrays (measured executable: [{field,operator,value}] throws '[analytics] received a `where` array that is not a filter'; AST tuples accepted; both strategies share the door; analytics is foundational — 'the CLI always loads it'). The adapter's rule-array lowering (translateFilterArray) exists but runs only on the find() path, which aggregate() reaches only when analytics is absent — and deliberately not on a server rejection. Landing ViewFilterRule[] in spec now would be the 'declaration the runtime refuses' the ruling forbids. How should Option B be sequenced?",
"options": [
"A. Sequence the consumer first: objectui card — ObjectStackAdapter.aggregate lowers rule-shaped arrays before the wire (reusing translateFilterArray, already used one method over on find()); land, bump .objectui-sha; THEN converge the spec entry to z.array(ViewFilterRuleSchema) as ruled, on a follow-up card Blocked-by the pin bump.",
"B. Converge the spec entry now anyway — forbidden by the ruling's own text (recreates the defect in the opposite direction).",
"C. Flip to the record form (original Option A) — forbidden by the dispatch (不许悄悄倒向 record 形); would need a re-ruling.",
"D. Align the spec entry to the WIRE vocabulary instead (FilterCondition | FilterArray AST) — differs from both ruled options; also needs a re-ruling."
],
"recommendation": "A. Four axes: real business need — measured: designer/registry tier already declares 'array' for every filter input and authors write rule arrays in view configs; the element errors today only because one adapter path skips a lowering it performs elsewhere. Long-term soundness — Option B's one-orthography goal stands; the lowering belongs in the consumer that owns the authoring→wire translation (contract-first: no wire-tier loosening, no spec-side fallback). AI-hard-to-get-wrong — one array orthography platform-wide beats a per-block record exception; the current state (registry says array, spec says record, wire refuses raw arrays on one path) is the maximally confusing state for an AI author. Startup scope — smallest change set: one adapter method reuses existing machinery; no new capability surface, no new spec shape."
}
],
"out_of_scope_findings": []
}
Checklist conclusion (verified against GitHub + local git diff): draft ✓ · base main ✓ · Part of #12039 first line (correct — this card stays open past the merge; Key 2 remains) ✓ · 9 files +200/−10: data → ViewDataSchema.optional() exactly per ruling ui#6207-A (quoted verbatim in the PR), acceptance-criterion test present ({ provider: 'value', items: [] } accepted — the exact value the old entry refused — plus bare-array refused at the data path), ADR-0087 semantic entry object-grid-data-view-data-converged + regenerated registry, ADR-0122 pin departure with receipt (835→834), api-surface/export-origins carry the new ObjectGridPropsParsed export ✓ · element:number.filter untouched ✓ · view.zod.ts NOT touched (H17 #8346 exclusion honored — import only) ✓ · ruled migration check ran: zero bare-array data authors across examples/skills/create-objectstack/docs/fixtures ✓ · no governed path ✓.
Key 2 (ruling ui#6206-B): the binding measurement-first precondition failed as the ruling anticipated — at objectui pin 190fbd01 the pinned runtime refuses raw ViewFilterRule[] on the element's primary (analytics) read path; the mechanism chain (renderer opaque pass-through → adapter sends raw on the aggregate path → lowerAnalyticsWhere throws → analytics is the default deployment's path) is recorded in the PR body with executable evidence. The dev stopped per the ruling's own text instead of forcing or silently flipping — correct outcome, not a failed dispatch. Fork recorded in the decision comment below.
Contract review (clause-② content leg, Key 1: accept-set change on a published props-map entry): fuse reading get_session.external_metadata.last_served_model = claude-fable-5 = CONTRACT_REVIEW_TIER (this seat, self-dispatched-card path). Verdict: PASS — the convergence lands exactly the ruled authority (ViewDataSchema, #5090-pinned), no narrowing beyond the ruling's scope (staticData retained as the ruling left it), migration prescription registered. Clearing needs:contract-review both carriers in this write batch.
Landing posture: per the opposing-fact-during-ruled-implementation clause, PR #12120 does NOT get auto-merge yet — it stays draft as the maintainer's objection window while the Key-2 fork decision is open (decision comment follows). CI green + reviewed; one label-flip away from the queue once the fork is answered or waved through.
Unlock note — the ruled Key-2 prerequisite has landed: the objectui pin now contains objectui#6828
From the domain:devx seat (session session_01WLJQhde67SeTccsmnBVarV), which executed #14027. For the domain:spec seat, which owns this card's Key-2 half — ⛔ no label or state changed here; the release decision is the unlock sweep's / your lane's.
Measured on origin/main after PR #14165 merged (83be4601):
Per the 2026-08-25 ruling's own caveat, the spec-side convergence to z.array(ViewFilterRuleSchema) should re-run the recorded refusal measurement at THIS pin before converging. The machine-readable blocker line recorded on this card (comment 5486863947) pointed at the pin bump; that predicate is now satisfied.
Unlock scan by the domain:spec seat (session_01GDA48PuRFrHyRfdkBz8m21): pm:blocked → pm:queue.
Release double-check per the state-machine rules: ① the most recent transition comment (5486691783, 2026-09-01T00:26Z) set the condition to Blocked-by: #14027 (the console-pin bump), refined by comment 5486863947 to "the pin must carry objectui#6828"; #14027 is CLOSED and the devx seat's unlock note (comment 5492894409) measured the pin — re-verified by this seat on origin/main at a39b02a6: .objectui-sha = d8ec8d6d4f01…, packages/data-objectstack/src/aggregate-filter-lowering.test.ts PRESENT at that pin, and translateFilterArray(params.filter) present in packages/data-objectstack/src/index.ts at that pin (1 hit). ② No PR merged against this card after that comment. File-surface re-check on the merged ref: packages/spec/src/ui/component.zod.ts last moved by #14165 (the pin bump) and #14158 — neither touched the element:number entry, so the Key-2 convergence is still owed.
What re-enters the queue is Key 2 ONLY (Key 1 landed via PR #12120): converge ComponentPropsMap['element:number'].filter to the ViewFilterRule array form per ruling ui#6206-B and the 2026-08-25 Option-A sequencing ruling (comment 5409255480), with that ruling's own caveat carried into the dispatch order — re-run the recorded refusal measurement (executable commands in PR #12120's body) at THIS pin before converging. Clause-② yes ⇒ CONTRACT_REVIEW_TIER. Type Bug places it in the Bug tier of the selection order; it claims fresh when a slot frees (5/5 in flight at this write). No assignee residue to clear.
Claim: PM loop, domain:spec seat (seat post #6017) — Key 2 only (Key 1 landed via PR #12120)
Session: session_01GDA48PuRFrHyRfdkBz8m21
Branch: claude/issue-12039-element-number-filter-array
Worktree: objectstack-issue-12039-key2
Domain: domain:spec
File surface: packages/spec/src/ui/component.zod.ts (the element:number entry's filter only) + its tests + ADR-0087 semantic entry (tail-append under packages/spec/src/migrations/entries/semantic/, regenerated registry.ts) + changeset + regenerated references/api-surface products as the gates demand. packages/spec/src/ui/view.zod.ts EXCLUDED (H17: on-hold #8346 declares it Restart-touch — ViewFilterRuleSchema is imported, never edited; dev stops and reports if an edit there turns out to be needed).
Container & model: S, mode:subagent, served claude-fable-5-1 (Clause-② content leg: accept-set change on a published props-map entry ⇒ contract-review tier; fuse literal reads claude-fable-5, posture recorded on #14303).
Clause-②: yes.
Ruling implemented: ui#6206-B (one filter orthography platform-wide — z.array(ViewFilterRuleSchema)) under the 2026-08-25 Option-A sequencing ruling (comment 5409255480). Prerequisite satisfied and re-verified this seat: .objectui-sha on origin/main = d8ec8d6d…, which carries objectui#6828 (comment 5502817852). The ruling's own caveat is binding in the dispatch order: re-run the recorded refusal measurement (executable commands in PR #12120's body) at THIS pin before converging; runtime still refusing ⇒ STOP and report the fork, no declaration change.
Serial constraints: component.zod.ts has no in-flight writer in this lane (seat post §3); no ADR-0087 registry tail-append in flight in this lane after PR #14335 lands (relay: this PR's registry entry regenerates after that merge).
{
"issue": 12039,
"status": "done",
"branch": "claude/issue-12039-element-number-filter-array",
"pr": "https://github.com/objectstack-ai/objectstack/pull/14404",
"head": "8068a7f31",
"premise_still_valid": true,
"summary": "Key 2 only (Key 1 landed via PR #12120). Step 0 passed on all four readings at objectui pin d8ec8d6d before any edit: (1) the adapter's aggregate() analytics branch lowers an array filter through translateFilterArray (index.ts:4924-4926, objectui#6828); (2) aggregate-filter-lowering.test.ts is present at the pin and pins rule-object arrays (single rule to AST tuple, isFilterAST false-then-true, alias mapping, and-join, spread-into-logical-node, cross-path parity); (3) elements.tsx passes filter opaquely to adapter.aggregate; (4) the objectstack analytics door is unchanged by design (isFilterAST(rule array)=false, isFilterAST(AST)=true, lowerAnalyticsWhere throws on the raw rule array, lowers AST tuples to { stage: 'won' }) — so no fork, the chain is authored array -> adapter lowering -> AST -> accepted. Then ComponentPropsMap['element:number'].filter converged from FilterConditionSchema to z.array(ViewFilterRuleSchema).optional() per ruling ui#6206-B under the 2026-08-25 Option-A sequencing ruling, with ADR-0087 semantic entry element-number-filter-rule-array (registry regenerated via gen:migration-registry; Key 1's and #14335's entries both present), a BREAKING minor changeset carrying the FROM -> TO block and the registered marker, ElementNumberPropsParsed declared with the Iso818 pin deleted and receipted (836 -> 835, ADR-0122), new orthography tests, and api-surface/export-origins/docs reference regenerated. Ruled migration sweep: exactly one in-repo record-form author — the spec fixture 'should accept full number props' — rewritten to the array form; zero in examples/, skills/, create-objectstack, content/docs/, packages/apps/ (the page.test.ts hits are dataSource.filter, a different key). Observed, not changed: element:record_picker.filter still reads FilterConditionSchema (component.zod.ts:2141). view.zod.ts untouched (H17). Branch was fast-forwarded onto origin/main d62f990a9 (#14335 merged) before the first edit, so no regen-merge was needed. needs:contract-review added to PR #14404 and to #12039 with comparative read-back. Draft PR, not flipped ready, no auto-merge, not enqueued.",
"tests": "All on HEAD 8068a7f31 (tree clean), each under scripts/pm/os-verify-lock.sh with exit codes captured from redirection. pnpm --filter @objectstack/spec run typecheck: tsc --noEmit clean, check:scripts-typecheck clean, 'check:test-typecheck: OK — 54 file(s) / 262 error(s) / 146 pinned signature(s) held' (VERDICT command-exit 0, re-run after the last commit). Touched files: vitest run --maxWorkers=2 src/ui/component.test.ts src/type-alias-convention.pin.test.ts — Test Files 2 passed, Tests 238 passed. Full spec suite: pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 — Test Files 450 passed | 1 skipped (451), Tests 12111 passed | 1 skipped (12112), 325.86s, VERDICT command-exit 0. check:generated — '✓ All 15 generated artifacts are up to date' (after --fix proved exactly three stale: api-surface, export-origins, docs). check:api-surface ✓, check:export-origins ✓, check:authorable-surface ✓ ('authorable-defaults/ verified against upstream d62f990a985e — 1236 default(s) unchanged', no anchor hand-edited), check:migration-registry ✓, scripts/check-adr-0087-registration.mjs ✓ ('1 declared-breaking changeset(s), each carrying an ADR-0087 disposition … registered element-number-filter-rule-array'), check:doc-authoring ✓ ('14375 customer-facing string(s) across 705 spec sources clean'), check:nul-bytes ✓ ('scanned 7843 text file(s) … no raw ASCII control bytes'). Derived union: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (stderr: derived at commit 8068a7f31) = 74 commands; 72 exit 0 — those that first printed PREREQUISITE NOT MET / dist-older-than-src were re-run green after building the lint closure and then the workspace (turbo run build --filter='./packages/*' --filter='./packages/*/*', 70/70 successful): doc-formula-expressions, doc-security-posture, browser-reachable-entries, dual-source-exports, entry-nameability, exported-any, skill-examples ('259 prose examples type-check'), dev-prereqs ('67 package build artifacts present'), dual-build-cjs-loads ('102 published require entry point(s) across 66 package(s) load'), type-check-coverage, type-check-debt ('27 ledger entr(ies) re-measured … none above its recorded number'); check:query-options-erasure ran alone after a first attempt was cut by the ~10-minute foreground cap ('ratchet holds: 67 unswept non-test site(s) in 17 file(s), none new'). The 2 remaining are not runnable here by their own verdicts: check:react-declaration-parity (needs objectui's sdui.manifest.json — the on-demand pin-bump gate) and check-test-completeness (needs a saved turbo test log; NOT MEASURED). Type-level reverse verification against the rebuilt dist (tsc --noEmit --ignoreConfig probe in packages/lint against @objectstack/spec/ui): record-form filter RED with TS2353 naming the '{ field; operator; value }[]' type, array form GREEN (0 errors); probe deleted. No ablation was run (no new gate was added, so none was owed). Repo-wide pnpm lint and the multi-package farm are CI's runs (declared narrowing).",
"mcp_calls": "0 — no MCP GitHub calls; every GitHub read/write went through authenticated REST (curl) plus local git",
"open_questions": [],
"out_of_scope_findings": []
}
ACCEPT — PR #14404 (head 8068a7f3), Key 2; reviewed against GitHub, not the report. Card complete with this merge (Key 1 landed via PR #12120), so Fixes #12039 is the right closing line.
Ruling compliance (ui#6206-B under the 2026-08-25 Option-A sequencing, comment 5409255480)
Step 0 (measurement-first, binding) passed and is re-verified by this seat at objectui pin d8ec8d6d: the adapter's aggregate() analytics branch lowers an array filter through translateFilterArray(params.filter) (packages/data-objectstack/src/index.ts ~4924–4926, objectui#6828), and packages/data-objectstack/src/aggregate-filter-lowering.test.ts is present at the pin. The analytics door itself still refuses a raw rule-object array by design; the end-to-end chain is authored array → adapter lowering → AST → accepted, which is exactly the sequencing the ruling ordered.
Rejection tests assert the envelope: the record form is refused at the filter path with invalid_type (expected: 'array'), the array form is accepted and normalizes operators through the real ViewFilterRuleSchema, and a scalar in value is refused at filter.0.value.
Contract review (Clause ②) — PASS, in-seat
Path limb: packages/spec/src/ui/component.zod.ts + packages/spec/src/migrations/** ⇒ Clause ② applies.
Content limb: yes — accept-set change (record/object form → rule array) on a published props-map entry; ruled, sequenced, registered.
Clause ② carriers agree (check-clause2-carriers.mjs --pair).
Trial merge against origin/main (5ec4f6fa): CLEAN; main has not moved on any of this PR's files since the merge base.
Closing keywords: only Fixes #12039.
Gate union: 74 derived commands, 72 exit 0; the two not runnable locally by their own verdict (check:react-declaration-parity needs objectui's manifest; check-test-completeness needs the CI test log) are CI's — declared narrowing accepted.
Observed residue (not this PR's): element:record_picker.filter still declares FilterConditionSchema (component.zod.ts ~2141) — the last record-form filter in the map after this convergence. Filed as a separate finding for triage (number in the follow-up comment); ⛔ not folded here.
Follow-up to the ACCEPT above: the observed residue (element:record_picker.filter still on FilterConditionSchema) is filed as #14406 for first-touch grading — measurement-first before any declaration moves, per the same ruling.
Activity
os-litant commentedon Aug 25, 2026
Claim: PM loop round R6
Session:
session_01NDGG54XF5gbTLdQzCtnaVVBranch:
claude/issue-12039-componentpropsmap-rulingsWorktree:
objectstack-issue-12039Domain:
domain:specFile surface:
packages/spec/src/ui/component.zod.ts(object-grid.data + element:number.filter entries) + tests + migrations tail-append + changeset;packages/spec/src/ui/view.zod.tsEXCLUDED (H17: on-hold #8346 declares it Restart-touch — dev stops and reports if it turns out to be needed) (stop on breach; explain in the report)Container & model: M,
mode:subagent,model: claude-fable-5— dispatch-gates 2026-08-25 @0b04839: "no path-derived mandate … a card changing contract accept/reject behaviour or widening the public surface is claude-fable-5"; clause-② content leg + ruling implementation ⇒ fable mandatoryClause-②: yes
Serial constraints cleared: registry.ts tail-append shared with batch siblings #11842 (certain) and #11832 (conditional) — same-file serial WAIVED this batch by maintainer instruction (2026-08-25, chat, verbatim: 「并行5个子任务,加快进度」) under the 4-point substitute discipline. component.zod.ts sole writer this batch. Rulings implemented: ui#6207-A and ui#6206-B (maintainer batch adjudication recorded on those cards 2026-08-25 06:24Z; 6206-B's measurement-first precondition is binding and quoted in the dispatch order). No pin assertions on sibling behavior.
Generated by Claude Code
os-litant commentedon Aug 25, 2026
{ "issue": 12039, "status": "needs_decision", "branch": "claude/issue-12039-componentpropsmap-rulings", "pr": "https://github.com/objectstack-ai/objectstack/pull/12120", "premise_still_valid": true, "summary": "Key 1 (object-grid.data, ui#6207-A) implemented and shipped as a draft PR: ComponentPropsMap['object-grid'].data converged from z.array(z.unknown()) onto ViewDataSchema; ADR-0087 semantic entry object-grid-data-view-data-converged; ruled migration check ran — zero bare-array data authors in examples/, skills/, create-objectstack, content/docs/, spec fixtures; ADR-0122 fallout handled (ObjectGridPropsParsed declared, Iso839 pin deleted with receipt, count 835->834 post-merge). Key 2 (element:number.filter, ui#6206-B) NOT edited: the binding measurement-first precondition measured the pinned runtime REFUSING the raw ViewFilterRule[] form on the element's primary (analytics) read path, so per the ruling's own text this is a fork back to triage — see open_questions. Both premises re-verified at BASE 8d60dcd (data was bare array at component.zod.ts:2266; filter was FilterConditionSchema at :1703); ui#6207/ui#6206 confirmed OPEN with pm:blocked.", "tests": "Union at final commit 8fcbd6b (after merging origin/main @ a933ed7): spec full suite 'pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2' -> 'Test Files 425 passed (425) / Tests 11340 passed (11340)'; 'check:generated' -> '✓ All 14 generated artifacts are up to date' (check:test-typecheck ✓); spec typecheck green. Key-2 measurement executable: lowerAnalyticsWhere({where:[{field:'stage',operator:'equals',value:'won'}]}) THREW '[analytics] received a 'where' array that is not a filter'; AST tuples accepted -> {stage:'won'}; isFilterAST(ViewFilterRule[]) = false. Type-level reverse verification on rebuilt dist .d.ts: bare-array data -> TS2322 naming the ViewData union (red); provider object -> exit 0 (green). Reverse direction observed as predicted (red), no inversion.", "gates": { "head": "8fcbd6b", "verdict_source": "each gate's own printed verdict line; exit codes captured before any pipe (run-gates scripts write per-gate logs)", "dispatch_named": { "check:changeset-gate-self-tests": 0, "check:cross-package-test-inputs": 0, "@objectstack/lint check:doc-formula-expressions": 0, "@objectstack/spec check:empty-state": 0, "@objectstack/spec check:liveness": 0, "@objectstack/spec check:strictness-ledger": 0, "@objectstack/spec check:variant-docs": 0, "check:merge-driver": 0, "check:published-files": 0, "check:query-options-erasure": 0, "check:slot-lookup": 0, "check:spec-parsed-alias": 0, "scripts/check-adr-0087-registration.mjs": 0, "check:nul-bytes": 0 }, "rederived_additional": { "note": "node scripts/pm/dispatch-gates.mjs re-run with no paths at 8fcbd6b; stderr line confirms answer taken from objectstack-ai/objectstack at 8fcbd6b", "all_exit_0": [ "check:doc-anchors", "check:doc-authoring", "@objectstack/lint check:doc-security-posture", "check:docs-audit-scope", "check:docs-redirects", "check:published-readme-links", "check:quick-reference-counts", "check:react-page-adapter-contract", "check:role-word", "check:objectui-changeset", "check:test-source-alias", "check:type-source-resolution", "check:engine-double-contract", "check:where-matcher", "check-changeset-no-major", "check-ci-filter-parity", "check-cross-package-test-inputs(ci)", "check-doc-frontmatter", "check-doc-route-spelling", "check-docs-section-name", "check-empty-changeset", "check-plugin-teardown-shape", "check-section-landing-index", "docs-audit/check-affected-docs", "docs-audit/check-drift-comment", "release-rehearsal-clone --self-test" ], "post_workspace_build_exit_0": [ "check-dev-prereqs", "check:type-check-coverage", "check:type-check-debt (--re-measure, after turbo build of the 70-task package closure: 70 successful)" ] } }, "line_budget": { "skills_diff": "none — no skills/** file touched", "branch_delta_vs_origin_main": "9 files changed, 200 insertions(+), 10 deletions(-)" }, "files_changed": [ ".changeset/object-grid-data-view-data-converged.md", "content/docs/references/ui/component.mdx", "packages/spec/api-surface/ui.json", "packages/spec/export-origins/ui.json", "packages/spec/src/migrations/entries/semantic/18.object-grid-data-view-data-converged.ts", "packages/spec/src/migrations/registry.ts", "packages/spec/src/type-alias-convention.pin.test.ts", "packages/spec/src/ui/component.test.ts", "packages/spec/src/ui/component.zod.ts" ], "deviations": [ "Key 2 not implemented — the ruling's own binding fork condition triggered (runtime refuses the array form on the primary read path); reported as needs_decision, not silently flipped.", "Declared file-surface increment: packages/spec/src/type-alias-convention.pin.test.ts (not in the claim's file list) — forced by the convergence itself: ViewDataSchema's input≠infer removes ObjectGridPropsSchema from the pin family; handled per that file's own prescription (Iso839 deleted with receipt, ObjectGridPropsParsed declared).", "Declared narrowing: repo-wide farm (pnpm lint et al.) left to CI; local runs = affected package full suite + every dispatch-named gate + every additionally path-derived family from the dispatch-gates recompute.", "Key-2 runtime measurement executed via tsx on service-analytics src (built dist does not export lowerAnalyticsWhere); the same refusal class is pinned in the package's own filter-array-lowering.test.ts UNLOWERABLE table, and the dependency closure was built first.", "Two verify-lock runs exceeded the 600s foreground cap and were moved to background by the harness; both completed under the lock with VERDICT command-exit lines read from their logs (no watcher-parked turn end).", "registry.ts tail-append discipline: entries are per-file (conflict-free by construction); merged origin/main post-sibling-landing, gen:migration-registry re-run produced zero drift; the one real conflict (pin-count self-test) resolved to the joint truth 834 with both receipts kept." ], "open_questions": [ { "question": "element:number.filter (ui#6206-B) — Option B's binding measurement failed at the current objectui pin (190fbd01): the renderer passes filter opaquely; ObjectStackAdapter.aggregate sends it RAW as analytics `where`; the platform analytics door refuses non-AST arrays (measured executable: [{field,operator,value}] throws '[analytics] received a `where` array that is not a filter'; AST tuples accepted; both strategies share the door; analytics is foundational — 'the CLI always loads it'). The adapter's rule-array lowering (translateFilterArray) exists but runs only on the find() path, which aggregate() reaches only when analytics is absent — and deliberately not on a server rejection. Landing ViewFilterRule[] in spec now would be the 'declaration the runtime refuses' the ruling forbids. How should Option B be sequenced?", "options": [ "A. Sequence the consumer first: objectui card — ObjectStackAdapter.aggregate lowers rule-shaped arrays before the wire (reusing translateFilterArray, already used one method over on find()); land, bump .objectui-sha; THEN converge the spec entry to z.array(ViewFilterRuleSchema) as ruled, on a follow-up card Blocked-by the pin bump.", "B. Converge the spec entry now anyway — forbidden by the ruling's own text (recreates the defect in the opposite direction).", "C. Flip to the record form (original Option A) — forbidden by the dispatch (不许悄悄倒向 record 形); would need a re-ruling.", "D. Align the spec entry to the WIRE vocabulary instead (FilterCondition | FilterArray AST) — differs from both ruled options; also needs a re-ruling." ], "recommendation": "A. Four axes: real business need — measured: designer/registry tier already declares 'array' for every filter input and authors write rule arrays in view configs; the element errors today only because one adapter path skips a lowering it performs elsewhere. Long-term soundness — Option B's one-orthography goal stands; the lowering belongs in the consumer that owns the authoring→wire translation (contract-first: no wire-tier loosening, no spec-side fallback). AI-hard-to-get-wrong — one array orthography platform-wide beats a per-block record exception; the current state (registry says array, spec says record, wire refuses raw arrays on one path) is the maximally confusing state for an AI author. Startup scope — smallest change set: one adapter method reuses existing machinery; no new capability surface, no new spec shape." } ], "out_of_scope_findings": [] }Generated by Claude Code
os-litant commentedon Aug 25, 2026
os-dev-report (re-post: the HTML-comment marker on the previous comment was eaten by the body sanitizer; this literal-text form is the scannable copy — same JSON)
{ "issue": 12039, "status": "needs_decision", "branch": "claude/issue-12039-componentpropsmap-rulings", "pr": "https://github.com/objectstack-ai/objectstack/pull/12120", "premise_still_valid": true, "summary": "Key 1 (object-grid.data, ui#6207-A) implemented and shipped as a draft PR: ComponentPropsMap['object-grid'].data converged from z.array(z.unknown()) onto ViewDataSchema; ADR-0087 semantic entry object-grid-data-view-data-converged; ruled migration check ran — zero bare-array data authors in examples/, skills/, create-objectstack, content/docs/, spec fixtures; ADR-0122 fallout handled (ObjectGridPropsParsed declared, Iso839 pin deleted with receipt, count 835->834 post-merge). Key 2 (element:number.filter, ui#6206-B) NOT edited: the binding measurement-first precondition measured the pinned runtime REFUSING the raw ViewFilterRule[] form on the element's primary (analytics) read path, so per the ruling's own text this is a fork back to triage — see open_questions. Both premises re-verified at BASE 8d60dcd (data was bare array at component.zod.ts:2266; filter was FilterConditionSchema at :1703); ui#6207/ui#6206 confirmed OPEN with pm:blocked.", "tests": "Union at final commit 8fcbd6b (after merging origin/main @ a933ed7): spec full suite 'pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2' -> 'Test Files 425 passed (425) / Tests 11340 passed (11340)'; 'check:generated' -> '✓ All 14 generated artifacts are up to date' (check:test-typecheck ✓); spec typecheck green. Key-2 measurement executable: lowerAnalyticsWhere({where:[{field:'stage',operator:'equals',value:'won'}]}) THREW '[analytics] received a 'where' array that is not a filter'; AST tuples accepted -> {stage:'won'}; isFilterAST(ViewFilterRule[]) = false. Type-level reverse verification on rebuilt dist .d.ts: bare-array data -> TS2322 naming the ViewData union (red); provider object -> exit 0 (green). Reverse direction observed as predicted (red), no inversion.", "gates": { "head": "8fcbd6b", "verdict_source": "each gate's own printed verdict line; exit codes captured before any pipe (run-gates scripts write per-gate logs)", "dispatch_named": { "check:changeset-gate-self-tests": 0, "check:cross-package-test-inputs": 0, "@objectstack/lint check:doc-formula-expressions": 0, "@objectstack/spec check:empty-state": 0, "@objectstack/spec check:liveness": 0, "@objectstack/spec check:strictness-ledger": 0, "@objectstack/spec check:variant-docs": 0, "check:merge-driver": 0, "check:published-files": 0, "check:query-options-erasure": 0, "check:slot-lookup": 0, "check:spec-parsed-alias": 0, "scripts/check-adr-0087-registration.mjs": 0, "check:nul-bytes": 0 }, "rederived_additional": { "note": "node scripts/pm/dispatch-gates.mjs re-run with no paths at 8fcbd6b; stderr line confirms answer taken from objectstack-ai/objectstack at 8fcbd6b", "all_exit_0": [ "check:doc-anchors", "check:doc-authoring", "@objectstack/lint check:doc-security-posture", "check:docs-audit-scope", "check:docs-redirects", "check:published-readme-links", "check:quick-reference-counts", "check:react-page-adapter-contract", "check:role-word", "check:objectui-changeset", "check:test-source-alias", "check:type-source-resolution", "check:engine-double-contract", "check:where-matcher", "check-changeset-no-major", "check-ci-filter-parity", "check-cross-package-test-inputs(ci)", "check-doc-frontmatter", "check-doc-route-spelling", "check-docs-section-name", "check-empty-changeset", "check-plugin-teardown-shape", "check-section-landing-index", "docs-audit/check-affected-docs", "docs-audit/check-drift-comment", "release-rehearsal-clone --self-test" ], "post_workspace_build_exit_0": [ "check-dev-prereqs", "check:type-check-coverage", "check:type-check-debt (--re-measure, after turbo build of the 70-task package closure: 70 successful)" ] } }, "line_budget": { "skills_diff": "none — no skills/** file touched", "branch_delta_vs_origin_main": "9 files changed, 200 insertions(+), 10 deletions(-)" }, "files_changed": [ ".changeset/object-grid-data-view-data-converged.md", "content/docs/references/ui/component.mdx", "packages/spec/api-surface/ui.json", "packages/spec/export-origins/ui.json", "packages/spec/src/migrations/entries/semantic/18.object-grid-data-view-data-converged.ts", "packages/spec/src/migrations/registry.ts", "packages/spec/src/type-alias-convention.pin.test.ts", "packages/spec/src/ui/component.test.ts", "packages/spec/src/ui/component.zod.ts" ], "deviations": [ "Key 2 not implemented — the ruling's own binding fork condition triggered (runtime refuses the array form on the primary read path); reported as needs_decision, not silently flipped.", "Declared file-surface increment: packages/spec/src/type-alias-convention.pin.test.ts (not in the claim's file list) — forced by the convergence itself: ViewDataSchema's input≠infer removes ObjectGridPropsSchema from the pin family; handled per that file's own prescription (Iso839 deleted with receipt, ObjectGridPropsParsed declared).", "Declared narrowing: repo-wide farm (pnpm lint et al.) left to CI; local runs = affected package full suite + every dispatch-named gate + every additionally path-derived family from the dispatch-gates recompute.", "Key-2 runtime measurement executed via tsx on service-analytics src (built dist does not export lowerAnalyticsWhere); the same refusal class is pinned in the package's own filter-array-lowering.test.ts UNLOWERABLE table, and the dependency closure was built first.", "Two verify-lock runs exceeded the 600s foreground cap and were moved to background by the harness; both completed under the lock with VERDICT command-exit lines read from their logs (no watcher-parked turn end).", "registry.ts tail-append discipline: entries are per-file (conflict-free by construction); merged origin/main post-sibling-landing, gen:migration-registry re-run produced zero drift; the one real conflict (pin-count self-test) resolved to the joint truth 834 with both receipts kept.", "Report marker: the previous report comment's HTML-comment marker was stripped by the body sanitizer (read back and confirmed); this comment is the corrected, literal-text-marked copy." ], "open_questions": [ { "question": "element:number.filter (ui#6206-B) — Option B's binding measurement failed at the current objectui pin (190fbd01): the renderer passes filter opaquely; ObjectStackAdapter.aggregate sends it RAW as analytics `where`; the platform analytics door refuses non-AST arrays (measured executable: [{field,operator,value}] throws '[analytics] received a `where` array that is not a filter'; AST tuples accepted; both strategies share the door; analytics is foundational — 'the CLI always loads it'). The adapter's rule-array lowering (translateFilterArray) exists but runs only on the find() path, which aggregate() reaches only when analytics is absent — and deliberately not on a server rejection. Landing ViewFilterRule[] in spec now would be the 'declaration the runtime refuses' the ruling forbids. How should Option B be sequenced?", "options": [ "A. Sequence the consumer first: objectui card — ObjectStackAdapter.aggregate lowers rule-shaped arrays before the wire (reusing translateFilterArray, already used one method over on find()); land, bump .objectui-sha; THEN converge the spec entry to z.array(ViewFilterRuleSchema) as ruled, on a follow-up card Blocked-by the pin bump.", "B. Converge the spec entry now anyway — forbidden by the ruling's own text (recreates the defect in the opposite direction).", "C. Flip to the record form (original Option A) — forbidden by the dispatch (不许悄悄倒向 record 形); would need a re-ruling.", "D. Align the spec entry to the WIRE vocabulary instead (FilterCondition | FilterArray AST) — differs from both ruled options; also needs a re-ruling." ], "recommendation": "A. Four axes: real business need — measured: designer/registry tier already declares 'array' for every filter input and authors write rule arrays in view configs; the element errors today only because one adapter path skips a lowering it performs elsewhere. Long-term soundness — Option B's one-orthography goal stands; the lowering belongs in the consumer that owns the authoring→wire translation (contract-first: no wire-tier loosening, no spec-side fallback). AI-hard-to-get-wrong — one array orthography platform-wide beats a per-block record exception; the current state (registry says array, spec says record, wire refuses raw arrays on one path) is the maximally confusing state for an AI author. Startup scope — smallest change set: one adapter method reuses existing machinery; no new capability surface, no new spec shape." } ], "out_of_scope_findings": [] }Generated by Claude Code
os-litant commentedon Aug 25, 2026
ACCEPT (Key 1 half-delivery) — PR #12120 (R6, reviewer of record: spec seat, session
session_01NDGG54XF5gbTLdQzCtnaVV).Checklist conclusion (verified against GitHub + local git diff): draft ✓ · base
main✓ ·Part of #12039first line (correct — this card stays open past the merge; Key 2 remains) ✓ · 9 files +200/−10:data→ViewDataSchema.optional()exactly per ruling ui#6207-A (quoted verbatim in the PR), acceptance-criterion test present ({ provider: 'value', items: [] }accepted — the exact value the old entry refused — plus bare-array refused at thedatapath), ADR-0087 semantic entryobject-grid-data-view-data-converged+ regenerated registry, ADR-0122 pin departure with receipt (835→834), api-surface/export-origins carry the newObjectGridPropsParsedexport ✓ ·element:number.filteruntouched ✓ ·view.zod.tsNOT touched (H17 #8346 exclusion honored — import only) ✓ · ruled migration check ran: zero bare-arraydataauthors across examples/skills/create-objectstack/docs/fixtures ✓ · no governed path ✓.Key 2 (ruling ui#6206-B): the binding measurement-first precondition failed as the ruling anticipated — at objectui pin
190fbd01the pinned runtime refuses rawViewFilterRule[]on the element's primary (analytics) read path; the mechanism chain (renderer opaque pass-through → adapter sends raw on the aggregate path →lowerAnalyticsWherethrows → analytics is the default deployment's path) is recorded in the PR body with executable evidence. The dev stopped per the ruling's own text instead of forcing or silently flipping — correct outcome, not a failed dispatch. Fork recorded in the decision comment below.Contract review (clause-② content leg, Key 1: accept-set change on a published props-map entry): fuse reading
get_session.external_metadata.last_served_model = claude-fable-5=CONTRACT_REVIEW_TIER(this seat, self-dispatched-card path). Verdict: PASS — the convergence lands exactly the ruled authority (ViewDataSchema, #5090-pinned), no narrowing beyond the ruling's scope (staticDataretained as the ruling left it), migration prescription registered. Clearingneeds:contract-reviewboth carriers in this write batch.Landing posture: per the opposing-fact-during-ruled-implementation clause, PR #12120 does NOT get auto-merge yet — it stays draft as the maintainer's objection window while the Key-2 fork decision is open (decision comment follows). CI green + reviewed; one label-flip away from the queue once the fork is answered or waved through.
Generated by Claude Code
os-litant commentedon Aug 25, 2026
[决策] Key 2(
element:number.filter,裁决 ui#6206-B)实测分叉 —— 如何给 Option B 排序?分叉事实(dev 实测,机制链在 PR #12120 正文,可执行证据齐全):裁决 B 的绑定前置「Measurement-first is binding」实测失败 —— objectui pin
190fbd01上,渲染器把 filter 不透明下传,ObjectStackAdapter.aggregate()走 analytics 路径时原样发送;analytics 门对非 AST 数组响亮拒绝(lowerAnalyticsWherethrows);analytics 是基础能力(CLI 恒加载)⇒ 拒绝路径就是默认部署路径。规则数组的 lowering 机器(translateFilterArray)同文件已存在,但只接在find()路径上 —— 这是 objectui 适配器一条路径漏接 lowering,不是运行时语义上不能收数组。现在落 spec 声明 = 裁决自己禁止的「运行时拒绝的声明」。选项:
ObjectStackAdapter.aggregate()在上线前对 rule 形数组做 lowering(复用find()路径既有机器)→ 落地 → pin bump → 然后 spec entry 按原裁决收敛z.array(ViewFilterRuleSchema),Blocked-by pin bump。不改任何已裁语义,只加前置顺序。四维分析:
array;作者照 manifest 补全写数组,今天在这个 element 上会渲染成错误态 —— 这是真实用户可达的缺陷,修的是适配器漏接的 lowering,不是投机面。IHttpServer.setFallbackHandler的 Hono 实现 + dispatcher 端点派发步 + 路由台账登记 #5090/正字法裁决一脉;C/D 都要推翻或旁绕已裁方向。推荐:A。 若同意,本席将:在 objectui 立消费方卡(出处 + 可执行判据 + Blocked-by 关系),本卡 Key 2 半边转
pm:blocked等 pin bump,PR #12120(Key 1,已复核全绿)翻 ready 入队。置信缺口:实测钉在 pin
190fbd01;裁到之时若 pin 已前移或适配器已在上游修掉,判据重跑一遍再执行(实测命令在 PR 正文,可复跑)。PR #12120(Key 1)现按异议窗口条款悬置 draft —— 本卡答复即放行。回一个字母即可。
Generated by Claude Code
12 remaining items
baozhoutao commentedon Sep 1, 2026
Unlock note — the ruled Key-2 prerequisite has landed: the objectui pin now contains objectui#6828
From the
domain:devxseat (sessionsession_01WLJQhde67SeTccsmnBVarV), which executed #14027. For thedomain:specseat, which owns this card's Key-2 half — ⛔ no label or state changed here; the release decision is the unlock sweep's / your lane's.Measured on
origin/mainafter PR #14165 merged (83be4601):.objectui-sha=d8ec8d6d4f011b11c8eb1e6dbd364ef206711391(objectui main, 2026-09-01 08:23Z)git show $(cat .objectui-sha):packages/data-objectstack/src/aggregate-filter-lowering.test.tssucceeds — the objectui#6828 fix and its 22-pin test file are at the pin.Per the 2026-08-25 ruling's own caveat, the spec-side convergence to
z.array(ViewFilterRuleSchema)should re-run the recorded refusal measurement at THIS pin before converging. The machine-readable blocker line recorded on this card (comment 5486863947) pointed at the pin bump; that predicate is now satisfied.Generated by Claude Code
os-zhuang commentedon Sep 2, 2026
Unlock scan by the
domain:specseat (session_01GDA48PuRFrHyRfdkBz8m21):pm:blocked→pm:queue.Release double-check per the state-machine rules: ① the most recent transition comment (5486691783, 2026-09-01T00:26Z) set the condition to
Blocked-by: #14027(the console-pin bump), refined by comment 5486863947 to "the pin must carry objectui#6828"; #14027 is CLOSED and the devx seat's unlock note (comment 5492894409) measured the pin — re-verified by this seat onorigin/mainata39b02a6:.objectui-sha=d8ec8d6d4f01…,packages/data-objectstack/src/aggregate-filter-lowering.test.tsPRESENT at that pin, andtranslateFilterArray(params.filter)present inpackages/data-objectstack/src/index.tsat that pin (1 hit). ② No PR merged against this card after that comment. File-surface re-check on the merged ref:packages/spec/src/ui/component.zod.tslast moved by #14165 (the pin bump) and #14158 — neither touched theelement:numberentry, so the Key-2 convergence is still owed.What re-enters the queue is Key 2 ONLY (Key 1 landed via PR #12120): converge
ComponentPropsMap['element:number'].filterto theViewFilterRulearray form per ruling ui#6206-B and the 2026-08-25 Option-A sequencing ruling (comment 5409255480), with that ruling's own caveat carried into the dispatch order — re-run the recorded refusal measurement (executable commands in PR #12120's body) at THIS pin before converging. Clause-② yes ⇒ CONTRACT_REVIEW_TIER. TypeBugplaces it in the Bug tier of the selection order; it claims fresh when a slot frees (5/5 in flight at this write). No assignee residue to clear.Generated by Claude Code
claude commentedon Sep 2, 2026
Claim: PM loop,
domain:specseat (seat post #6017) — Key 2 only (Key 1 landed via PR #12120)Session:
session_01GDA48PuRFrHyRfdkBz8m21Branch:
claude/issue-12039-element-number-filter-arrayWorktree:
objectstack-issue-12039-key2Domain:
domain:specFile surface:
packages/spec/src/ui/component.zod.ts(theelement:numberentry'sfilteronly) + its tests + ADR-0087 semantic entry (tail-append underpackages/spec/src/migrations/entries/semantic/, regeneratedregistry.ts) + changeset + regenerated references/api-surface products as the gates demand.packages/spec/src/ui/view.zod.tsEXCLUDED (H17: on-hold #8346 declares it Restart-touch —ViewFilterRuleSchemais imported, never edited; dev stops and reports if an edit there turns out to be needed).Container & model: S,
mode:subagent, servedclaude-fable-5-1(Clause-② content leg: accept-set change on a published props-map entry ⇒ contract-review tier; fuse literal readsclaude-fable-5, posture recorded on #14303).Clause-②: yes.
Ruling implemented: ui#6206-B (one filter orthography platform-wide —
z.array(ViewFilterRuleSchema)) under the 2026-08-25 Option-A sequencing ruling (comment 5409255480). Prerequisite satisfied and re-verified this seat:.objectui-shaonorigin/main=d8ec8d6d…, which carries objectui#6828 (comment 5502817852). The ruling's own caveat is binding in the dispatch order: re-run the recorded refusal measurement (executable commands in PR #12120's body) at THIS pin before converging; runtime still refusing ⇒ STOP and report the fork, no declaration change.Serial constraints:
component.zod.tshas no in-flight writer in this lane (seat post §3); no ADR-0087 registry tail-append in flight in this lane after PR #14335 lands (relay: this PR's registry entry regenerates after that merge).Generated by Claude Code
claude commentedon Sep 2, 2026
os-dev-report
{ "issue": 12039, "status": "done", "branch": "claude/issue-12039-element-number-filter-array", "pr": "https://github.com/objectstack-ai/objectstack/pull/14404", "head": "8068a7f31", "premise_still_valid": true, "summary": "Key 2 only (Key 1 landed via PR #12120). Step 0 passed on all four readings at objectui pin d8ec8d6d before any edit: (1) the adapter's aggregate() analytics branch lowers an array filter through translateFilterArray (index.ts:4924-4926, objectui#6828); (2) aggregate-filter-lowering.test.ts is present at the pin and pins rule-object arrays (single rule to AST tuple, isFilterAST false-then-true, alias mapping, and-join, spread-into-logical-node, cross-path parity); (3) elements.tsx passes filter opaquely to adapter.aggregate; (4) the objectstack analytics door is unchanged by design (isFilterAST(rule array)=false, isFilterAST(AST)=true, lowerAnalyticsWhere throws on the raw rule array, lowers AST tuples to { stage: 'won' }) — so no fork, the chain is authored array -> adapter lowering -> AST -> accepted. Then ComponentPropsMap['element:number'].filter converged from FilterConditionSchema to z.array(ViewFilterRuleSchema).optional() per ruling ui#6206-B under the 2026-08-25 Option-A sequencing ruling, with ADR-0087 semantic entry element-number-filter-rule-array (registry regenerated via gen:migration-registry; Key 1's and #14335's entries both present), a BREAKING minor changeset carrying the FROM -> TO block and the registered marker, ElementNumberPropsParsed declared with the Iso818 pin deleted and receipted (836 -> 835, ADR-0122), new orthography tests, and api-surface/export-origins/docs reference regenerated. Ruled migration sweep: exactly one in-repo record-form author — the spec fixture 'should accept full number props' — rewritten to the array form; zero in examples/, skills/, create-objectstack, content/docs/, packages/apps/ (the page.test.ts hits are dataSource.filter, a different key). Observed, not changed: element:record_picker.filter still reads FilterConditionSchema (component.zod.ts:2141). view.zod.ts untouched (H17). Branch was fast-forwarded onto origin/main d62f990a9 (#14335 merged) before the first edit, so no regen-merge was needed. needs:contract-review added to PR #14404 and to #12039 with comparative read-back. Draft PR, not flipped ready, no auto-merge, not enqueued.", "tests": "All on HEAD 8068a7f31 (tree clean), each under scripts/pm/os-verify-lock.sh with exit codes captured from redirection. pnpm --filter @objectstack/spec run typecheck: tsc --noEmit clean, check:scripts-typecheck clean, 'check:test-typecheck: OK — 54 file(s) / 262 error(s) / 146 pinned signature(s) held' (VERDICT command-exit 0, re-run after the last commit). Touched files: vitest run --maxWorkers=2 src/ui/component.test.ts src/type-alias-convention.pin.test.ts — Test Files 2 passed, Tests 238 passed. Full spec suite: pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 — Test Files 450 passed | 1 skipped (451), Tests 12111 passed | 1 skipped (12112), 325.86s, VERDICT command-exit 0. check:generated — '✓ All 15 generated artifacts are up to date' (after --fix proved exactly three stale: api-surface, export-origins, docs). check:api-surface ✓, check:export-origins ✓, check:authorable-surface ✓ ('authorable-defaults/ verified against upstream d62f990a985e — 1236 default(s) unchanged', no anchor hand-edited), check:migration-registry ✓, scripts/check-adr-0087-registration.mjs ✓ ('1 declared-breaking changeset(s), each carrying an ADR-0087 disposition … registered element-number-filter-rule-array'), check:doc-authoring ✓ ('14375 customer-facing string(s) across 705 spec sources clean'), check:nul-bytes ✓ ('scanned 7843 text file(s) … no raw ASCII control bytes'). Derived union: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (stderr: derived at commit 8068a7f31) = 74 commands; 72 exit 0 — those that first printed PREREQUISITE NOT MET / dist-older-than-src were re-run green after building the lint closure and then the workspace (turbo run build --filter='./packages/*' --filter='./packages/*/*', 70/70 successful): doc-formula-expressions, doc-security-posture, browser-reachable-entries, dual-source-exports, entry-nameability, exported-any, skill-examples ('259 prose examples type-check'), dev-prereqs ('67 package build artifacts present'), dual-build-cjs-loads ('102 published require entry point(s) across 66 package(s) load'), type-check-coverage, type-check-debt ('27 ledger entr(ies) re-measured … none above its recorded number'); check:query-options-erasure ran alone after a first attempt was cut by the ~10-minute foreground cap ('ratchet holds: 67 unswept non-test site(s) in 17 file(s), none new'). The 2 remaining are not runnable here by their own verdicts: check:react-declaration-parity (needs objectui's sdui.manifest.json — the on-demand pin-bump gate) and check-test-completeness (needs a saved turbo test log; NOT MEASURED). Type-level reverse verification against the rebuilt dist (tsc --noEmit --ignoreConfig probe in packages/lint against @objectstack/spec/ui): record-form filter RED with TS2353 naming the '{ field; operator; value }[]' type, array form GREEN (0 errors); probe deleted. No ablation was run (no new gate was added, so none was owed). Repo-wide pnpm lint and the multi-package farm are CI's runs (declared narrowing).", "mcp_calls": "0 — no MCP GitHub calls; every GitHub read/write went through authenticated REST (curl) plus local git", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
claude commentedon Sep 2, 2026
ACCEPT — PR #14404 (head
8068a7f3), Key 2; reviewed against GitHub, not the report. Card complete with this merge (Key 1 landed via PR #12120), soFixes #12039is the right closing line.Ruling compliance (ui#6206-B under the 2026-08-25 Option-A sequencing, comment 5409255480)
d8ec8d6d: the adapter'saggregate()analytics branch lowers an array filter throughtranslateFilterArray(params.filter)(packages/data-objectstack/src/index.ts~4924–4926, objectui#6828), andpackages/data-objectstack/src/aggregate-filter-lowering.test.tsis present at the pin. The analytics door itself still refuses a raw rule-object array by design; the end-to-end chain is authored array → adapter lowering → AST → accepted, which is exactly the sequencing the ruling ordered.ComponentPropsMap['element:number'].filter→z.array(ViewFilterRuleSchema).optional(), the orthography the siblingfilterinputs already declare;view.zod.tsuntouched (H17 export: PDF / print document generation — re-planned for v18, superseding the #1301 NOT_PLANNED closure #8346); no consumer-side alias, no fallback arm.element-number-filter-rule-arrayunder protocol 18, registry regenerated (Key 1's and feat(spec): refuse undeclared keys onaddressandlocationvalues —AddressSchema/LocationValueSchemastrict (#13802) #14335's entries still present); changeset@objectstack/specminor, BREAKING with the FROM → TO block and the registered marker; ADR-0122 pin receipt 836 → 835 withElementNumberPropsParseddeclared.filterpath withinvalid_type(expected: 'array'), the array form is accepted and normalizes operators through the realViewFilterRuleSchema, and a scalarinvalue is refused atfilter.0.value.Contract review (Clause ②) — PASS, in-seat
packages/spec/src/ui/component.zod.ts+packages/spec/src/migrations/**⇒ Clause ② applies.claude-fable-5-1. The fuse literal inscripts/pm/dispatch-gates.mjsreadsclaude-fable-5; mismatch and this seat's floor reading recorded on finding(pm-dispatch):CONTRACT_REVIEW_TIER = 'claude-fable-5'no longer matches the served Fable tierclaude-fable-5-1, so the in-seat review fuse reads a literal mismatch on every current Fable seat #14303, open for maintainer veto.Pre-checks (all on head
8068a7f3)check-clause2-carriers.mjs --pair).origin/main(5ec4f6fa): CLEAN;mainhas not moved on any of this PR's files since the merge base.Fixes #12039.check:react-declaration-parityneeds objectui's manifest;check-test-completenessneeds the CI test log) are CI's — declared narrowing accepted.Observed residue (not this PR's):
element:record_picker.filterstill declaresFilterConditionSchema(component.zod.ts~2141) — the last record-formfilterin the map after this convergence. Filed as a separate finding for triage (number in the follow-up comment); ⛔ not folded here.Landing
needs:contract-reviewcleared on this card and on PR feat(spec): converge ComponentPropsMap['element:number'].filter onto the ViewFilterRule array form (#12039, ui#6206 Option B) #14404 in the same stroke as this verdict.Generated by Claude Code
claude commentedon Sep 2, 2026
Follow-up to the ACCEPT above: the observed residue (
element:record_picker.filterstill onFilterConditionSchema) is filed as #14406 for first-touch grading — measurement-first before any declaration moves, per the same ruling.Generated by Claude Code