Skip to content

fix(plugin-view): resolve context tokens in the filters ObjectView hands on (objectui#10506) - #10577

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-10506-named-view-context-tokens
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-10506-named-view-context-tokens

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #10506
Clause-②: no — a renderer stops sending a spec context token literally; no export, schema or accept set moves.

What changed

@object-ui/plugin-view — ObjectView. It now sends the spec's context tokens ({current_user_id}, {current_org_id}) as real values in the authored filters it passes on. It uses the ONE shared resolveFilterPlaceholders from @object-ui/core, with the session scope the host provides through useFilterScope() from @object-ui/react. The console shell mounts FilterScopeProvider from useAuth()'s user and active organization, the same two sources the app-shell ObjectView uses for its own filterScope. No second resolver and no fallback: a token the scope cannot resolve is left as written, and the resolver warns once, which is the resolver's own rule. The filter is therefore never widened.

  • A new hook in ObjectView.tsx, useResolvedFilterSegments, resolves the three authored filter segments once, in one call: the active view's filter (a named listViews entry, else the host views entry), table.filter, and table.defaultFilters.
  • An authored filter leaves this component in three places, and all three now read the resolved segments: the non-grid find(), the object-grid schema handed to ObjectGrid, and the list-view schema handed to a host's renderListView.
  • The resolved value is held in state. It is keyed on the raw segments (compared by structure with the package's isStructurallyEqual) and on the scope's three members (currentUserId, currentOrgId, onUnresolved), each compared on its own, never the scope object's identity (AGENTS.md [WIP] Enhance every detail of the designer #10). The reasons: ObjectGrid and ListView refetch whenever the filter object's identity changes, and a {now} macro gives a new value on every call.

@object-ui/core — the resolver's two object walks (the Date repair).

  • resolveContextTokens in filter-tokens.ts and resolveDateMacros in date-macros.ts used to rebuild every object from its own keys. A Date has no own keys, so a Date comparand came back from resolveFilterPlaceholders as {}, even though the spec admits Date (ACCEPTED_FILTER_COMPARAND_TYPES).
  • Both walks now descend only into a plain object, whose prototype is Object.prototype or null. Any other object is a leaf, returned as the same instance: a Date, and equally a class instance. A class instance with own keys carrying a token is therefore no longer copied and resolved. The contract review of this change found no in-repo caller that passes one.
  • This is two mechanical hunks. There is no new export and no other change to core.
  • The defect predates this card. The resolver's existing call sites (app-shell ObjectView, ObjectDataPage, useOpenRecordList; plugin-charts ObjectChart; the plugin-dashboard widgets) all get the repair. The plugin-view change above would otherwise have been a tenth door onto it.

Changeset: .changeset/10506-named-view-context-tokens.md, with '@object-ui/plugin-view': patch and '@object-ui/core': patch.

Prose this change would make false, or that AGENTS.md #2 requires:

  • content/docs/api/schema-reference.md: its sentence saying resolution on the object-view node was still tracked in this card is corrected.
  • packages/plugin-view/README.md and content/docs/plugins/plugin-view.mdx: each gains one paragraph beside the filter-tier precedence.

Commits:

  • 2218bbbbb fix(plugin-view): resolve the tokens.
  • fa4f23b3f docs and changeset.
  • ec06abc38 test typing.
  • bf6a8fbe9 hold in state, not a ref.
  • f0173ec3f fix(core): non-plain objects are leaves.
  • 57eda69be docs: changeset and docs prose made true to the contract review; prose only.
  • 53aa3660c docs: the ObjectStack auth (401 at the REST auth gate) and timezone (tenant localization.timezone) facts corrected; prose only.

Measured

The code was measured at f0173ec3f, base 9cbe4dbca. 57eda69be and 53aa3660c change only the changeset and the three docs files: git diff --stat f0173ec3f..53aa3660c lists exactly those four.

  • plugin-view pin packages/plugin-view/src/__tests__/ObjectView.namedViewContextTokens-10506.test.tsx, 14 cells.
    • Base code: 12 of the first 13 cells fail, each reading the literal {current_user_id} where usr_42 was expected.
    • The Date cell fails at bf6a8fbe9 (expected {} to be an instance of Date).
    • Head: all 14 pass.
  • core pin packages/core/src/utils/__tests__/filter-tokens.dateLeaf-10506.test.ts, 5 cells.
    • At bf6a8fbe9, the 3 Date cells fail (expected {} to be 2026-01-01T00:00:00.000Z). The 2 controls pass: a plain object and a null-prototype object carrying a token are still resolved.
    • Head: 5 of 5 pass.
  • Per-hunk ablation (ablation-replace.mjs --delete of the guard line, each file on its own): 4 failed | 15 passed for each, with the controls green. Both files were restored: blob equals HEAD and git diff HEAD is empty.
  • Earlier checks on the plugin-view hunk:
    • With the pre-change ObjectView.tsx put back: 12 failed | 1 passed.
    • With the hold changed to resolve on every render: 4 failed | 9 passed.
  • Suites at f0173ec3f:
    • packages/core/: 172 passed, 3491 passed.
    • packages/plugin-view/: 49 passed, 477 passed.
    • The resolver's call-site suites: 73 passed, 677 passed.
    • app-shell views/ObjectView.* and Studio: 85 passed, 622 passed.
    • core and plugin-view type-check: exit 0.
  • Gates at 53aa3660c, each exit 0: check-changeset-presence, check-changeset-overwrite, check-changeset-fixed, check-changeset-no-major, check:new-line-citations (0 new), check:control-bytes, docs:check-links and check:doc-fences.
    • check-changeset-claims is report-only. The 18 listed paragraphs are unchanged in meaning by this prose, and this changeset negates neither package it declares.
  • CI at f0173ec3f: 43 check runs, 40 success, 3 skipped, 0 failure.

Acceptance notes

  • Date macros ({today}, {current_quarter_start}, …) are now resolved on the client for this node, in the browser's local time.
    • That is the same call the app-shell host, the dashboards and ObjectChart already make; a directly authored object-grid or list-view node still does not make it.
    • Before, on this node's paths, the macros reached the server as literals. The ObjectStack server resolved them in the tenant's configured localization.timezone, UTC by default: REST reads it through resolveLocalizationContext into the execution context, and objectql's resolveWhereTokens hands that context to resolveFilterTokens. So a browser whose local day differs from the server's day can now get a different day.
    • {now} and {today} are resolved once and held until the user, the organization or a segment changes. A refreshKey re-query reuses the held value. This governs the non-grid find(), the grid, and any renderListView host that queries with the filter it is handed.
    • The console's app-shell host is not one of those: its renderListView resolves the active view's own filter itself (viewDef.filter ?? listSchema.filter, and the schema it gives this component carries no filter segment). So on the console's delegated grid path the date macros are still resolved by the app-shell on each of its renders, as before. The active view's filter still reaches this component there, through the views prop, so this component's pass still runs on it.
  • Context tokens against the backend: the ObjectStack server also expands {current_user_id} for a signed-in request, and refuses the request otherwise: the REST face answers 401 at its auth gate before the filter is read, and where a guest context reaches the engine the resolver answers 400. So against that backend, the literal token this node used to send was expanded, or the request was refused, before the database saw it. This is a source reading, not an end-to-end measurement. The gap this PR closes is on the client, and it still matters for any backend that has no such resolver, where a literal token matches no record.
  • With no user in scope on the console, only this component's pass warns. The app-shell's own pass substitutes 'current-user' (FALLBACK_USER) silently, as before, so the console's query is unchanged from base and the only difference is one new warning. A host that resolves the handed value again changes nothing: a resolved id or date no longer matches the whole-token pattern (pinned).
  • All three segments are resolved even when one wins, so a token in a losing segment can warn for a filter that is never sent.
  • Seen from source only, not measured: a directly authored object-grid or list-view node still sends its own schema.filter tokens unresolved (plugin-grid and plugin-list have zero reads of the resolver). The contract review calls this a real defect of the same class. The seat filed it as objectui#10607; it is not fixed in this PR.

Generated by Claude Code

…nds on

ObjectView had no read of @object-ui/core's resolveFilterPlaceholders, so a
named list view filtering `owner` on `{current_user_id}` reached the query
with the literal token on the registered object-view node, while the
app-shell host resolved the same view.

The three authored filter segments (the active view's filter, table.filter,
table.defaultFilters) are now resolved once through the shared resolver,
against the session scope the host provides through useFilterScope, and the
non-grid find(), the object-grid schema and the delegated list-view schema
all read the resolved segments. The resolved value is held against its raw
inputs (compared by structure) and the scope's members, so ObjectGrid and
ListView, which key their fetch on the filter's identity, do not refetch per
render, and a `{now}` macro cannot mint a new value per render.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
The schema reference said resolving `{current_user_id}` on the object-view
node path was still open; it is resolved there now. The plugin-view README
and plugin page gain one paragraph beside the filter-tier precedence saying
every tier's filter goes through resolveFilterPlaceholders, where the scope
comes from, and what an unresolvable token does. Adds the plugin-view patch
changeset.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
tsc -p tsconfig.test.json refused the implicit any (TS7006).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…t a ref

Reading and writing a ref during render drew eleven react-hooks/refs
warnings, which the lint config keeps only for code that predates the
rule. React's own pattern for a value derived from previous renders is a
state pair set during render; ObjectView.tsx lints at its base warning count
again. Also types the view segment through NamedListView rather than a bare
any.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions github-actions Bot added documentation Improvements or additions to documentation plugin tests labels Sep 25, 2026
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 18 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/3917-retire-action-condition-branch.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    • ActionCondition is removed from @object-ui/types (and from the barrel export). - ActionSchema.condition is retyped to the predicate the runtime actually honours: boolean | string | { dialect?: string; source: string } — the same three arms ActionRunner's own ActionDef.condition carries, and the same vocabulary visible and disabled use. - ActionConditionSchema is removed from @object-ui/types/zod (and from the zod barrel); the condition key now validates against that predicate union. - The two teaching sites (content/docs/core/enhanced-actions.mdx Conditional Execution, content/docs/api/schema-reference.md ActionSchema table) are rewritten to the live vocabulary: condition is a gate; a branch is expressed as separate actions with mutually exclusive conditions.

.changeset/6726-find-envelope-records-arms.md

  • names plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    | module | what it does | | --- | --- | | components/src/hooks/related-count-store.ts | related-list tab badge count | | components/src/renderers/basic/data-list.tsx | element:repeater rows | | components/src/renderers/basic/elements.tsx | element:number client-side aggregate | | components/src/renderers/basic/record-picker.tsx | element:record_picker options | | plugin-detail/src/renderers/record-activity.tsx | record:activity self-fetch | | plugin-detail/src/renderers/record-history.tsx | record:history self-fetch | | plugin-view/src/ObjectView.tsx | non-grid (kanban / calendar / gallery / timeline) fetch |

.changeset/6896-retire-chart-inline-data.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    ⚠️ One correction to the record the ruling rests on. The ruling states zero authorship of a populated series[].data outside tests across packages/ / apps/ / examples/. The re-measurement finds one such site inside those roots — packages/types/examples/data-display-examples.json (2 series) — plus four outside them, in documentation: content/docs/api/schema-reference.md (3) and content/docs/core/report-schema.mdx (1).

.changeset/6939-kanban-column-cards.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Breaking, deliberately. KanbanColumn declared its card list as items in complex.ts and in the zod mirror complex.zod.ts. Every board reads cards. Measured on origin/main 78a3cc238: KanbanImpl.tsx reads .cards on 12 lines, KanbanEnhanced.tsx on 8, and bucketCardsIntoColumns twice more as col.cards || []; .items had zero read sites in either board (a same-shaped .title control on the same two files returns 8 and 3, so those zeros are readings and not a mis-shaped probe). Both catalog entries, the plugin docs and content/docs/api/schema-reference.md all author cards.

.changeset/6951-tree-view-data-retired.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What was measured, on this branch's base. TreeViewSchema declared two spellings for its one inline-nodes slot — nodes (read second) and data (read third: boundData || schema.nodes || schema.data || [] at renderers/data-display/tree-view.tsx:105), both declared by objectui#6150. data had been REQUIRED until objectui#6939 / PR fix(types): tree-view mirror stops requiring the limb it reads third (group 2 of objectui#6939) #7533 made it optional, so this retirement starts from a declared-and-optional member on both faces. The in-repo corpus at the retirement: seven tree-view nodes under examples/schema-catalog and packages/types/examples plus one content/docs fence — six on nodes, two on data (packages/types/examples/data-display-examples.json and content/docs/api/schema-reference.md), both rewritten; no package source authored either spelling.

.changeset/7070-no-invented-gantt-date-fields.md

  • names plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    • app-shell/src/views/ObjectView.tsx — the console object page. The inline branch becomes ganttViewOptions, the sibling of calendarViewOptions and timelineViewOptions: the declared block spread whole, title floored at 'name', no date field invented. - plugin-list/src/ListView.tsx — the render branch AND the capability gate. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

.changeset/7499-gantt-non-axis-floors-omitted.md

  • names plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    • plugin-list/src/ListView.tsx — the object-gantt render branch. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

.changeset/7664-kanban-arm-plugin-dialect.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Migration. Author boards in the plugin dialect — objectName + groupBy for an object-bound board, or columns[].cards[] with badges for a static one. Replace DeclarativeKanbanSchema imports with KanbanSchema (from @object-ui/types, or the Zod KanbanSchema from @object-ui/types/zod; @object-ui/plugin-kanban re-exports the same KanbanSchema type). Delete draggable (drag-and-drop is always on) and column color (style a lane through className). content/docs/api/schema-reference.md's kanban section now documents this dialect.

.changeset/7997-detail-view-related-retired.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Documentation. packages/plugin-detail/README.md and content/docs/api/schema-reference.md stop teaching the retired array and gain a migration block each.

.changeset/8221-retire-legacy-string-sort.md

  • names content/docs/plugins/plugin-view.mdx → content/docs/plugins/plugin-view.mdx — edited by this change

    Docs teach the array only: content/docs/plugins/plugin-map.mdx, content/docs/plugins/plugin-view.mdx and packages/plugin-view/README.md.

  • names packages/plugin-view/README.md → packages/plugin-view/README.md — edited by this change

    Docs teach the array only: content/docs/plugins/plugin-map.mdx, content/docs/plugins/plugin-view.mdx and packages/plugin-view/README.md.

.changeset/8268-testid-emitted-as-data-testid.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    The other direction — retiring the promise — was considered and declined. It is what objectui#7088 did for BaseSchema.hidden, but that key had a working behaviour to describe and zero named consumers, and the ruling's decline turned on exactly that. This promise already has carriers outside the type declaration: content/docs/api/schema-reference.md states it as a table row and authors testId in that page's own base-schema example, @object-ui/cli's OBJECTUI_STRUCTURAL_KEYS identifies a file as an ObjectUI schema node by this key, ObjectGridSlotKey / ObjectFormSlotKey pin it, SchemaBuilder.testId() writes it, and ADR-0054 C4 — shipped — reads "the renderer emits data-testid … derived from metadata".

.changeset/8653-listview-title-retired-rowactiondefs-pinned.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    title — retired. ListView resolved its export filename through schema.label || (schema as any).title. @objectstack/spec/ui's ListViewSchema refuses title by name (unrecognized_keys: ['title']) while ObjectGridPropsSchema accepts it; packages/types mirrors the platform contract rather than ruling over it, so declaring title on ListViewSchema would have made this repo accept what the platform save gate rejects. That asymmetry is also why objectui#6639 could take the declare branch for ObjectGridSchema.title one package over and this site could not. A parse-based census of apps/ examples/ content/ and packages/ found zero list-view nodes authoring title, so the retirement costs no author a filename. Over that same corpus the instrument reports three object-grid nodes carrying the key: two authored ones, both in content/docs/api/schema-reference.md, plus one that is not authored at all — packages/plugin-view/src/ObjectView.tsx composes title: schema.table?.title onto a grid node it builds, so it is a producer writing the key rather than an author declaring it. ObjectGrid's own title reads are untouched — they remain declared, ruled and read.

  • names packages/plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    title — retired. ListView resolved its export filename through schema.label || (schema as any).title. @objectstack/spec/ui's ListViewSchema refuses title by name (unrecognized_keys: ['title']) while ObjectGridPropsSchema accepts it; packages/types mirrors the platform contract rather than ruling over it, so declaring title on ListViewSchema would have made this repo accept what the platform save gate rejects. That asymmetry is also why objectui#6639 could take the declare branch for ObjectGridSchema.title one package over and this site could not. A parse-based census of apps/ examples/ content/ and packages/ found zero list-view nodes authoring title, so the retirement costs no author a filename. Over that same corpus the instrument reports three object-grid nodes carrying the key: two authored ones, both in content/docs/api/schema-reference.md, plus one that is not authored at all — packages/plugin-view/src/ObjectView.tsx composes title: schema.table?.title onto a grid node it builds, so it is a producer writing the key rather than an author declaring it. ObjectGrid's own title reads are untouched — they remain declared, ruled and read.

.changeset/8801-object-kanban-allow-collapse-retired.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    • the declarations retired here — packages/types/src/objectql.ts and its mirror packages/types/src/zod/objectql.zod.ts; - the pins that assert the retirement — object-kanban-allow-collapse-retired-8801.test.ts and bare-kanban-node-key-retired-8802.test.ts; - a comment in packages/types/src/zod/complex.zod.ts, recording that the deleted retiredZeroReadKanbanKey helper once carried this spelling on the SIBLING arm; - one row of content/docs/api/schema-reference.md; - the .changeset/ release notes that discuss it — this one, the two historical entries covering the sibling arm's own spelling, and objectui#9629's note recording the correction to this paragraph.

.changeset/9187-record-highlights-layout-two-values.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    ⚠️ The census behind this narrowing covers this repository only, and it found no in-repo authoring to migrate: every in-tree layout: 'grid' belongs to a different component (detail-view in content/docs/api/schema-reference.md and phase2-schemas.test.ts, ai-recommendations in packages/plugin-ai/README.md), and the one in-repo consumer of this interface that writes a layout (p1-spec-alignment.test.ts) writes 'horizontal'. So no document in this repository stops type-checking. A TypeScript consumer outside this repo that wrote grid is not observable from here and gets a compile error (TS2322) naming the key — which is why the FROM/TO is spelled out above.

.changeset/calendar-view-schema-converge.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Runtime renderer behaviour is unchanged. @object-ui/plugin-calendar's README and content/docs/api/schema-reference.md are repaired to the converged surface in the same change, so no copy of the old contradiction survives.

.changeset/issue-5373-retire-crud-schema.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    crud had four declaration faces and no registered renderer, for the whole life of the key: the TS interface (packages/types/src/crud.ts), the zod mirror (packages/types/src/zod/crud.zod.ts), a dedicated branch in validateSchema that affirmatively PASSED it, and CRUDBuilder in @object-ui/core. A node spelling it painted the OBJUI-001 "Unknown component type" panel, and content/docs/api/schema-reference.md published it as reference material — so a reader (or an AI author) who copied the page got a red panel.

  • names api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Authoring crud is now REFUSED BY NAME rather than passed or silently ignored. validateSchema returns an error with code: 'RETIRED_TYPE' on schema.type — at any depth, since it is what validateChildren recurses with — so assertValidSchema throws and isValidSchema answers false. The message names the migration: object-grid for the record table with its toolbar, filters, pagination and row/batch actions, object-form for the create/edit form, and detail for the record view. api/schema-reference.md is rewritten around those shapes.

.changeset/object-view-unmirrored-keys-7779.md

  • names packages/plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    What was measured. Every reading was taken on the object-view node renderer (packages/plugin-view/src/ObjectView.tsx, registered by plugin-view/src/index.tsx) with schema.objectName / schema.layout as the positive controls of the same schema.KEY query, so each zero is a reading; the repo-wide census of viewTabBar finds the key in no source file outside @object-ui/types (two doc tables listed it as authorable and are corrected here). The spec side was read through the installed pin (@objectstack/spec@17.2.0, ui entry, 117 exported object schemas walked; control keys objectName / columns / navigation / listViews hit): the three spec-modelled keys are optional slots on ListViewSchema and ObjectListViewSchema; the six local keys have no spec slot anywhere.

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    listViews stays unmirrored, on the ruling's own fallback clause. The declaration's value is the local NamedListView — 64 declared top-level members (⚠️ re-taken at objectui#8980, which added the seventeen the protocol declares on this surface to the 47 this entry first measured), of which the renderer reads 21 off a named view. data is one of the 21 now: it used to reach the renderer through an as any cast on the named-view config in packages/plugin-view/src/ObjectView.tsx and be declared nowhere, and the objectui#8980 ruling declared it by name — objectui#7928's open half, answered. The spec's ViewSchema.listViews is a record of the STRICT ObjectListViewSchema, which requires columns and refuses options, ObjectQL tuple filters and default — that is, it refused the named views this package's own README and content/docs/api/schema-reference.md taught when this entry was written ({ label: 'All Users' } fails at columns; filter: [["owner", "=", "..."]] fails at filter.0), and objectui#8255 has since rewritten them in the spec shape. Mirroring the spec value would have lost documented behaviour; mirroring the local value would enforce 43 unread members (64 declared, minus the 21 that are both declared and read) into the contract — the very thing ruling B refused for the six local keys. The key therefore stays in the parity ledger with that measurement, pinned, until the maintainer decides its value type. It is not papered over with z.any().

.changeset/table-renderer-declared-column-contract-5350.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What makes this site different from its three siblings is that the alias was not merely tolerated, it was published. content/docs/api/schema-reference.md §TableSchema shipped a copyable { "name": "id", "label": "#" } example and a property row reading "Column definitions with name, label, …", while packages/types declared the opposite pair. Docs and type disagreed about one type they both call TableColumn, each internally consistent. Retiring the alias without correcting the page would have turned a documented, working example into a silently broken one, so both halves land together: the page now authors accessorKey/header. The same row also advertised a render property that TableColumn has never declared — the renderer's hook is cell — and that claim is dropped rather than re-spelled.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Compared the checked-out tree with ab7751321 (merge-base with origin/main): 8 file(s) changed outside .changeset/, read against 1467 pending declaration(s) that publish a body (2049 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3047.3 KB 3104.5 KB
Main entry chunk (gzip) 147.8 KB 350 KB
Entry file index-B80aTiuE.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 545.30KB 130.42KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 258.75KB 65.60KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.40KB 14.61KB
plugin-charts (index.js) 74.94KB 20.89KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.50KB 35.37KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.59KB 61.50KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 147.92KB 37.83KB
plugin-gantt (index.js) 169.62KB 41.91KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 48.26KB 15.04KB
plugin-list (index.js) 114.43KB 28.26KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.84KB 21.96KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

resolveContextTokens and resolveDateMacros rebuilt every object from its own
keys, so a Date comparand, which the spec admits, came back from
resolveFilterPlaceholders as an empty object. Routing plugin-view's filters
through the shared resolver made that reachable on the object-view node.
Only a plain object (prototype Object.prototype or null) is walked now;
anything else is returned as the same instance.

Pins: a core test (a Date survives as the same instance through
resolveFilterPlaceholders and through each walk alone; plain and
null-prototype objects carrying a token are still resolved) and one more
plugin-view cell (a Date in a named-view filter reaches find() as a Date).
The changeset gains the core patch line.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3047.3 KB 3104.5 KB
Main entry chunk (gzip) 147.8 KB 350 KB
Entry file index-vN0Ex7VR.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 545.30KB 130.42KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 258.75KB 65.60KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.40KB 14.61KB
plugin-charts (index.js) 74.94KB 20.89KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.50KB 35.37KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.59KB 61.50KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 147.92KB 37.83KB
plugin-gantt (index.js) 169.62KB 41.91KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 48.26KB 15.04KB
plugin-list (index.js) 114.43KB 28.26KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.84KB 21.96KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: f0173ec3fa97ba95253362ebe1d48d01b18a47c6

① Derived judgments

  • Head. git rev-parse refs/review/pr-10577 = f0173ec3f…; base 9cbe4dbca; 5 commits; 9 files (+563/−15). The branch had not moved.
  • One resolver: yes. The diff adds one import of resolveFilterPlaceholders and one call, inside resolveFilterSegments; no regex, no token or macro spelling in code. Scope is useFilterScope() from @object-ui/react (hooks/useFilterScope.ts, exported via hooks/index.ts and the root index). The console mounts FilterScopeProvider once, in app-shell/src/console/AppContent.tsx, with currentUserId={user?.id ?? null} and currentOrgId={activeOrganization?.id ?? null} from the same useAuth() the app-shell ObjectView reads. Field by field against the app-shell ObjectView's filterScope ({ currentUserId: currentUser.id, currentOrgId: activeOrganization?.id ?? null }): org identical; user identical while signed in; signed out the provider gives null while app-shell substitutes FALLBACK_USER.id = 'current-user'; neither passes onUnresolved. Consequence, signed out on the console grid path: plugin-view's pass leaves the token and warns, then the app-shell's second pass substitutes 'current-user' WITHOUT warning (its scope is never empty). So the PR body's "With no user in scope, both passes warn" is false for the user token; the query result is unchanged from base ('current-user'), the delta is one new warning. No provider mounted: context default is {}, token left literal, resolver warns (pinned, cell "keeps an unresolvable token literal with no scope mounted"). The no-app branch of AppContent (create-app / marketplace / metadata routes) returns before the provider; it routes no object view.
  • Three doors: all fed; no fourth found. (1) non-grid find(): $filter: finalFilter from mergeFilterNodes(authoredFilters.view || .table || .tableDefaults); (2) grid schema: defaultFilters: viewFilter || authoredFilters.tableDefaults, filter: viewFilter ? undefined : authoredFilters.table; (3) mergedFilters → list-view filter: mergedFilters for renderListView. Hunt at head: every .filter property read is at the hook input, the activeViewQueryInputs builder, and the three doors (6 lines); .find(/count/export/preview: 0 filter-bearing hits; viewSwitcherSchema carries no filter; the only spread of a raw source is ...currentNamedViewConfig?.userActions. Precedence unchanged: the two delegated doors used to read activeView?.filter and now read activeViewQueryInputs?.filter, which is useStableIdentity({ id, filter: activeView.filter, sort }), the same value.
  • Identity / refetch. held is useState, keyed on segments via isStructurallyEqual (the existing export of plugin-view/src/stableIdentity.ts, present on base; the file is not in the diff) and on currentUserId, currentOrgId, onUnresolved one by one, never the context object (AGENTS.md [WIP] Enhance every detail of the designer #10 satisfied; FilterScopeProvider accepts only the two ids, so onUnresolved cannot churn). User switch re-resolves (pinned), a filter edit re-resolves (segments differ), an equal inline rebuild does not (pinned: 1 find across 3 re-renders). Render-phase setHeld cannot loop: the retry runs with the same props, so every segment member is the same reference and Object.is short-circuits before isStructurallyEqual's depth cap (12) or its unmodelled-type false. Date macros: {now}/{today} resolve once at mount and are frozen until user, org or a segment changes; a refreshKey bump (a dep of the non-grid effect) re-queries with the frozen value; {today} rolls over only on such a change. On the console grid path the app-shell's second pass, which used to re-resolve a date macro afresh per render (its own comment: "a date macro that resolves differently — still hands over the new value"), now sees a resolved ISO string and is a no-op, so plugin-view's hold governs date-macro freshness there too. Same policy as ObjectMetricWidget/DatasetWidget (useMemo on [filter, filterScope]); stricter than the app-shell's. Not stated in body or changeset beyond "does not produce a new value on every render". Side effect: all three segments are resolved even when one wins, so a token in a losing segment can warn for a filter never sent.
  • Behaviour moved beyond the card. objectstack origin/main: ObjectQL.resolveWhereTokens (engine.ts) calls @objectstack/core resolveFilterTokens with filterTokenContextFrom(execCtx) = { now, timezone: execCtx?.timezone, userId, orgId: tenantId }; date macros go through proxyDay(now, ctx.timezone) → calendarPartsInTzOrUtc, documented "Defaults to UTC"; git grep timezone origin/main -- packages/rest/src = 0 hits, so a REST read resolves in UTC. The client (date-macros.ts: setHours(0,0,0,0), getFullYear()) resolves in browser local time. So on this node's non-grid find() path, {today}-family macros now bound the browser's calendar day where the server bounded the UTC day: results differ for a browser whose local day differs from UTC at query time. The server also throws UnresolvedFilterTokenError (400) for {current_user_id} with no userId, and UnknownFilterTokenError (400) for an unknown token. Client resolution is the choice app-shell (substituteFilterTokens), ObjectChart (2 calls) and the 4 dashboard widgets already make, and the console grid path already resolved client-side via the app-shell second pass, so this is consistency; the change is confined to the non-grid path and to non-console hosts. PR body Acceptance note: accurate as far as it goes (local time; same call as the others; literals reached the server before) but incomplete: it does not say the server resolved those literals in UTC, nor that a non-UTC browser can now get a different day.
  • Core repair. The diff is exactly one guard (const proto = Object.getPrototypeOf(value); if (proto !== Object.prototype && proto !== null) return value;) plus a comment in each walk, 6 added lines per file; nothing else in core. Measured on the published @object-ui/core@17.6.0 (its dist has no getPrototypeOf) against the same dist with the guard patched in: a Date in a triple and in { $gte } → {} on base, same instance with the guard; a null-prototype bag is walked in both; RegExp {} → kept; a class instance with own keys carrying {current_user_id} was resolved into a plain copy on base and is now returned untouched with the token literal. Callers on main: 9 resolveFilterPlaceholders call sites in 8 files (ObjectDataPage 1: URL triples; app-shell ObjectView 1 via substituteFilterTokens: view/URL arrays; useOpenRecordList 1: drill Record with { $gte, $lt } plain objects; ObjectChart 2: schema.filter; DatasetWidget, ObjectDataTable, ObjectMetricWidget, ObjectPivotTable 1 each: widget/schema filters), plus 4 direct walk call sites the seat did not count (ObjectChart resolveContextTokens(shiftFilterByCompareTo(...)), compare-to.ts resolveDateMacros ×2, dashboard-filters.ts resolveDateMacros(value) on a string, unaffected). All pass JSON-derived or URL-parsed plain data; none passes a class instance or a cross-realm object (the only iframe seam, postMessage, clones into the receiving realm). Headers "Walks arrays and plain objects recursively" (filter-tokens) and "Walks any plain-object / array structure recursively. Non-string values … passed through untouched" (date-macros) are now true. Pins: core pin 5 cells, the 3 Date cells red on base for the measured reason ({}), 2 real controls (plain object, null-prototype bag) green on both sides; plugin-view pin 14 cells (7+3+4 it). The per-hunk ablation 4 failed | 15 passed per file is consistent with the cell layout (cell 3 exercises each walk separately) but was not re-run here: no local toolchain (no vitest/tsx/tsc in node_modules/.bin, no core dist); CI at head is the instrument.
  • Seat decision. Sound: the defect reproduces on published 17.6.0, so it predates the card; "9 production callers" is right (9 call sites, 8 files); the 3 object-walking direct callers widen option A's blast radius slightly beyond the names given, all inside ObjectChart/compare-to and covered by the ObjectChart suite in the call-site run.
  • Prose. schema-reference.md, plugin-view.mdx, README.md: "resolves through the same resolveFilterPlaceholders… nearest FilterScopeProvider… console shell mounts one… on all three paths… pins each path" — true at head. The shared sentence "With no provider mounted the token is left as written, so the view matches no record rather than every record" is backend-dependent: against ObjectStack the literal token is resolved server-side (signed in) or refused with 400 (signed out); it matches no record only on a backend with no resolver. It copies the existing client framing (useFilterScope.ts header, the resolver's own warning text), so it is not a new class of claim, but it is stated as fact.

② Semver level

patch + patch, Clause-②: no — upheld. No export, type, schema or documented accept set moves: resolveFilterPlaceholders/resolveContextTokens/resolveDateMacros keep their signatures; core/src/index.ts untouched. The documented input set was always "arrays and plain objects"; what changes is behaviour on undocumented inputs: Date/Map/Set/RegExp go from {} to the same instance (repair), and a class instance with own keys goes from a resolved plain copy to an untouched instance with its tokens literal — the one consumer-observable narrowing beyond the Date repair, measured above; zero in-repo callers hit it. Changeset sentence by sentence: all true except (a) "as they already did on every other surface" — plugin-grid/plugin-list do not (the dev's own note); (b) "still matches no record rather than every record" — backend-dependent as above; (c) "the scope's two ids" — three members are compared (harmless). The class-instance narrowing is covered only implicitly by "returns a non-plain object as the same instance". Pending changesets: 7265-core-context-token-mirrors.md (describes its own swap, measured on string tokens) and 9129-filter-token-suggestion-proto-guard.md (the NEAR_MISS_SUGGESTIONS block is untouched) remain true; the 7 other pending changesets naming plugin-view/src/ObjectView.tsx make no filter-resolution claim.

③ Boundary flags

  • CI on head: 43 check runs, 40 success, 3 skipped (coverage ×2, dependabot), 0 failure; Lint, README Export Check, Doc Snippet Type Check, Changeset Claim Re-read, Type Check, Test (dist pins) all success, covering the dev's two NOT MEASURED gates.
  • merge-tree: git merge-tree --write-tree refs/review/main refs/review/pr-10577 → 00720612a…, exit 0, no conflicts; main is 17 commits past base (50e41f738); 0 of them touch the 9 files. PR mergeable_state: clean.
  • Serial overlap: 15 open PRs; file lists read for all 14 others. None touches packages/plugin-view/src/ObjectView.tsx, filter-tokens.ts, date-macros.ts or the tests/changeset/mdx/README. One overlap: draft PR 8941 (lucide bump, updated 2026-09-20) edits content/docs/api/schema-reference.md in the detail-view "icon": "trash" example, a different hunk from this PR's my-deals paragraph: no textual conflict. No open PR from objectui#7924.
  • Dev's note on direct object-grid/list-view nodes: accurate — packages/plugin-grid/src and packages/plugin-list/src have 0 production reads of resolveFilterPlaceholders/resolveContextTokens/resolveDateMacros. A real defect of the same class as this card (a spec token honoured on object-view but not on a directly authored object-grid/list-view node; against ObjectStack the server resolves it, elsewhere it is literal); worth a card.
  • Should stop landing: nothing in code. The seat authors the landing body, so correct before squash: (1) "With no user in scope, both passes warn" → only plugin-view's pass warns; the app-shell pass substitutes 'current-user' silently; (2) the Acceptance note on macros should add that the server resolved them in UTC (REST threads no timezone), so a non-UTC browser can now bound a different day, and that {now}/{today} are held until user, org or filter change, including on the console's delegated path. Optional: soften "matches no record" in the three docs hunks to "is not widened", and add "a class instance is a leaf too" to the changeset.

Implemented-by: claude/issue-10506-named-view-context-tokens
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS


Generated by Claude Code

…e review

Prose only, from the contract review of the head before this commit:
- the date macros resolve "as they already did" only on the surfaces that
  call the resolver (app-shell host, ObjectChart, dashboard widgets), not on
  every surface; the server-side UTC difference is stated;
- an unresolved token never widens the filter, and what the literal selects
  depends on the backend (ObjectStack resolves it when signed in, refuses it
  with a 400 otherwise; a backend with no resolver matches no record);
- the hold compares three scope members, not two ids;
- a non-plain object, a class instance included, is a leaf of both walks;
- the console's app-shell renderListView resolves the view's own filter
  itself, so its date macros and signed-out fallback are unchanged, and the
  one difference there is a warning from this component's pass.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3047.5 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-C4AtQRI1.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 546.37KB 130.65KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.12KB 65.73KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.40KB 14.61KB
plugin-charts (index.js) 74.94KB 20.89KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.50KB 35.37KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.59KB 61.50KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 147.92KB 37.83KB
plugin-gantt (index.js) 169.62KB 41.91KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 48.26KB 15.04KB
plugin-list (index.js) 114.43KB 28.26KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.84KB 21.96KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 57eda69be50d35fac4cda4ea05060f358bab846f

① Derived judgments

  • Head. refs/review/pr-10577 = 57eda69be; one commit past f0173ec3f (docs(plugin-view): make the 10506 changeset and docs prose true to the review). The six commits the body lists are all on the branch, in that order.
  • Prose only: yes. git diff --stat f0173ec3f..57eda69be lists exactly .changeset/10506-named-view-context-tokens.md (+38/−14 counted lines), content/docs/api/schema-reference.md (1 line), content/docs/plugins/plugin-view.mdx (+3/−1), packages/plugin-view/README.md (+3/−1): 34 insertions, 14 deletions, no .ts/.tsx, no test, no pin. Changeset frontmatter (first 4 lines) md5 8212bf42dc2f6ba4025ef7ced79c6d6d at both heads: byte-identical, '@object-ui/plugin-view': patch / '@object-ui/core': patch. Whole-file e4187dff… → ec4934ee…, as the dev reported.
  • Round-1 prose findings, re-read at head:
    • (a) Now "as they already did on the surfaces that call the resolver: the app-shell host, ObjectChart and the dashboard widgets. (A directly authored object-grid or list-view node still does not call it.)" TRUE: git grep resolveFilterPlaceholders refs/review/main production callers are app-shell ObjectDataPage/ObjectView/useOpenRecordList, ObjectChart (2, plus one direct resolveContextTokens), and dashboard DatasetWidget/ObjectDataTable/ObjectMetricWidget/ObjectPivotTable; plugin-grid/src and plugin-list/src have 0 reads (grep exit 1). schema-reference.md's "that the app-shell host, the charts and the dashboard widgets call" is the same set: TRUE.
    • (b) "matches no record" is replaced in all four files by "the filter is never widened" plus a backend sentence. "Never widened" is TRUE (the resolver leaves the token as written; onUnresolved defaults to console.warn, filter-tokens.ts:152-156). The backend sentence is NOT true as stated; see the two findings below.
    • (c) "the scope's three members (currentUserId, currentOrgId, onUnresolved), each compared on its own": TRUE, plugin-view ObjectView.tsx:288-291.
    • (d) "A non-plain object is a leaf whatever it is: a class instance with own keys carrying a token is also returned as the same instance, no longer copied and resolved. The contract review of this change found no in-repo caller that passes one.": TRUE at head (the two guards are unchanged since f0173ec3f; the caller census is round 1's, 13 call sites, all JSON-derived).
    • The signed-out console sentence: TRUE, see the contradiction below.
    • The date-macro UTC note: NOT true as stated, see below.
  • ⚠️ Contradiction settled at source: the dev is right, the round-1 record was wrong. packages/app-shell/src/views/ObjectView.tsx at head: the objectViewSchema memo (3040-3070) carries type, objectName, layout, showSearch, showFilters, showSort, showCreate, allowCreateView, viewActions, onNavigate and nothing else: 0 table/listViews/filter/views keys. renderListView (2457) sets const viewDef = activeView (2472, the app-shell's own merged view, 1945) and computes const base = (viewDef as any).filter ?? listSchema.filter (2672), then substituteFilterTokens(combined, filterScope) (2675) with filterScope = { currentUserId: currentUser.id, currentOrgId } (2164-2167) and currentUser falling to FALLBACK_USER 'current-user' (2158-2160). So the app-shell resolves its own RAW copy of the view filter on every renderListView call; plugin-view's resolved value in listSchema.filter is reached only when viewDef.filter is nullish, and in that case plugin-view's own activeView.filter is nullish too (same view) and the schema has no table, so mergedFilters is empty. The round-1 sentence "the app-shell's second pass now sees a resolved ISO string and is a no-op" is FALSE; the landing prose ("its renderListView resolves the active view's own filter itself … so its date macros and its 'current-user' fallback … are unchanged"; body: "resolved by the app-shell on each of its renders, as before") is TRUE.
    • Precision, not a falsehood: the filter still reaches plugin-view in the console, through the views={mergedViews} prop (3373/3416) rather than the schema. plugin-view activeView = viewsPropResolved.find(...) (1010), authoredFilters.view = currentNamedViewConfig?.filter || activeViewQueryInputs?.filter (1057). That is what makes the signed-out sentence true: plugin-view resolves {current_user_id} from the views prop under useFilterScope() = { currentUserId: null, currentOrgId: null } (AppContent.tsx:1029-1031, FilterScopeProvider passes no onUnresolved), leaves it and warns once (held in state, 286); the app-shell substitutes 'current-user' silently; the query is the app-shell's own resolved viewDef.filter, unchanged from base. The prose's "with no filter segment in the schema it gives this component" is literally true but reads as if plugin-view had nothing to resolve there; a reader who wants the mechanism will not find "the views prop" in any of the four files. A one-clause addition ("the active view's filter reaches this component through the views prop") would close it. Not a stop.
    • Doors in the console: only door 3 is reached. renderListView provided ⇒ the non-grid effect returns at 1089 and the mutation subscription at 950; renderContent returns renderListView({ schema: { type: 'list-view', … filter: mergedFilters } }) at 2370-2371 before any ObjectGrid branch. Door 3 carries the resolved active-view filter into listSchema.filter, and the app-shell then ignores it whenever the view defines a filter. No prose sentence claims all three doors are reached in the console (all say "all three paths a filter leaves this component by"), so nothing false there. Scope nit only: "With no user in scope, only this component's pass warns" is exact for {current_user_id}; for {current_org_id} with no organization both passes warn (both scopes carry currentOrgId: null). The sentence does not claim otherwise.
  • Hunt, four prose files plus the landing body. Two claims stated as fact are not true at objectstack origin/main; both were dictated by the round-1 record and copied faithfully.
    1. "the ObjectStack server resolves the literal token for a signed-in request and refuses it with a 400 otherwise" (changeset 28-29; schema-reference.md 910; plugin-view.mdx 205-207; README.md 266-268; body line 64 "refuses it with a 400 when the request has no user"). The 400 is UnresolvedFilterTokenError (core/src/utils/filter-tokens.ts:153-163, status = 400, thrown at 298-309 when !ctx.userId), and REST does map a numeric error.status (rest/src/error-response.ts:421). But on the REST face a request with no user never reaches the resolver: the per-request context builder returns undefined for !authz.userId (rest/src/rest-server.ts:3259, comment "no resolved principal → no context → 401"), assembleExecutionContext is the fail-closed default (core/src/security/assemble-execution-context.ts:377-390, "the surface answers 401"), and enforceAuth (2145-2154, "Reject anonymous requests with HTTP 401 — unconditionally") guards every data/meta route (9208, 9261, 9338, …). The 400 is reachable only where a user-less context reaches the engine: the runtime face's explicit guest entry (runtime/src/security/resolve-execution-context.ts:251 → assembleExecutionContextOrGuest, 395) or a system caller. So "refuses it with a 400 otherwise", unqualified, is wrong on the REST face (it is a 401 that never reads the filter) and right on the runtime face. Correct wording: "refuses the request otherwise (the REST face answers 401 at its auth gate before the filter is read; where a guest context reaches the engine, the resolver answers 400)".
    2. "as the ObjectStack server does in UTC" (changeset 23) and body line 61 "The ObjectStack server resolved them in UTC (… proxyDay, whose zone defaults to UTC; REST passes no timezone)". proxyDay(now, ctx.timezone) does default to UTC (filter-tokens.ts:105, 179-180), and filterTokenContextFrom(execCtx) passes timezone: execCtx?.timezone (429). But REST DOES pass one: for a signed-in request rest-server.ts:3262-3267 calls resolveLocalizationContext({ ql, settings, tenantId, userId }), which reads the tenant's localization.timezone setting (core/src/security/resolve-authz-context.ts:1570-1591, sys_setting fallback 1608-1624) and returns timezone: tz ?? 'UTC' (a string, always); assembleExecutionContext sets timezone: anonymous ? undefined : localization?.timezone (366); the engine hands opCtx.context to resolveWhereTokens (objectql/src/engine.ts:11056, 10775). The runtime face does the same (241-249). So the server resolves date macros in the tenant's configured timezone, UTC only by default. Round 1's git grep timezone -- packages/rest/src = 0 was a false negative: the assembly lives in core and the localization read is called from REST at 3262. "REST passes no timezone" is false; "resolved them in UTC" is true only for a tenant with no localization.timezone. Correct wording: "in the tenant's configured localization.timezone, UTC by default". The direction of the note (a browser's local day can differ from the server's day) stays true.
  • Everything else re-read in the four files and the body is true at head: the three segments and three doors (1055-1060, 1148-1149, 2124/2144/2151, 2324-2326); useFilterScope and the console mount; held in state keyed on structure and three members; ObjectGrid/ListView fetch keyed on filter identity (ListView.tsx:2666 lists schema.filter); {now}/{today} held until user, org or segment change, refreshKey reusing it; the second pass being a fixed point (pin cell 274); "All three segments are resolved even when one wins"; local-time macros (date-macros.ts:51-83, setHours(0,0,0,0), getFullYear()); the plugin-grid/plugin-list defect note; the pin counts (14 cells: 7+3+4 it); "the code was measured at f0173ec3f" and "CI at f0173ec3f: 43/40/3/0" match the round-1 record. The body's "Gates at 57eda69be, each exit 0" is the dev's own run, not re-run here (no local toolchain); CI's Lint, Doc Snippet Type Check, README Export Check, Changeset Claim Re-read on the head are the instrument and are green.

② Semver level

patch + patch, Clause-②: no, unchanged. The delta is prose only: no export, type, schema or accept set moves; the changeset frontmatter is byte-identical; the seat's own bullets 1-4 of 5830728744 hold. The prose round moves nothing.

③ Boundary flags

  • CI on head (latest read): 43 check runs, 39 success, 3 skipped (coverage ×2, dependabot), 1 in progress (Spec Main Shape Gate, run 36127421841), 0 failure. Lint, Type Check, Doc Snippet Type Check, README Export Check, Changeset Claim Re-read, Test (dist pins) and all eight test shards: success. No red check.
  • merge-tree: git merge-tree --write-tree refs/review/main refs/review/pr-10577 → dc84914f99a057778fc9e00fed4641016e87992a, exit 0, no conflicts. refs/review/main = aa38e195d, 20 commits past merge-base 9cbe4dbca; 0 of them touch the PR's 9 files. PR mergeable: true, mergeable_state: behind.
  • Should stop landing: the two stated-as-fact claims above. They land in three customer-facing docs pages and the CHANGELOG (the 400 claim in all four files and the body; the UTC claim in the changeset and the body), and both are wrong on the face the console talks to. The fix is a second prose-only round, no code: (1) in all four files, replace "refuses it with a 400 otherwise" with "refuses the request otherwise: the REST face answers 401 at its auth gate before the filter is read, and where a guest context reaches the engine the resolver answers 400"; (2) changeset line 23, "as the ObjectStack server does in UTC" → "as the ObjectStack server does in the tenant's configured localization.timezone, UTC by default"; body line 61, drop "REST passes no timezone" and say the server resolves in the tenant's configured timezone, UTC by default. Optional in the same round: name the views prop as the route by which the active view's filter still reaches this component in the console. Both errors originate in the round-1 record (5831301651 ① "refused with 400 (signed out)" and "REST threads no timezone"), not in the dev's reading; its ③ item (2) is superseded by the wording above.

Implemented-by: claude/issue-10506-named-view-context-tokens
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: FAIL

Exactly what stops it: the landing prose states two backend facts that are not true at objectstack origin/main: that a signed-out request is "refused with a 400" (the REST face answers 401 at enforceAuth before the resolver runs; the 400 is the runtime face's guest path) and that the server resolves date macros "in UTC" because "REST passes no timezone" (REST resolves the tenant's localization.timezone into the context; UTC is only the default). One prose-only round with the wording in ③ makes the PR landable; code, tests, semver, CI and merge state are all clean.


Generated by Claude Code

…ectly

Prose only, from the delta review of the previous head:
- a signed-out request is refused at the REST auth gate with a 401 before
  the filter is read; the resolver's 400 is reachable only where a guest
  context reaches the engine;
- the server resolves date macros in the tenant's configured
  localization.timezone, UTC only by default;
- the changeset names the views prop as the route by which the console's
  active-view filter still reaches ObjectView.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3047.6 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-R0qiyeuI.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 546.37KB 130.65KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.41KB 65.82KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.40KB 14.61KB
plugin-charts (index.js) 74.94KB 20.89KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.50KB 35.37KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.60KB 61.49KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 147.92KB 37.83KB
plugin-gantt (index.js) 169.62KB 41.91KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 48.26KB 15.04KB
plugin-list (index.js) 114.43KB 28.26KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.67KB 8.95KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.84KB 21.96KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 53aa3660c98f1b99d59aea13b8ab906f2d6d444c

① Derived judgments

  • Head. refs/review/pr-10577 = 53aa3660c; one commit past 57eda69be (docs(plugin-view): state the ObjectStack auth and timezone facts correctly); 7 commits from merge-base 9cbe4dbca; PR reads 7 commits, 9 files, +588/−15. The seven shas the body lists are all on the branch, in that order.
  • Prose only since the code head: yes. git diff --stat f0173ec3f..53aa3660c lists exactly .changeset/10506-named-view-context-tokens.md (39 lines), content/docs/api/schema-reference.md (2), content/docs/plugins/plugin-view.mdx (6), packages/plugin-view/README.md (6): 39 insertions, 14 deletions, no .ts/.tsx, no test, no pin. Changeset frontmatter (first 4 lines) md5 8212bf42dc2f6ba4025ef7ced79c6d6d at f0173ec3f, 57eda69be and 53aa3660c: byte-identical, '@object-ui/plugin-view': patch / '@object-ui/core': patch. The round-3 delta (57eda69be..53aa3660c) is three hunks in the changeset and one hunk in each docs file: the two facts plus the views-prop clause, nothing else.
  • Fact 1, re-verified at objectstack origin/main f09d4122b. The REST per-request context builder returns undefined for !authz.userId (rest/src/rest-server.ts:3259, comment "no resolved principal → no context → 401"); assembleExecutionContext is the fail-closed entry (core/src/security/assemble-execution-context.ts:380-384, undefined for no userId); every data route runs resolveExecCtx then enforceAuth ("Reject anonymous requests with HTTP 401 — unconditionally", 2145-2154) before it reads req.body (the two POST data handlers at 9653-9720: context at +14, enforceAuth at +15, req.body at +22). The only enforceAuth bypasses are the anonymous public-audience GET on /meta book/doc routes (5685-5687, isPublicAudienceRead) and the public form GET/POST (10840-10860); neither carries an authored filter. The 400 is UnresolvedFilterTokenError (core/src/utils/filter-tokens.ts:153-156, status = 400, thrown at 301-307 when !ctx.userId), mapped by REST through declaredHttpStatus (error-response.ts:420-425), and reached only where a context with no userId is handed to resolveWhereTokens (objectql/src/engine.ts:10773-10775): the runtime face's explicit guest entry (runtime/src/security/resolve-execution-context.ts:251, assembleExecutionContextOrGuest), consumed by runtime/src/dispatcher-plugin.ts and verify/src/handle.ts. Wording at head, all four files and body line 65: "refuses the request otherwise (the REST face answers 401 at its auth gate before the filter is read, and where a guest context reaches the engine the resolver answers 400)": TRUE, and it is the wording ③ of 5831522007 asked for. "refuses it with a 400 otherwise" and "when the request has no user" are gone from all five texts.
  • Fact 2, re-verified. resolveLocalizationContext (core/src/security/resolve-authz-context.ts:1481) returns the unwrapped { timezone, locale, currency } (return hit.value / return value of resolveLocalizationContextUncached), whose timezone is coerceTimeZone(setting) ?? 'UTC' on both the settings-service arm (1590) and the direct sys_setting arm (1620): always a string. REST calls it for every signed-in request (rest-server.ts:3262-3267) and passes the result as localization to assembleExecutionContext (3325-3331), which sets timezone: anonymous ? undefined : localization?.timezone (366); filterTokenContextFrom(execCtx) carries timezone: execCtx?.timezone (filter-tokens.ts:423-429); proxyDay(now, ctx.timezone) (179-180; "Defaults to UTC" at 105). The runtime face does the same (241-249). Wording at head: changeset "as the ObjectStack server does in the tenant's configured localization.timezone (UTC by default), a browser whose local day differs from the server's day can now get a different day", and body line 62 "resolved them in the tenant's configured localization.timezone, UTC by default: REST reads it through resolveLocalizationContext into the execution context, and objectql's resolveWhereTokens hands that context to resolveFilterTokens": TRUE. "REST passes no timezone" and the bare "in UTC" are gone (0 hits across the four files and the body). Precision, not a falsehood: the setting resolves through the localization SettingsManifest cascade ("platform default → global → tenant", docblock 1457-1460, "incl. the OS_LOCALIZATION_TIMEZONE env override", rest-api-plugin.ts:459-462), so a global-scope or env value can supply the zone when the tenant sets none; "the tenant's configured" names the value the tenant resolves to, which is what a reader needs.
  • The new views-prop clause: TRUE at head on both sides. app-shell ObjectView.tsx passes views={mergedViews} on both mounts (3373, 3416); mergedViews (3030-3037) is the host's views array with the active draft spread over its entry, each entry carrying its own filter; objectViewSchema (3040-3070) carries no filter/table/listViews. plugin-view ObjectView.tsx destructures views: viewsProp (876), viewsPropResolved (1003-1006), activeView = viewsPropResolved?.find(...) (1010), activeViewQueryInputs = useStableIdentity({ id, filter: activeView.filter, sort }) (1044-1048), and useResolvedFilterSegments({ view: currentNamedViewConfig?.filter || activeViewQueryInputs?.filter, … }, useFilterScope()) (1055-1062). So the active view's filter reaches this component through the views prop and is resolved there; the changeset clause "with no filter segment in the schema it gives this component; the active view's filter still reaches this component through the views prop" and body line 64's "The active view's filter still reaches this component there, through the views prop, so this component's pass still runs on it" are both exact.
  • Hunt, four files plus the full landing body: everything else is true at head.
    • Body line 31's description of 53aa3660c matches its diff; line 35 "lists exactly those four" TRUE; line 56 "CI at f0173ec3f: 43/40/3/0" matches record 5831301651.
    • Line 54 "Gates at 53aa3660c, each exit 0" is the dev's own run, not re-run here (no local toolchain; the Actions job-log endpoint is refused by the proxy). CI's Changeset Declaration, Changeset Overwrite Report, Changeset Fixed Group Check, Changeset Bump Policy, Line Citation Gate, Control Byte Scan, Internal Docs Link Check and Doc Fence Language Check are the instrument and are green on the head. Line 55's "18 listed paragraphs" is the dev's count, not re-measured; Changeset Claim Re-read green.
    • Line 61 and the changeset's "the surfaces that call the resolver: the app-shell host, ObjectChart and the dashboard widgets … a directly authored object-grid or list-view node still does not": re-checked on current main 58b7b3dc1, since main moved plugin-charts/src three times after base. resolveFilterPlaceholders( production reads: app-shell ObjectDataPage/ObjectView/useOpenRecordList, ObjectChart.tsx ×2, dashboard DatasetWidget/ObjectDataTable/ObjectMetricWidget/ObjectPivotTable; plugin-grid/src + plugin-list/src = 0 (grep exit 1). TRUE.
    • The seat's line 68 sentence "The seat filed it as objectui#10607; it is not fixed in this PR": objectui#10607 exists, open, label finding, created 2026-09-25T11:18Z, titled exactly this defect (a directly authored object-grid or list-view node sends the context tokens literally; neither package calls the shared resolver), body naming PR fix(plugin-view): resolve context tokens in the filters ObjectView hands on (objectui#10506) #10577 and the round-1 record. TRUE. Its Limits paragraph, outside this PR, restates the two round-1 errors verbatim; see ③.
    • Changeset "ObjectGrid and ListView key their fetch on the filter's identity": ObjectGrid.tsx:1761-1762 derives schemaFilterResult with a useMemo keyed on [schemaFilterSource] where schemaFilterSource = schema.filter; ListView.tsx:2666 per round 2. TRUE.
    • Everything the round-2 record marked TRUE at 57eda69be is untouched by this delta (three segments, three doors, held state on the three members, useFilterScope and the console mount, the fixed-point pin, local-time macros, the 14-cell pin count, the signed-out console sentence). Main moved none of app-shell/src/views/ObjectView.tsx, console/AppContent.tsx, react/src/hooks/useFilterScope.ts, plugin-grid/src, plugin-list/src since base (git log empty), so the prose stays true after merge.
    • Cosmetic only: the changeset reflow leaves one ~150-character line ("unchanged. A host that resolves the handed value again…"); no gate reads line length (Lint, Changeset Declaration green).

② Semver level

patch + patch, Clause-②: no, unchanged: confirmed. The changeset frontmatter is byte-identical across f0173ec3f, 57eda69be and 53aa3660c; the two prose-only commits touch no export, type, schema, accept set or test, so nothing the round-1 record measured moves.

③ Boundary flags

  • CI on head: 43 check runs at the final read, 40 success, 3 skipped (coverage ×2, dependabot), 0 failure, 0 in progress. The eight test shards, Type Check, Lint, Spec Main Shape Gate, Doc Snippet Type Check, README Export Check, Changeset Claim Re-read, Test (dist pins), Build & E2E and Build Docs all success. No red check.
  • merge-tree: git merge-tree --write-tree refs/review/main refs/review/pr-10577 → 93588ad9109fbbc6eba84b32c464bd4b3c0548a3, exit 0, no conflicts. refs/review/main = 58b7b3dc1, 22 commits past merge-base 9cbe4dbca; 0 of them touch the PR's 9 files. PR mergeable: true, mergeable_state: behind.
  • Serial overlap: 18 open PRs; file lists read for all 17 others. The only overlap is draft PR 8941 (lucide bump) on content/docs/api/schema-reference.md, at hunk @@ -684 (detail-view example) versus this PR's @@ -907 (my-deals paragraph): disjoint. None touches the other 8 files.
  • PR is draft: true. GitHub refuses to squash-merge a draft; the seat marks it ready for review before applying the landing body and merging. Mechanical, not a defect.
  • Follow-up outside the PR, not a stop: objectui#10607's Limits paragraph carries the two facts this round corrected ("refuses it with a 400 when there is no user"; "resolved there in UTC"). The seat should edit that card to the ③ wording of 5831522007 so the next developer does not re-import them.
  • Should stop landing: nothing. The two facts the round-2 record failed are corrected in all five texts with the wording it prescribed; the views-prop clause is true on both sides; code, tests, semver, CI and merge state are clean.

Implemented-by: claude/issue-10506-named-view-context-tokens
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 25, 2026 11:53
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit 6c2f3c5 Sep 25, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10506-named-view-context-tokens branch September 25, 2026 12:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation package: core plugin tests

Projects

None yet

2 participants