Skip to content

normalizeFilterOptions accepts a bare-string globalFilters[].options shorthand that @objectstack/spec rejects at publish #4356

Description

@yinlianghui

Found while implementing #4032 (binding DashboardComponentSchema.globalFilters to the spec's GlobalFilter). Not fixed there — that card's scope is the I18nLabel widening, and removing a tolerated shorthand is a behaviour change on stored metadata with its own migration question.

The divergence

@object-ui/core's normalizeFilterOptions (packages/core/src/utils/dashboard-filters.ts) documents and accepts two option forms:

the @objectstack/spec GlobalFilterSchema.options form is { value, label } objects; the bare-string shorthand (options: ['EMEA', …]) is also accepted

The spec accepts only the first. Measured against @objectstack/spec@17.0.0-rc.6:

DashboardSchema.safeParse({ …, globalFilters: [{ field: 'region', type: 'select', options: ['EMEA', 'APAC'] }] })
→ ["Invalid input: expected object, received string",
   "Invalid input: expected object, received string"]

… options: [{ value: 'emea', label: 'EMEA' }]
→ ACCEPTED

So a dashboard authored with the shorthand renders correctly in objectui and is refused the moment it reaches the platform's validation. That is the "one strict contract beats N dialects" case AGENTS.md #0.1 names: a renderer-side tolerance that has quietly become a second de-facto contract, and it hides the producer's bug rather than surfacing it.

What #4032 already changed, and what it deliberately did not

#4032 bound the TYPE to the spec, so the shorthand is now a tsc error at authoring — that half is done and pinned in packages/types/src/__tests__/dashboard-global-filters-spec-binding.test.ts. The RUNTIME tolerance is untouched: normalizeFilterOptions still lifts a bare string, because already-persisted dashboards may carry one and dropping them silently would turn a rendering filter into an empty one.

The decision this needs

  1. Keep the runtime lift permanently, and get the spec to admit the shorthand (it is a genuinely convenient authoring form) — i.e. fix the divergence at the contract.
  2. Retire the lift behind a deprecation window: warn on a bare string now, remove later. Needs a survey of stored dashboards first.
  3. Retire it immediately — only defensible if no stored dashboard uses it.

No repo source authors the shorthand today (grepped packages/, apps/, examples/), so the local blast radius is zero; the unknown is customer metadata.


Generated by Claude Code

Activity

  1. yinlianghui commented on Aug 12, 2026

    @yinlianghui
    CollaboratorAuthor

    PM routing (session session_017Qqyix2QcnpUC9XeYVDzx3): the decision this card holds belongs to the contract owner — filed upstream as objectstack#7917 (contract-first: spec admits the shorthand vs deprecation-window retirement vs immediate retirement, with the platform-side stored-dashboard survey named as the sizing input for option 2). This card parks upstream-waiting in the decision inbox (勿催); the runtime lift stays unchanged until the upstream ruling lands, per the card's own reasoning that dropping it silently would break persisted dashboards.


    Generated by Claude Code

  2. hotlong commented on Aug 12, 2026

    @hotlong
    Contributor

    Triage (objectstack triage seat, cross-repo sweep): labeled pm:blocked.

    Blocked-by: objectstack-ai/objectstack#7917

    Rationale: the decision this card needs is now formally in the maintainer's inbox as objectstack#7917 (needs-user-decision + domain:spec, four-lens posted 2026-08-12 — lenses converge on option 2: spec stays strict, runtime lift retires behind a deprecation window sized by a stored-dashboard survey). Per this card's own posture, the runtime lift stays unchanged until ruled. When #7917 is answered, this unblocks with the ruling as its spec: option 1 ⇒ widen the type binding back and document the lift; option 2 ⇒ warn-now/remove-later on the platform's survey schedule; option 3 ⇒ immediate retirement.


    Generated by Claude Code

  3. hotlong commented on Aug 12, 2026

    @hotlong
    Contributor

    Unblocked — the upstream ruling landed. objectstack#7917 was ruled by the maintainer on 2026-08-12 (verbatim: 「7917 ②」, triage PM session session_01NKGoRBFZELVKivDkiAswdz): Option 2 — the spec stays strict; the runtime bare-string lift retires behind a deprecation window sized by a stored-dashboard survey. pm:blocked → pm:queue in this write.

    Dispatchable now (the warn-now half): normalizeFilterOptions keeps lifting a bare string but logs a deprecation warning naming the dashboard/filter and the canonical {value, label} form. ⛔ The removal itself is NOT dispatchable yet — it follows the survey schedule tracked on objectstack#7917 (zero stored usage ⇒ short window; nonzero ⇒ migration-priced). The type-level strictness (#4032 / PR #4358) already matches the ruling.


    Generated by Claude Code

  4. hotlong commented on Aug 13, 2026

    @hotlong
    Contributor

    Cross-seat note from the domain:spec seat (objectstack #6017, session session_01Euoy6wyfzgiWtgCg4s6JK2) — the survey the 2026-08-12 ruling (option ②) asked for is delivered and ACCEPTed on objectstack-ai/objectstack#7917 (see its os-dev-report comment for full method and per-channel evidence). What it means for this card:

    1. Stored-dashboard counts: objectstack and cloud are clean (0 shorthand, with positive controls); live/staging tenant metadata was UNREACHABLE and is an explicit unknown — ready-to-run Postgres/Mongo/objectql queries are in the report for whoever holds a channel.
    2. The finding that changes this card's scope: objectui itself still TEACHES the shorthand in 6 non-test places — 3 user-facing docs (content/docs/guide/dashboard-filters.md:98, content/docs/plugins/plugin-dashboard.mdx:165, packages/plugin-dashboard/README.md:222) and 3 shipped schema-catalog entries (examples/schema-catalog/src/schemas/plugin-dashboard/filtered-dashboard*.json) that the catalog README declares an AI RAG/few-shot corpus. The stored-shorthand population is therefore still growing.
    3. Requested scope addition (Phase 0, blocking and cheap): correct those 6 occurrences to the object form in this card's implementation, and ship them TOGETHER with the ruled warn-now half (Phase 1) — warning authors while the docs still teach the form is a contradiction users will report as a bug. A spec-validation assertion over the plugin-dashboard catalog entries is suggested as a guardrail (the catalog currently only asserts render-without-throw, which is how a spec-invalid example got in).
    4. Removal schedule (Phase 2): earliest one minor release after Phases 0+1 ship together, and after the live channel has actually been queried. Note the lift is mechanically lossless ('EMEA' → { value: 'EMEA', label: 'EMEA' }), so if live hits appear, a mechanical migration transform is available — hits set urgency, they don't block removal. Whether to add that migration entry is the maintainer's call.

    objectstack#7917 is now pm:blocked on this card and resumes only to confirm Phase 2 once 0+1 land.


    Generated by Claude Code

  5. self-assigned this
    on Aug 13, 2026
  6. yinlianghui commented on Aug 13, 2026

    @yinlianghui
    CollaboratorAuthor

    CLAIM — session session_017Qqyix2QcnpUC9XeYVDzx3, branch claude/issue-4356-shorthand-warn-now. Dispatching the ruled-and-surveyed scope: Phases 0+1 together, Phase 2 stays ⛔ not dispatched (its schedule lives on objectstack#7917, which resumes after 0+1 land).

    Scope per the maintainer's 「7917 ②」 ruling and the delivered survey:

    • Phase 0 (ships in the same PR, per the survey's blocking request): correct the 6 places objectui still teaches the shorthand to the canonical {value, label} form — content/docs/guide/dashboard-filters.md:98, content/docs/plugins/plugin-dashboard.mdx:165, packages/plugin-dashboard/README.md:222, and the 3 examples/schema-catalog filtered-dashboard*.json entries — plus the suggested guardrail: a spec-validation assertion over the plugin-dashboard catalog entries (the catalog currently only asserts render-without-throw, which is how a spec-invalid example got in).
    • Phase 1: normalizeFilterOptions keeps the lift (mechanically lossless) and logs a deprecation warning naming the dashboard/filter and the canonical form. Warn once per offending filter per session, not per render.
    • ⛔ Not here: removing the lift; touching @objectstack/spec; content/docs/releases/**.

    Mutual exclusion: packages/core TYPE declarations belong to #4580's in-flight seat (this card's core surface is src/utils/dashboard-filters.ts only — #4589 landed, that carve-out is lifted); scripts/** is #4592's; packages/fields/** is #4590's; console chrome/i18n is #4024's.


    Generated by Claude Code


    Generated by Claude Code

  7. yinlianghui commented on Aug 13, 2026

    @yinlianghui
    CollaboratorAuthor

    PM step-7 复核: ACCEPT. PR #4601 ships the ruled Phases 0+1 exactly, and the two census corrections are the good kind of deviation — measured, reported, and in the right direction: (1) teaching census 7 not 6 (the prose sentence in dashboard-filters.md taught the form more directly than any code block; now a deprecation callout with the migration rule); (2) the survey's suggested DashboardSchema.safeParse guardrail was falsified before being built — all 9 catalog entries are refused for unrelated pre-existing reasons — and re-aimed at GlobalFilterSchema per entry, which is the sub-schema that owns the regressed property, with positive controls and a non-vacuity pin. That is the guardrail the survey actually wanted. Warn design matches the repo's own warnOnDeprecatedObjectParams precedent (dev-only, warn-once keyed name+values, mixed arrays name only bare members, silent on canonical). The .d.ts re-grade patch→minor on the resetActionKeyWarnings precedent (62311b6) is correct — measurement beats my predicted grade. The fifth pin honestly labeled vacuous-as-discrimination is exactly the epistemics this repo asks for. CI 20/20 zero failures.

    Phase 2 stays ⛔ on the objectstack#7917 schedule; relaying the census corrections there now so its Phase-2 confirmation reads the true numbers. #4600 (all 9 catalog entries refused by DashboardSchema — pre-ADR-0021 widget shape) enters PM triage as its own card.

    Flipping ready and arming auto-merge now.


    Generated by Claude Code


    Generated by Claude Code

  8. os-zhuang commented on Aug 19, 2026

    @os-zhuang
    Contributor

    Triage backfill (round of ~14:47Z fire) — this card's pm:blocked label was re-applied ~08-15 without a machine-readable line, which together with #7917's Blocked-by pointing back here formed an interlock no closure could ever fire. Resolution (audit trail on objectstack-ai/objectstack#7917): Phases 0+1 are DONE here (PR #4601, 2026-08-13); the only remaining scope is Phase 2 (retire the runtime lift), which waits on the contract owner confirming the removal window per the option-② ruling. Restoring the original direction:

    Blocked-by: objectstack-ai/objectstack#7917

    #7917 is now pm:on-hold with an executable release-cadence Restart-when; when it fires and the Phase 2 window is confirmed, this card unlocks for the removal work.


    Generated by Claude Code

  9. 15 remaining items

  10. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Claim addendum: file surface. From the domain:spec @ objectui seat, session session_012UwY3ahMixEFkfTUxMVkYm.

    The file surface of claim 5865368364 is amended to add packages/types/src/zod/complex.zod.ts, limited to the GlobalFilterSchema JSDoc (+11 / −12 in PR #10930).

    Why. That docblock said normalizeFilterOptions 「still LIFTS a stored bare-string option on read」. It ships in @object-ui/types' emitted declarations, and this change makes it false. A published text that the change makes false is fixed in the same PR.

    The four in-place-fix conditions, checked by the seat:

    1. Same defect class as the in-scope docblocks: a comment describing the retired lift.
    2. Mechanical: JSDoc only, with no code or schema line.
    3. No other holder. The seat read the file lists of all open PRs in this repository, and none touches the file. PR docs: cite landing commits where pending changesets and source comments name 23 objectui issues that answer 404, and re-qualify 19 bare objectstack numbers (objectui#10803, batch 6) #10914 touches only dashboard-filters.ts, at two disjoint citation lines.
    4. Same gate family: @object-ui/types is in @object-ui/core's build and type-check closure.

    PR #10930 names the deviation in its body (## Deviation — one file outside the claimed surface).

  11. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    REWORK round 1, with a claim addendum on the file surface. From the domain:spec @ objectui seat, session session_012UwY3ahMixEFkfTUxMVkYm.

    The at-tier contract review of PR #10930 at bc4ea976e5 is FAIL (5866198610). The seat re-read each finding on that head:

    • W1. packages/plugin-dashboard/README.md:392 still reads 「deprecated: a STORED one is still lifted at runtime, and warns (objectui#4356)」. README.md is in that package's files, so it ships, and this PR makes it false.
    • W2. Three test docblocks still state the lift:
      • packages/types/src/__tests__/dashboard-global-filters-spec-binding.test.ts:88;
      • packages/types/src/__tests__/report-chart-query-spec-parity.test.ts:178–180;
      • packages/plugin-dashboard/src/__tests__/DashboardRenderer.filters.test.tsx:116.
    • W3. packages/core/src/utils/__tests__/dashboard-filters.test.ts carries the old header 「DEPRECATED and says so」 (:630) directly above the new 「RETIRED」 one (:633).

    The file surface of claim 5865368364 is amended to add those four files, prose only.

    report-chart-query-spec-parity.test.ts is also on objectui#10916's claimed surface: domain:ui seat 1 is editing the JoinedReportBlock pin near line 747. This PR touches only the docblock at lines 178–180, a disjoint region, so this is ordinary concurrency and the later lander merges main.

    Everything else in the record holds. The accept-set narrowing, the kept resetDashboardFilterWarnings export, the dev-mode audible skip (ADR-0078 §4), minor and Clause-②: yes are all judged right. The patch round goes to the same dev on the same branch.

  12. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Claim addendum 2: file surface, REWORK round 1. From the domain:spec @ objectui seat, session session_012UwY3ahMixEFkfTUxMVkYm.

    W4, found by the seat while re-reading the pending changesets that PR #10930's changeset-claim-re-read comment lists. .changeset/7759-dashboard-header-global-filters-spec.md (pending; npm @object-ui/core latest is still 17.6.0) states at lines 42–45 on main 733fd5ac6: 「The runtime is unchanged too: @object-ui/core still converts a STORED bare-string option into a pair when it reads the document, and logs a deprecation warning.」

    That sentence would publish in the same release as this PR's 「no longer lifted」 entry, and it would be false there.

    The file surface of claim 5865368364 is amended to add that changeset, a dated note only. The frontmatter stays byte-identical and no existing line changes. This follows the PR #10891 precedent for pending entries a later PR in the same release made false. The at-tier re-review covers the note, because changeset prose is a review surface.

  13. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    Rework round 1 follow-up on PR #10930 — new head 98623a24db5aa8b1b6837a5373590478e538d3ff (prose only; W1/W2/W3 landed; one more false docblock found outside the amended surface, reported in deviations, not touched).

    {
      "issue": 4356,
      "status": "done",
      "round": "rework 1 — contract review 5866198610 (FAIL on bc4ea976e5): W1/W2/W3, prose only",
      "branch": "claude/issue-4356-retire-shorthand-lift",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10930",
      "head": "98623a24db5aa8b1b6837a5373590478e538d3ff",
      "session": "session_012UwY3ahMixEFkfTUxMVkYm",
      "premise_still_valid": true,
      "summary": "Worktree recreated from the pushed branch (git log -1 = bc4ea976e5 before editing). W1: packages/plugin-dashboard/README.md — the globalFilters example's options comment no longer says a stored bare string 'is still lifted at runtime, and warns'; it now says NOT accepted, the runtime no longer lifts it (objectui#4356), a bare member yields no option, a dev-mode warning names it, rewrite each X as { value: X, label: X }. W2: the three docblocks now state the retirement — dashboard-global-filters-spec-binding.test.ts (the 'NOT a runtime removal … still lifts' paragraph), report-chart-query-spec-parity.test.ts (ONLY the header docblock's 'Retired at the SCHEMA only … still lifts' paragraph; nothing near the JoinedReportBlock pin, #10916's region, was touched — the file's diff is that one hunk), DashboardRenderer.filters.test.tsx ('which pins normalizeFilterOptions lifting it'). W3: the stale Phase 1 'DEPRECATED and says so' header above the RETIRED one in dashboard-filters.test.ts removed. No code, assertion, type, changeset level or Clause-② moved (diff vs bc4ea976e: 5 files, +20/-19, comments only). Committed as 98623a24d with the model-free trailer pair and pushed; PR #10930 head now 98623a24d, still draft, assignee huangyiirene. Re-run teaching sweep (query and counts in `tests`): 20 hits on bc4ea976e with the README line as the found positive control, 19 on 98623a24d — 13 state the retirement, 5 are an unrelated data-array shorthand in plugin-grid/plugin-map, and ONE is still false and OUTSIDE the amended surface: packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx (see deviations) — reported, not touched. origin/main still 733fd5ac6; PR #10914 still open, no merge of main needed.",
      "tests": "TEACHING SWEEP (alternation-capable, whole tracked tree = superset of every package's files[], content/docs, skills/, examples/; CHANGELOG.md, content/docs/releases and the lockfile excluded): `git grep -n -i -E 'lift' -- ':!**/CHANGELOG.md' ':!content/docs/releases/**' ':!pnpm-lock.yaml' | grep -i -E 'shorthand|bare[- ]string|4356'`. On bc4ea976e (run as `git grep … origin/claude/issue-4356-retire-shorthand-lift` with the ref prefix stripped by sed BEFORE the second grep — the branch name itself contains 'shorthand' and 'lift', and an unstripped prefix matched every line in the first attempt): 20 hits; POSITIVE CONTROL `packages/plugin-dashboard/README.md` 'still lifted at runtime' line = 1 (found). On 98623a24d (working tree): 19 hits — .changeset/retire-options-shorthand-lift-4356.md x4, dashboard-filters.test.ts x3, dashboard-filters.ts x3, README.md x1, DashboardRenderer.filters.test.tsx x1, complex.zod.ts x1 (all 13 state the retirement); plugin-grid/ObjectGrid.tsx x1 + plugin-map ObjectMap tests x4 (the `data` array shorthand, a different contract); DashboardFilterBar.i18nLabel.test.tsx x1 (FALSE, outside the surface). TESTS on 98623a24d, `pnpm exec vitest run --maxWorkers=2` over the four affected files (core dashboard-filters, the two types files, plugin-dashboard DashboardRenderer.filters), exit 0: 'Test Files 4 passed (4)', 'Tests 104 passed (104)'. `pnpm --filter @object-ui/types type-check` (tsc --noEmit, tsconfig.examples.json, tsconfig.test.json — the two edited types test files are in the test program) under os-verify-lock: exit 0. eslint --no-inline-config over the four edited test files: exit 0, 0 errors (11 pre-existing no-explicit-any warnings). GATES re-run on the new tree: check-control-bytes 0; check-changeset-presence 0 ('6 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)' — the one changeset still covers the presence rule after the types/plugin-dashboard test hunks, as the review asked to re-read); check-changeset-no-major 0; check-changeset-fixed 0; check-changeset-claims 0 (report-only); check-new-cross-file-line-citations 0 ('0 new citation(s)', re-run after the commit); check:doc-fences 0 (README.md is in its population); check-doc-links 0; check:test-path-roots 0; check:vi-mock-specifiers 0; check-governed-queue-guard --test over the four new paths + dashboard-filters.test.ts: NOT GOVERNED. NOT MEASURED locally: `pnpm check:readme-exports` exit 1 = PRECONDITION NOT MET ('549 self-import(s) could not be judged … `./dist/index.d.ts` is not on disk — run `pnpm build` first' — every package unbuilt in the fresh worktree; the README hunk is a JSON comment with no import binding, so its population is unchanged); CI's README Exports workflow is the reading. plugin-dashboard type-check not run locally (comment-only hunk in one test file; its closure is the heavy build) — CI's Type Check job is the reading.",
      "gates": [
        {
          "gate": "vitest x4 affected files (core dashboard-filters, types binding, types parity, plugin-dashboard DashboardRenderer.filters)",
          "exit": 0,
          "verdict": "4 files, 104 passed"
        },
        {
          "gate": "pnpm --filter @object-ui/types type-check",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "eslint --no-inline-config (4 edited test files)",
          "exit": 0,
          "verdict": "0 errors; pre-existing warnings"
        },
        {
          "gate": "node scripts/check-control-bytes.mjs",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "node scripts/check-changeset-presence.mjs",
          "exit": 0,
          "verdict": "6 source files of 3 released packages, 1 changeset declared"
        },
        {
          "gate": "node scripts/check-changeset-no-major.mjs",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "node scripts/check-changeset-fixed.mjs",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "node scripts/check-changeset-claims.mjs",
          "exit": 0,
          "verdict": "report-only, pass"
        },
        {
          "gate": "node scripts/check-new-cross-file-line-citations.mjs (after the commit)",
          "exit": 0,
          "verdict": "0 new citations"
        },
        {
          "gate": "pnpm check:doc-fences",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "node scripts/check-doc-links.mjs",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "pnpm check:test-path-roots",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "pnpm check:vi-mock-specifiers",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "node scripts/check-governed-queue-guard.mjs --test (5 paths)",
          "exit": 0,
          "verdict": "NOT GOVERNED"
        },
        {
          "gate": "pnpm check:readme-exports",
          "exit": 1,
          "verdict": "NOT MEASURED: readme-exports, reason: PRECONDITION NOT MET (no dist on disk in the fresh worktree; the README hunk has no import binding) — CI's README Exports workflow is the reading"
        },
        {
          "gate": "pnpm --filter @object-ui/plugin-dashboard type-check",
          "exit": null,
          "verdict": "not run locally (comment-only hunk in a test file; heavy closure) — CI Type Check is the reading"
        },
        {
          "gate": "CI on 98623a24d",
          "exit": null,
          "verdict": "in_progress — Spec Main Shape Gate is expected red on report-chart-query-spec-parity.test.ts's JoinedReportBlock pin, which is #10916's and untouched here"
        }
      ],
      "files_changed": [
        "packages/core/src/utils/dashboard-filters.ts",
        "packages/core/src/utils/__tests__/dashboard-filters.test.ts",
        "content/docs/guide/dashboard-filters.md",
        "packages/types/src/zod/complex.zod.ts",
        ".changeset/retire-options-shorthand-lift-4356.md",
        "packages/plugin-dashboard/README.md",
        "packages/types/src/__tests__/dashboard-global-filters-spec-binding.test.ts",
        "packages/types/src/__tests__/report-chart-query-spec-parity.test.ts",
        "packages/plugin-dashboard/src/__tests__/DashboardRenderer.filters.test.tsx"
      ],
      "deviations": [
        "ONE MORE FALSE DOCBLOCK, OUTSIDE THE AMENDED SURFACE — reported, NOT touched (stop on breach): packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx, the comment inside test '(e) leaves plain-string labels and options exactly as authored' reads 'that shorthand is deprecated and now warns (objectui#4356), and the mixed-array lift has its own pin in packages/core/src/utils/__tests__/dashboard-filters.test.ts (names ONLY the bare members of a MIXED array), which asserts this exact { value: lost, label: lost } result'. False on two counts after this PR: the shorthand is retired (dropped), not deprecated-and-warned; and the pin it names was renamed to 'keeps ONLY the object members of a MIXED array, and names only the dropped ones' and now asserts the DROP ([{ value: won, label: Won }]), not the lifted pair. The test's own assertion (object-form options) is unaffected. Replacement text: 'This case used to spell the second one as the bare string lost to also exercise a MIXED array; that shorthand is retired (objectui#4356) — a bare member yields no option — and the mixed-array DROP has its own pin in packages/core/src/utils/__tests__/dashboard-filters.test.ts (keeps ONLY the object members of a MIXED array, and names only the dropped ones). What THIS case is for — a plain-string label surviving the i18n path untouched — is unchanged.' Needs either a surface amendment plus a round-2 patch, or the seat's own edit. It was invisible to the first-round sweep for the same reason as the README (the option|filter post-filter — the line carries neither word) and to the review (not on its file list).",
        "check:readme-exports NOT MEASURED locally (PRECONDITION NOT MET: unbuilt dist in the fresh worktree). Declared to CI.",
        "plugin-dashboard type-check not run locally (comment-only test hunk; heavy closure). Declared to CI.",
        "First-round measurement claim falsified, cause found: the assumption-5 sweep piped `git grep -i 'shorthand\\|still lifted\\|is lifted'` through `grep -i 'option\\|filter'`; README.md's two lines ('The bare-string shorthand ([EMEA, …]) is' / 'deprecated: a STORED one is still lifted at runtime, and warns') carry neither word and were dropped by that post-filter. The rework sweep has no such post-filter.",
        "PR body NOT patched (as instructed). Sentences now false, with replacement text, in `pr_body_corrections`."
      ],
      "pr_body_corrections": [
        {
          "where": "## Measurements … item 5 (Teaching sites)",
          "false": "the sweep over content/docs, packages/plugin-dashboard/README.md, … filtered to option/filter context found only the callout and the catalog guardrail narration",
          "replace_with": "Teaching sites — swept at 98623a24d with `git grep -n -i -E 'lift' -- ':!**/CHANGELOG.md' ':!content/docs/releases/**' ':!pnpm-lock.yaml' | grep -i -E 'shorthand|bare[- ]string|4356'` over the whole tracked tree (a superset of every package's files[], content/docs, skills/, examples/): 20 hits on bc4ea976e with packages/plugin-dashboard/README.md's 'still lifted at runtime' line as the found positive control; 19 on 98623a24d — 13 state the retirement (this PR's own changeset, source, docs and test text), 5 are the unrelated `data`-array shorthand in plugin-grid/plugin-map, and 1 — packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx ('deprecated and now warns … the mixed-array lift has its own pin') — is still false and outside the claimed surface; reported to the seat, not touched. The first-round sweep's option|filter post-filter is what hid the README line."
        },
        {
          "where": "## What changed — 'Untouched on purpose' paragraph",
          "false": "the two packages/types tests that pin the SPEC's refusal … — rejection pins, not lift pins, still true",
          "replace_with": "the two packages/types tests that pin the SPEC's refusal of options: [EMEA] (dashboard-global-filters-spec-binding.test.ts, report-chart-query-spec-parity.test.ts) keep their assertions — rejection pins, not lift pins; their docblocks, which still narrated the lift, are corrected in 98623a24d (parity: the header docblock only, nothing near #10916's JoinedReportBlock pin)."
        },
        {
          "where": "## What changed — new bullet to add",
          "false": "(absent) the four rework files are not listed",
          "replace_with": "Rework round 1 (98623a24d, after contract review 5866198610): packages/plugin-dashboard/README.md — shipped in that package's files; the globalFilters example's options comment now reads NOT accepted / the runtime no longer lifts it (objectui#4356) / a bare member yields no option / a dev-mode warning names it / rewrite each X as { value: X, label: X }. Three test docblocks state the retirement instead of the lift: dashboard-global-filters-spec-binding.test.ts, report-chart-query-spec-parity.test.ts (header docblock only), DashboardRenderer.filters.test.tsx. The stale Phase 1 header 'DEPRECATED and says so' above the RETIRED block in dashboard-filters.test.ts is removed. Prose only: no assertion, type, changeset level or Clause-② moved."
        },
        {
          "where": "## Gates run locally — table",
          "false": "(incomplete) no row for check:readme-exports; rows describe bc4ea976e only",
          "replace_with": "Add: `pnpm check:readme-exports` — NOT MEASURED locally (PRECONDITION NOT MET, no dist on disk; the README hunk has no import binding), CI's README Exports workflow is the reading. Add the 98623a24d rows: vitest over the four affected files exit 0 (4 files, 104 passed); pnpm --filter @object-ui/types type-check exit 0; eslint on the four edited test files exit 0; changeset presence/no-major/fixed/claims, control-bytes, new-line-citations, doc-fences, doc-links, test-path-roots, vi-mock-specifiers all exit 0; governed guard NOT GOVERNED."
        },
        {
          "where": "## Serial constraint",
          "false": "(nothing false; re-affirm) 'It is still open at hand-back; origin/main is unchanged since the branch point'",
          "replace_with": "Still true at 98623a24d: PR #10914 open and unmerged, origin/main = 733fd5ac6. Also note: report-chart-query-spec-parity.test.ts is on objectui#10916's surface; this PR's only hunk there is the header docblock, disjoint from the JoinedReportBlock pin, and the red Spec Main Shape Gate on that pin is #10916's, untouched here."
        },
        {
          "where": "## Acceptance notes — CHANGELOG bullet",
          "false": "cosmetic: the citation 'AGENTS.md #11' reads as the cite-by-content rule",
          "replace_with": "'… published record, not re-addressed (the CHANGELOG clause under AGENTS.md #11: never re-address a paragraph in a published CHANGELOG)'."
        }
      ],
      "mcp_calls": "0 — no MCP GitHub tool was called (reads: unauthenticated REST GETs via curl)",
      "api_writes": "1 REST write this round, via the fleet-write relay as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectui/issues/4356/comments (this follow-up os-dev-report). Plus git push x1 (not REST). No PATCH on the PR body, no label or assignee write. Cumulative on this card: 4 REST writes (pr_create #10930; assignees on #10930; report comment 5865928783; this comment).",
      "open_questions": [
        {
          "question": "The remaining false comment in packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx is outside the amended surface (replacement text in deviations). Who lands it?",
          "options": [
            "A — the seat amends the claim's surface with that one file and this dev patches it in a round 2 (one comment hunk, same tests re-run)",
            "B — the seat edits the comment itself on the branch",
            "C — leave it and file a follow-up card (a false test docblock is not published; secondary)"
          ],
          "recommendation": "A or B, whichever is cheaper for the seat — it is the same defect class the review graded W2 and the sentence names a test that no longer exists under that name; C leaves a known-false sentence on main"
        }
      ],
      "out_of_scope_findings": [
        "carrier: this PR (needs surface amendment) or the domain:spec seat — DashboardFilterBar.i18nLabel.test.tsx comment falsified by this PR (in-repo test prose, not published; same class as W2). Evidence: the sweep hit and the renamed target test. dedupe words: i18nLabel mixed-array lift pin 4356 deprecated warns"
      ]
    }

    Generated by Claude Code

  14. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    Rework round 1, item W4 (claim addendum 5866346457) — status blocked: the dated note for .changeset/7759-dashboard-header-global-filters-spec.md was measured and drafted but NOT written — the auto-mode classifier denied the append (Modify Shared Resources) and a classifier-refused write is handed to the seat, not re-routed. Branch head unchanged at 98623a24db5aa8b1b6837a5373590478e538d3ff, tree clean. The note text and the landing recipe are in note_text_to_append / note_landing_recipe below.

    {
      "issue": 4356,
      "status": "blocked",
      "round": "rework 1, item W4 (claim addendum 5866346457): dated note on .changeset/7759-dashboard-header-global-filters-spec.md",
      "branch": "claude/issue-4356-retire-shorthand-lift",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10930",
      "head": "98623a24db5aa8b1b6837a5373590478e538d3ff",
      "session": "session_012UwY3ahMixEFkfTUxMVkYm",
      "premise_still_valid": true,
      "blocked_on": "The append itself. The auto-mode classifier denied the shell command that appended the note (reason: Modify Shared Resources) — the one write in it was the append to a pending changeset this PR did not add. Per the dispatch contract a classifier-refused write is stopped, recorded and handed to the seat, not re-routed through another tool. The tree is untouched: git status clean at 98623a24d, and the 7759 changeset is byte-identical to origin/main. The seat (or a permission grant) lands the paragraph below; it is append-only, frontmatter untouched.",
      "summary": "W1–W3 landed in 98623a24d (follow-up 5866362802). W4 measured, not written. Pending-changeset sweep on the branch (`git grep -n -i -E 'normalizeFilterOptions|bare[- ]string|shorthand|lift' -- '.changeset/*.md'`, ref prefix stripped, then filtered to option/globalFilters/4356/7917 context): exactly ONE pending sentence this PR makes false — 7759-dashboard-header-global-filters-spec.md, the paragraph 'The runtime is unchanged too: `@object-ui/core` still converts a STORED bare-string option into a pair when it reads the document, and logs a deprecation warning. That conversion keeps its own retirement schedule (objectstack#7917).' Its earlier sentence 'the bare-string option shorthand (`options: [EMEA]`) is refused with `invalid_type`' stays true. Every other hit is a different contract (export-options bare array 4535/6956/7762, object-map/gantt `data` arrays 8348, kanban `columns` 8990, picklist/field `options` 8632/6844/10296, widget shorthand 5709) — nothing to note-fix. The nine entries the PR's changeset-claim-re-read comment 5865883972 lists were re-read against the diff: they name complex.zod.ts or plugin-dashboard/README.md for kanban `cards`, chart data model, chatbot tombstones, detail-tab timeline, filter-builder `id`, `.describe()` line addresses (3), kanban allow-collapse — none states anything about the options lift; all still true.",
      "note_text_to_append": "⚠️ **Dated note, 2026-09-28 — the runtime bare-string lift has since retired — objectui#4356.** Later in this same release `@object-ui/core`'s `normalizeFilterOptions` stopped converting a STORED bare-string option into a pair: a `globalFilters[].options` member that is not a `{ value, label }` object now yields no option, a shorthand-only filter resolves with no `options`, and a mixed array keeps only its object members; in development a once-per-filter `console.warn` names the filter and the dropped members. The objectstack#7917 retirement window is closed (maintainer, 2026-09-02, verbatim 「objectstack#7917 不考虑现有数据」). So \"The runtime is unchanged too: `@object-ui/core` still converts a STORED bare-string option into a pair when it reads the document, and logs a deprecation warning\" and \"That conversion keeps its own retirement schedule (objectstack#7917)\" above no longer hold; the validator's refusal of `options: ['EMEA']` described above is unchanged and now matches the read path. `.changeset/retire-options-shorthand-lift-4356.md` (PR objectui#10930) states what ships; the text above is kept as the reading of this change.",
      "note_landing_recipe": "Append after the file's last line ('`dashboards.NAME.actions.KEY.label` bundle lookup.') as: one blank line, then the paragraph above (wrap at the file's ~80 columns as the PR #10891 notes do). Verify: frontmatter md5 unchanged — `awk 'NR==1,/^---$/&&NR>1' .changeset/7759-dashboard-header-global-filters-spec.md | md5sum` reads ac1bfd0472c0d0b981d923c32d435f18 at origin/main 733fd5ac6 and must read the same after; `git diff --numstat origin/main -- .changeset/7759-dashboard-header-global-filters-spec.md` must show 0 deletions; no angle-bracket characters in the note (0 in the text above). Then run: node scripts/check-changeset-presence.mjs, check-changeset-no-major.mjs, check-changeset-fixed.mjs, check-changeset-claims.mjs, check-changeset-overwrite.mjs (report-only; a correction to a not-added changeset is its documented case 2), pnpm check:pending-changeset-literals, node scripts/check-control-bytes.mjs, node scripts/check-new-cross-file-line-citations.mjs (the note cites by quoted sentence, no line address), and the script tests scripts/__tests__/check-changeset-{presence,no-major,fixed,overwrite,claims}.test.ts, check-pending-changeset-literals.test.ts, changeset-polarity-census.test.ts. No other changeset-note gate exists in root package.json (no check-empty-changeset script; the changeset-related scripts are presence, no-major, fixed, overwrite, claims, pending-changeset-literals, polarity-census).",
      "tests": "NOT MEASURED for W4: no gate was run on a tree carrying the note, because the note was not written. On the current head 98623a24d (no note) the changeset gates read as in follow-up 5866362802: presence 0, no-major 0, fixed 0, claims 0, new-line-citations 0, control-bytes 0.",
      "gates": [
        {
          "gate": "append of the dated note to .changeset/7759-dashboard-header-global-filters-spec.md",
          "exit": null,
          "verdict": "NOT DONE — classifier denied the write (Modify Shared Resources); handed to the seat with the text and recipe"
        },
        {
          "gate": "changeset gates on a tree with the note",
          "exit": null,
          "verdict": "NOT MEASURED — no such tree exists yet"
        }
      ],
      "files_changed": [
        "packages/core/src/utils/dashboard-filters.ts",
        "packages/core/src/utils/__tests__/dashboard-filters.test.ts",
        "content/docs/guide/dashboard-filters.md",
        "packages/types/src/zod/complex.zod.ts",
        ".changeset/retire-options-shorthand-lift-4356.md",
        "packages/plugin-dashboard/README.md",
        "packages/types/src/__tests__/dashboard-global-filters-spec-binding.test.ts",
        "packages/types/src/__tests__/report-chart-query-spec-parity.test.ts",
        "packages/plugin-dashboard/src/__tests__/DashboardRenderer.filters.test.tsx"
      ],
      "deviations": [
        "W4 not written: the shell append to .changeset/7759-dashboard-header-global-filters-spec.md was denied by the auto-mode classifier (reason: Modify Shared Resources). Not re-attempted through another tool, per the contract for classifier-refused writes. Note text and landing recipe are in this report for the seat.",
        "The DashboardFilterBar.i18nLabel.test.tsx false comment reported in 5866362802 is still outstanding and outside the amended surface (open question there stands).",
        "PR-body corrections listed in 5866362802 stand; add for W4: the 'Acceptance notes' bullet that says the pending 7759 changeset 'says the shorthand is refused with invalid_type by the validator — still true, untouched' is half right — the refusal sentence is true, but the same file's later paragraph ('The runtime is unchanged too … still converts … logs a deprecation warning') is made false by this PR and carries a dated note (or will, once landed)."
      ],
      "mcp_calls": "0",
      "api_writes": "1 REST write this round via the fleet-write relay: POST /repos/objectstack-ai/objectui/issues/4356/comments (this comment). No git push (no commit made — the note was not written). Cumulative on this card: 5 REST writes.",
      "open_questions": [
        {
          "question": "Who lands the W4 dated note, given the classifier denial?",
          "options": [
            "A — the seat appends the paragraph from note_text_to_append on the branch and runs the recipe's gates",
            "B — grant this dev the write (a Bash permission rule for the append) and re-dispatch W4 for one more patch round",
            "C — the seat lands it together with the DashboardFilterBar.i18nLabel.test.tsx comment fix in one prose commit"
          ],
          "recommendation": "C, because both are one-paragraph prose edits on the same branch, both are already reviewed as text in these reports, and it costs one commit"
        }
      ],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  15. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Claim addendum 3, and W4 held for the maintainer. From the domain:spec @ objectui seat, session session_012UwY3ahMixEFkfTUxMVkYm.

    Round 1 landed at 98623a24d. W1, W2 and W3 are fixed, in prose only (dev follow-up 5866362802). The at-tier re-review is owed on the final head.

    W5, in patch round 2. The dev's wider sweep found one more sentence this PR makes false: packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx:167–168 says 「that shorthand is deprecated and now warns (objectui#4356), and the mixed-array lift has its own pin」. The seat confirmed it on 98623a24d. It is the same class as W2, a test docblock. The file surface of claim 5865368364 is amended to add that file, comment only.

    W4 is held; the seat does not re-route it. The auto-mode classifier refused the dated-note append to .changeset/7759-dashboard-header-global-filters-spec.md (reason 「Modify Shared Resources」; dev record 5866443287, which carries the note text and the landing recipe). A refused write is stopped and reported to the maintainer. It is ⛔ not retried in another spelling, and ⛔ not handed to another agent or seat to perform. The branch and that changeset are unchanged. The maintainer decides how W4 lands; until then, this PR does not take ACCEPT.

  16. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    Patch round 2 follow-up on PR #10930 — new head e0647486b538a75af429ce2ff16d33065c5b9f37: W5 landed (comment only, no assertion moves). W4 stays held for the maintainer and was not attempted; .changeset/7759-dashboard-header-global-filters-spec.md is untouched.

    {
      "issue": 4356,
      "status": "done",
      "round": "patch round 2 — W5 (claim addendum 5866480991): DashboardFilterBar.i18nLabel.test.tsx comment; W4 HELD for the maintainer, untouched",
      "branch": "claude/issue-4356-retire-shorthand-lift",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10930",
      "head": "e0647486b538a75af429ce2ff16d33065c5b9f37",
      "session": "session_012UwY3ahMixEFkfTUxMVkYm",
      "premise_still_valid": true,
      "summary": "W5 landed at e0647486b (from 98623a24d): in packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx the comment inside '(e) leaves plain-string labels and options exactly as authored' no longer says the shorthand 'is deprecated and now warns' nor that 'the mixed-array lift has its own pin' named `names ONLY the bare members of a MIXED array` asserting a lifted pair; it now says the shorthand is retired (objectui#4356) — a bare member yields no option — and the mixed-array DROP has its own pin, `keeps ONLY the object members of a MIXED array, and names only the dropped ones` (that name exists verbatim in dashboard-filters.test.ts: 1 hit). Comment only: git diff --numstat 6/6, 0 changed lines outside `//` comments; no assertion moves. Committed with the model-free trailer pair and pushed. W4 (.changeset/7759-dashboard-header-global-filters-spec.md dated note) was NOT attempted and that file is byte-identical to origin/main, per 5866480991 — the maintainer decides how it lands, so the PR's ACCEPT waits on it. origin/main still 733fd5ac6; PR #10914 still open, no merge of main needed. Teaching sweep on e0647486b (same query as round 1, ref prefix stripped first): 18 hits — every one either states the retirement (this PR's changeset, source, docs and test text) or is the unrelated `data`-array shorthand in plugin-grid/plugin-map; DashboardFilterBar.i18nLabel.test.tsx is no longer a hit; nothing false remains in the sweep's population.",
      "tests": "On e0647486b: `pnpm exec vitest run --maxWorkers=2 packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx` exit 0 — 'Test Files 1 passed (1)', 'Tests 8 passed (8)'. eslint --no-inline-config on that file: exit 0, no problems reported. node scripts/check-control-bytes.mjs exit 0 (9181 tracked text files). node scripts/check-new-cross-file-line-citations.mjs exit 0 ('0 new citation(s)'). Teaching sweep `git grep -n -i -E 'lift' -- ':!**/CHANGELOG.md' ':!content/docs/releases/**' ':!pnpm-lock.yaml' | grep -i -E 'shorthand|bare[- ]string|4356'` on e0647486b: 18 hits, i18nLabel test no longer among them. Not re-run this round (unchanged since 98623a24d, where they read exit 0): core dashboard-filters test, the two types tests, DashboardRenderer.filters, types type-check, changeset gates.",
      "gates": [
        {
          "gate": "vitest packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx (e0647486b)",
          "exit": 0,
          "verdict": "1 file, 8 passed"
        },
        {
          "gate": "eslint --no-inline-config (that file)",
          "exit": 0,
          "verdict": "no problems"
        },
        {
          "gate": "node scripts/check-control-bytes.mjs",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "node scripts/check-new-cross-file-line-citations.mjs",
          "exit": 0,
          "verdict": "0 new citations"
        },
        {
          "gate": "W4 dated note on .changeset/7759-dashboard-header-global-filters-spec.md",
          "exit": null,
          "verdict": "HELD for the maintainer (5866480991) — not attempted; file byte-identical to origin/main"
        },
        {
          "gate": "CI on e0647486b",
          "exit": null,
          "verdict": "in_progress — Spec Main Shape Gate expected red on #10916's JoinedReportBlock pin, untouched here"
        }
      ],
      "files_changed": [
        "packages/core/src/utils/dashboard-filters.ts",
        "packages/core/src/utils/__tests__/dashboard-filters.test.ts",
        "content/docs/guide/dashboard-filters.md",
        "packages/types/src/zod/complex.zod.ts",
        ".changeset/retire-options-shorthand-lift-4356.md",
        "packages/plugin-dashboard/README.md",
        "packages/types/src/__tests__/dashboard-global-filters-spec-binding.test.ts",
        "packages/types/src/__tests__/report-chart-query-spec-parity.test.ts",
        "packages/plugin-dashboard/src/__tests__/DashboardRenderer.filters.test.tsx",
        "packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx"
      ],
      "deviations": [
        "W4 remains held for the maintainer per 5866480991: not attempted in any spelling; .changeset/7759-dashboard-header-global-filters-spec.md untouched. The note text and landing recipe stay in 5866443287. The PR's ACCEPT waits on it.",
        "PR body NOT patched (as instructed). Sentences now false, with replacement text, in pr_body_corrections (adds to those in 5866362802)."
      ],
      "pr_body_corrections": [
        {
          "where": "## Measurements — item 5 (Teaching sites), replacing the round-1 replacement text's tail",
          "false": "'… and 1 — DashboardFilterBar.i18nLabel.test.tsx — is still false and outside the claimed surface; reported to the seat, not touched'",
          "replace_with": "'… 18 on e0647486b — every hit either states the retirement (this PR's changeset, source, docs and test text) or is the unrelated `data`-array shorthand in plugin-grid/plugin-map; the DashboardFilterBar.i18nLabel.test.tsx comment that was still false at 98623a24d is corrected in e0647486b (W5).'"
        },
        {
          "where": "## What changed — the rework bullet",
          "false": "(incomplete) W5 not listed",
          "replace_with": "Patch round 2 (e0647486b): packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx — the '(e) leaves plain-string labels…' comment states the retirement and names the current mixed-array DROP pin (`keeps ONLY the object members of a MIXED array, and names only the dropped ones`) instead of the renamed lift pin. Comment only."
        },
        {
          "where": "## Gates run locally — table",
          "false": "(incomplete) no e0647486b rows",
          "replace_with": "Add: vitest DashboardFilterBar.i18nLabel.test.tsx exit 0 (8 passed); eslint on it exit 0; check-control-bytes 0; check-new-cross-file-line-citations 0 — on e0647486b."
        },
        {
          "where": "## Acceptance notes — pending 7759 changeset bullet (from the W4 correction)",
          "false": "'… carries a dated note (or will, once landed)'",
          "replace_with": "'… is made false by this PR; its dated note is HELD for the maintainer (5866480991), text and recipe in 5866443287; the file is untouched on this branch.'"
        }
      ],
      "mcp_calls": "0 — no MCP GitHub tool was called",
      "api_writes": "1 REST write this round via the fleet-write relay as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectui/issues/4356/comments (this follow-up). git push x1 (e0647486b; not REST). No PATCH on the PR body, no label or assignee write. Cumulative on this card: 6 REST writes.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  17. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    Merge-only round on PR #10930 — new head 1daf7edf7c5acfd24b41d7f2591c17feb0db8a80 = origin/main 29c76e5 (PR #10934, objectui#10916) merged into the branch from e064748 with NO conflicts (report-chart-query-spec-parity.test.ts auto-merged: disjoint hunks; both sides verified). W4 stays held; .changeset/7759-dashboard-header-global-filters-spec.md equals origin/main.

    {
      "issue": 4356,
      "status": "done",
      "round": "merge-only round — origin/main 29c76e573 (PR #10934, objectui#10916) merged into the branch from e0647486b; W4 HELD, untouched",
      "branch": "claude/issue-4356-retire-shorthand-lift",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10930",
      "head": "1daf7edf7c5acfd24b41d7f2591c17feb0db8a80",
      "session": "session_012UwY3ahMixEFkfTUxMVkYm",
      "premise_still_valid": true,
      "merge": "`git merge --no-ff origin/main` from e0647486b: merge commit 1daf7edf7c5acfd24b41d7f2591c17feb0db8a80, parents e0647486b (branch) and 29c76e573 (main). NO conflicts — `git diff --name-only --diff-filter=U` was empty; git resolved report-chart-query-spec-parity.test.ts automatically because the two hunks are disjoint (my header docblock vs #10934's JoinedReportBlock pin/ledger further down). One main commit absorbed (29c76e573), bringing .changeset/10916-joined-report-block-pin-two-specs.md, the parity test's pin/ledger rewrite (+271/-53) and scripts/check-spec-symbol-derivation.mjs. Both sides verified in the merged file: my correction present ('at the READ PATH too' = 1 hit), the old lift sentence absent (0), and `git diff origin/main..HEAD` on that file is exactly my 5+/4- header-docblock hunk — i.e. #10934's text is byte-for-byte as main has it. Files differing from origin/main are exactly this PR's 10 files. .changeset/7759-dashboard-header-global-filters-spec.md equals origin/main (W4 held, not touched). No rebase, no force-push; pushed as an ordinary fast-forward of the branch; PR #10930 head is 1daf7edf7, still draft, assignee huangyiirene, mergeable_state blocked (draft + review gates, as expected).",
      "summary": "Merge-only round done. Post-merge verification on 1daf7edf7: the five affected test files pass, the types package type-checks (the parity test is in its test program, so #10934's pin compiles alongside my docblock), and the changeset/control-bytes/line-citation gates read 0. The one main commit was #10934; nothing else moved on main since the branch point.",
      "tests": "On 1daf7edf7 after `pnpm install` (exit 0): `pnpm exec vitest run --maxWorkers=2` over packages/core/src/utils/__tests__/dashboard-filters.test.ts, packages/types/src/__tests__/dashboard-global-filters-spec-binding.test.ts, packages/types/src/__tests__/report-chart-query-spec-parity.test.ts, packages/plugin-dashboard/src/__tests__/DashboardRenderer.filters.test.tsx, packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx — exit 0, 'Test Files 5 passed (5)', 'Tests 114 passed (114)'. `pnpm --filter @object-ui/types type-check` under os-verify-lock (waited 57s for another holder, held 33s): VERDICT command-exit 0. node scripts/check-changeset-presence.mjs exit 0 ('Compared the working tree with 29c76e573 (merge-base with origin/main): 10 file(s) changed, 7 of them published source of a package the release covers … declares 1 changeset(s)'). check-changeset-no-major 0; check-changeset-fixed 0; check-control-bytes 0 (9182 tracked text files); check-new-cross-file-line-citations 0 ('0 new citation(s)').",
      "gates": [
        {
          "gate": "git merge --no-ff origin/main (29c76e573)",
          "exit": 0,
          "verdict": "clean, 0 conflicts; merge commit 1daf7edf7"
        },
        {
          "gate": "vitest x5 affected files (1daf7edf7)",
          "exit": 0,
          "verdict": "5 files, 114 passed"
        },
        {
          "gate": "pnpm --filter @object-ui/types type-check (under lock)",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "node scripts/check-changeset-presence.mjs",
          "exit": 0,
          "verdict": "7 published source files of 3 released packages, 1 changeset declared"
        },
        {
          "gate": "node scripts/check-changeset-no-major.mjs",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "node scripts/check-changeset-fixed.mjs",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "node scripts/check-control-bytes.mjs",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "node scripts/check-new-cross-file-line-citations.mjs",
          "exit": 0,
          "verdict": "0 new citations"
        },
        {
          "gate": "W4 dated note on .changeset/7759-dashboard-header-global-filters-spec.md",
          "exit": null,
          "verdict": "HELD for the maintainer (5866480991) — not attempted; file equals origin/main"
        },
        {
          "gate": "CI on 1daf7edf7",
          "exit": null,
          "verdict": "in_progress — the Spec Main Shape Gate red should now clear, since #10934's pin fix is in this head"
        }
      ],
      "files_changed": [
        "packages/core/src/utils/dashboard-filters.ts",
        "packages/core/src/utils/__tests__/dashboard-filters.test.ts",
        "content/docs/guide/dashboard-filters.md",
        "packages/types/src/zod/complex.zod.ts",
        ".changeset/retire-options-shorthand-lift-4356.md",
        "packages/plugin-dashboard/README.md",
        "packages/types/src/__tests__/dashboard-global-filters-spec-binding.test.ts",
        "packages/types/src/__tests__/report-chart-query-spec-parity.test.ts",
        "packages/plugin-dashboard/src/__tests__/DashboardRenderer.filters.test.tsx",
        "packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx"
      ],
      "deviations": [
        "W4 remains held for the maintainer per 5866480991; .changeset/7759-dashboard-header-global-filters-spec.md untouched (equals origin/main after the merge). Note text and recipe in 5866443287.",
        "PR body NOT patched (as instructed). This round adds no false sentence; one sentence to refresh: the Serial-constraint paragraph's 'origin/main is unchanged since the branch point' / 'PR #10914 … If it lands first, this branch merges main' — replacement: 'origin/main moved once (29c76e573, PR #10934 for objectui#10916) and is merged into this branch at 1daf7edf7 with no conflicts; PR #10914 is still open and disjoint.' The Spec-Main-Shape-Gate sentence in the round-1 corrections ('the red … is #10916's') becomes: '#10916's fix (PR #10934) is merged into this head, so that gate should read green on 1daf7edf7.'"
      ],
      "mcp_calls": "0 — no MCP GitHub tool was called (reads: unauthenticated REST GETs via curl)",
      "api_writes": "1 REST write this round via the fleet-write relay as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectui/issues/4356/comments (this follow-up). git push x1 (1daf7edf7; not REST; ordinary push, no force). No PATCH on the PR body, no label or assignee write. Cumulative on this card: 7 REST writes.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  18. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    Contributor

    Ruling recorded: appending to a pending changeset is authorised. From the domain:spec @ objectui seat, session session_012UwY3ahMixEFkfTUxMVkYm, 2026-09-29.

    On 2026-09-29, in this seat's chat, the maintainer chose the option 「Allow the appended note (Recommended)」 in answer to the pending-changeset write question. The option read:

    You allow developers to append dated correction notes to pending .changeset files (appended only, no existing line or frontmatter edited). The #10930 developer adds the W4 note inside that PR, then it merges main, gets a final review, and merges. The same permission also unblocks objectui#10974 and #10979, which I dispatch straight away.

    Earlier, the auto-mode classifier refused a session's append to a pending changeset (「Modify Shared Resources」, objectui#4356 5866443287). That refusal was held and not re-routed. This ruling settles it for that form of edit.

    Scope:

  19. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    Final round on PR #10930 — head a890654027a69911412efbcb146e326da8476256 (W4 dated note on top of the merge of origin/main 2eaf5be; merge parents 1daf7ed + 2eaf5be, 0 conflicts). W4 landed under ruling 5883360525: 16 insertions, 0 deletions, frontmatter md5 ac1bfd0472c0d0b981d923c32d435f18 before and after. PR body refreshed (read back identical). All local gates 0; details below.

    {
      "issue": 4356,
      "status": "done",
      "round": "final round — W4 landed under the maintainer ruling 5883360525; main merged; PR body refreshed",
      "branch": "claude/issue-4356-retire-shorthand-lift",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10930",
      "head": "a890654027a69911412efbcb146e326da8476256",
      "session": "session_012UwY3ahMixEFkfTUxMVkYm",
      "premise_still_valid": true,
      "commits": "a89065402 (W4 dated note) on a1058f90e (merge of origin/main 2eaf5be27; parents 1daf7edf7 and 2eaf5be27; 70 main commits absorbed, 0 conflicts — files differing from origin/main after the merge were exactly this PR's ten). Plain git push; PR #10930 head is a89065402, still draft, assignee huangyiirene. No rebase, amend or force.",
      "w4": "Append to .changeset/7759-dashboard-header-global-filters-spec.md, authorised by 「Allow the appended note (Recommended)」 (5883360525), the classifier accepted the write this time. Re-measured on the merged tree first: the paragraph reads verbatim as quoted in 5866443287 and is still the ONLY pending sentence this PR makes false (sweep over 2326 pending entries; every other hit is another contract; the nine changeset-claim-re-read entries all still true). Diff: git diff --numstat origin/main = 16 insertions, 0 deletions; frontmatter md5 ac1bfd0472c0d0b981d923c32d435f18 before AND after (byte-identical); whole-file md5 94edd5855709afe137f9184c630f298c → 5c8f0508bed5ed6f5b76254bb74d622d; 0 angle-bracket characters; diff shows only the appended block after the former last line. The note is dated 2026-09-29, in the PR #10891 form, keeps objectui#4356, quotes the two sentences that no longer hold, and points at .changeset/retire-options-shorthand-lift-4356.md (PR objectui#10930) as what ships.",
      "tests": "Final head a89065402, under os-verify-lock: pnpm --filter '@object-ui/core^...' build 0; pnpm --filter @object-ui/core build 0; core type-check 0; types type-check 0; core lint 0 (0 errors, 568 pre-existing warnings); types lint 0 (0 errors, 295 pre-existing warnings); vitest packages/core/ + dashboard-global-filters-spec-binding + report-chart-query-spec-parity + DashboardRenderer.filters + DashboardFilterBar.i18nLabel: 'Test Files 188 passed (188)', 'Tests 3725 passed | 27 skipped (3752)', exit 0; vitest changeset script tests x7: 'Test Files 7 passed (7)', 'Tests 292 passed (292)', exit 0. Cheap gates on the final tree: check-changeset-presence 0 (11 files vs merge-base 2eaf5be27, 7 published source files of 3 released packages, 1 changeset added); check-changeset-no-major 0; check-changeset-fixed 0; check-changeset-claims 0 (report-only); check-changeset-overwrite 0 (report-only); pnpm check:pending-changeset-literals 0; check-control-bytes 0 (9331 files); check-new-cross-file-line-citations 0 (0 new); governed guard on the 7759 changeset path NOT GOVERNED. Teaching sweep on the merged tree: 18 hits, none false (unchanged from e0647486b).",
      "gates": [
        {
          "gate": "git merge --no-ff origin/main (2eaf5be27)",
          "exit": 0,
          "verdict": "clean, 0 conflicts; merge commit a1058f90e"
        },
        {
          "gate": "W4 append (numstat 16/0, frontmatter md5 unchanged, 0 angle brackets)",
          "exit": 0,
          "verdict": "landed in a89065402"
        },
        {
          "gate": "pnpm --filter '@object-ui/core^...' build · pnpm --filter @object-ui/core build",
          "exit": 0,
          "verdict": "0 / 0"
        },
        {
          "gate": "pnpm --filter @object-ui/core type-check · @object-ui/types type-check",
          "exit": 0,
          "verdict": "0 / 0"
        },
        {
          "gate": "pnpm --filter @object-ui/core lint · @object-ui/types lint",
          "exit": 0,
          "verdict": "0 / 0 (0 errors; pre-existing warnings)"
        },
        {
          "gate": "vitest packages/core/ + 4 touched dashboard-filter tests (under lock)",
          "exit": 0,
          "verdict": "188 files, 3725 passed, 27 skipped"
        },
        {
          "gate": "vitest changeset script tests x7",
          "exit": 0,
          "verdict": "292 passed"
        },
        {
          "gate": "node scripts/check-changeset-presence.mjs",
          "exit": 0,
          "verdict": "1 changeset added for 7 published source files"
        },
        {
          "gate": "node scripts/check-changeset-no-major.mjs · check-changeset-fixed.mjs · check-changeset-claims.mjs · check-changeset-overwrite.mjs",
          "exit": 0,
          "verdict": "0 x4 (claims/overwrite report-only)"
        },
        {
          "gate": "pnpm check:pending-changeset-literals",
          "exit": 0,
          "verdict": "no test source names a pending changeset"
        },
        {
          "gate": "node scripts/check-control-bytes.mjs",
          "exit": 0,
          "verdict": "pass"
        },
        {
          "gate": "node scripts/check-new-cross-file-line-citations.mjs",
          "exit": 0,
          "verdict": "0 new citations"
        },
        {
          "gate": "node scripts/check-governed-queue-guard.mjs --test (7759 changeset)",
          "exit": 0,
          "verdict": "NOT GOVERNED"
        },
        {
          "gate": "post-stamped.mjs --body=10930 (PR body refresh)",
          "exit": 0,
          "verdict": "20407 bytes stored, read back IDENTICAL; ack-through 5866696417; relay run 36520460127"
        },
        {
          "gate": "CI on a89065402",
          "exit": null,
          "verdict": "in_progress — seat runs the final at-tier contract review next"
        }
      ],
      "files_changed": [
        "packages/core/src/utils/dashboard-filters.ts",
        "packages/core/src/utils/__tests__/dashboard-filters.test.ts",
        "content/docs/guide/dashboard-filters.md",
        "packages/types/src/zod/complex.zod.ts",
        ".changeset/retire-options-shorthand-lift-4356.md",
        "packages/plugin-dashboard/README.md",
        "packages/types/src/__tests__/dashboard-global-filters-spec-binding.test.ts",
        "packages/types/src/__tests__/report-chart-query-spec-parity.test.ts",
        "packages/plugin-dashboard/src/__tests__/DashboardRenderer.filters.test.tsx",
        "packages/plugin-dashboard/src/__tests__/DashboardFilterBar.i18nLabel.test.tsx",
        ".changeset/7759-dashboard-header-global-filters-spec.md"
      ],
      "deviations": [
        "PR body refreshed via post-stamped.mjs --body=10930 (the seat asked for it this round): first line Fixes #4356, Clause-② line verbatim as the claim, W1–W5 outcomes, the W4 ruling quoted, the earlier rounds' corrections folded in, final-head gate table. Read back byte-identical; the session-URL footer survived the relay write.",
        "check:doc-snippets and check:readme-exports remain NOT MEASURED locally (PRECONDITION NOT MET, unbuilt packages) — CI is the reading; the docs hunk changes no fenced block and the README hunk has no import binding.",
        "No pr_ready, no auto-merge, no label write — the seat's."
      ],
      "mcp_calls": "0 — no MCP GitHub tool was called (reads: unauthenticated REST GETs via curl)",
      "api_writes": "2 REST writes this round via the fleet-write relay as objectstack-fleet[bot]: PATCH /repos/objectstack-ai/objectui/issues/10930 body (post-stamped, relay run 36520460127) and POST /repos/objectstack-ai/objectui/issues/4356/comments (this addendum). git push x1 (a89065402; plain). Cumulative on this card: 9 REST writes.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  20. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    Contributor

    ACCEPT: PR #10930 at a89065402. From the domain:spec @ objectui seat, session session_012UwY3ahMixEFkfTUxMVkYm.

    The review checklist was run against GitHub.

    • Form. Draft to main, first line Fixes #4356, Clause-②: line as the claim has it, assignee huangyiirene. 11 files; the net diff is the retirement plus W1–W5.
    • Contract review. Record 5883630543 is PASS on this head. It supersedes FAIL 5866198610 on bc4ea976e.
      • ① W1–W5 are each resolved in the diff.
      • ① The non-object options[] arm now yields no option, with no lift and no fallback. The dev-mode, once-per-filter console.warn promises no lift.
      • ① The GlobalFilterSchema docblock, the docs guide, the README and the four test docblocks all hold true on this head.
      • ② @object-ui/core: minor is right, with the break stated in the body.
      • ② W4: the dated note on .changeset/7759-dashboard-header-global-filters-spec.md is append-only. The file's previous 52 lines are a byte prefix of the new 68, so +16/−0, and the frontmatter md5 is unchanged at ac1bfd0472c0d0b981d923c32d435f18. The note is true and is the only pending sentence this PR made false. It lands under the maintainer's ruling 「Allow the appended note (Recommended)」 (5883360525).
      • ③ Every dev flag across the rounds is answered.
    • CI. 43 check-runs on the head: 40 success, 3 skipped, 0 failure. That includes Spec Main Shape Gate and Bundle Analysis.
    • Governed surface. None across the 11 paths.

    Noted, not filed. The record carries one observation, marked not verdict-carrying:

    • The object arm still fills a missing label with String(value), while the spec refuses label-less options. This tolerance predates this PR.
    • Read strictly, the W4 note's phrase "not a { value, label } object" is wider than the code for that one case: a label-less object still yields an option.
    • No reach has been measured, so no card is filed.

    Landing: marked ready and auto-merge enabled through the relay in this round.

  21. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    Contributor

    Landed. PR #10930 merged through the merge queue as a51fa0cca. From the domain:spec @ objectui seat, session session_012UwY3ahMixEFkfTUxMVkYm.

    Verified by content on main, in packages/core/src/utils/dashboard-filters.ts:

    • The retired warnShorthandOptions has 0 hits, and "Still lifted here" has 0 hits.
    • warnDroppedShorthandOptions is present, so a non-object globalFilters[].options member is dropped with a dev-mode, once-per-filter warning.

    Also present:

    • .changeset/retire-options-shorthand-lift-4356.md (@object-ui/core minor, break stated);
    • the dated note (2026-09-29) on .changeset/7759-dashboard-header-global-filters-spec.md. That is W4, appended under the maintainer's ruling 「Allow the appended note (Recommended)」.

    The squash commit's co-author trailer is the model-free Co-authored-by: Claude line only. A grep of the message for a model identifier finds 0.

    This card closed completed through Fixes, and pm:dispatched is removed. This lands Phase 2 of objectui#4356, the end state 「7917 ②」 named.

    Noted, not filed: the record's observation that the object arm still fills a missing label with String(value), which predates this PR.

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

Metadata

Metadata

Assignees

Labels

domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions