Skip to content

fix(spec,lint): gate unresolvable filter placeholders; freeze the {current_user_id} vocabulary (#3574) - #3594

Merged
os-zhuang merged 2 commits into
mainfrom
fix/dashboard-widget-current-user
Jul 27, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
fix/dashboard-widget-current-user

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jul 27, 2026 •

Copy link
Copy Markdown
Contributor

Closes #3574 (framework half). Runtime resolution ships in objectstack-ai/objectui#2857.

The bug behind the bug

The reported symptom is a dashboard widget filtered on {current_user_id} rendering 0. Investigating it turned up something worse than a missing substitution:

  • {current_user} — the spelling in the first repro row — has never existed anywhere. current_user is the RLS expression root (owner_id = current_user.id), a genuinely server-side mechanism. It was never a filter token. Only {current_user_id} is real.
  • The token was never part of the contract. Date macros are frozen in date-macros.zod.ts — a spec vocabulary, a lint-usable predicate, one client resolver. {current_user_id} had prose in an app.zod.ts JSDoc and three ad-hoc client implementations, each handling one surface's filter shape.
  • Six widgets were broken, not one — DatasetWidget, ObjectMetricWidget, ObjectDataTable, ObjectPivotTable, ObjectChart (both paths).
  • Our own docs taught two of the wrong spellings. skills/objectstack-ui/SKILL.md published a widget example using the token (which didn't work), and view.zod.ts documented {user_id}, which resolves nowhere.

What this PR does

Brings {current_user_id} up to the maturity date macros already had, and adds the gate that would have caught this at authoring time.

  • @objectstack/spec — data/context-tokens.zod.ts freezes CONTEXT_TOKENS (current_user_id, current_org_id) with isContextToken / isKnownFilterToken / classifyFilterToken and a CONTEXT_TOKEN_SUGGESTIONS near-miss table. The module is explicit that these are presentation scope, never an access boundary.
  • @objectstack/lint — validateFilterTokens (rule filter-token-unknown, severity error) walks filter / filters / runtimeFilter subtrees across dashboards, objects, views, reports, datasets, pages and apps.
  • @objectstack/cli — the gate runs in os validate and os compile.

Two design choices worth reviewing:

It scans for filter keys, not known surfaces. Enumerating surfaces is exactly how the dashboard got missed; a new surface following the convention is now covered the day it ships. Navigation recordId / params stay deliberately out of scope — they resolve AppContextSelector ids, which are meaningless in a filter.

It is an error, not a warning. The runtime failure is silent and indistinguishable from a genuine zero, so it survives human review. It is worse for AI authors: an AI reads a query returning 0 as a correct answer and builds on it, and its correction loop is author → validate → fix — a diagnostic only reaches it if it can fail the build. Each spelling in the suggestion table ({current_user}, {user_id}, {organization_id}) is correct somewhere else in the platform, which is why authors reach for them.

Verification

  • Gate proven red on real metadata: injecting {current_user} into the app-todo dashboard fails os validate, naming dashboard + widget + path and suggesting {current_user_id}. {today} in the same filter correctly passes.
  • Gate proven green: all three in-repo examples pass (3 dashboards, 28 pages, 4 views, 4 reports) — no false positives.
  • packages/spec 6734 tests, packages/lint 359 tests, all passing. tsc --noEmit clean on all three packages.

🤖 Generated with Claude Code

…ate unresolvable placeholders (#3574)

A dashboard widget filtered on `{current_user}` rendered `0` — not an error, a
zero indistinguishable from a metric that is legitimately empty, with nothing
in the console or the server log. `service_dashboard.my_open_cases_by_priority`
in the HotCRM template had shipped broken this way since the day it was written.

The token had never been part of the contract. Date macros were frozen in
`date-macros.zod.ts` with a spec vocabulary, a lint-usable predicate, and one
client resolver; `{current_user_id}` had only prose in an `app.zod.ts` JSDoc and
three ad-hoc client implementations, each handling one surface's filter shape.
Nothing could tell an author their token was wrong.

- spec: new `data/context-tokens.zod.ts` freezing CONTEXT_TOKENS
  (current_user_id, current_org_id) as the sibling of DATE_MACRO_TOKENS, with
  isContextToken / isKnownFilterToken / classifyFilterToken and a
  CONTEXT_TOKEN_SUGGESTIONS near-miss table. Documents what the tokens are NOT:
  presentation scope, never an access boundary — that is RLS, which uses the
  unrelated `current_user.id` expression root.
- lint: new `validateFilterTokens` (rule `filter-token-unknown`, error). Walks
  filter / filters / runtimeFilter subtrees across dashboards, objects, views,
  reports, datasets, pages and apps. It scans for filter KEYS rather than
  enumerating known surfaces, so a new surface following the convention is
  covered the day it ships — enumerating surfaces is how the dashboard was
  missed in the first place. Navigation recordId / params stay out of scope:
  they resolve AppContextSelector ids, meaningless in a filter.
- cli: the gate runs in `os validate` and `os compile`.

Error rather than warning because of who authors this metadata. An AI reads a
query returning 0 as a correct answer and builds on it; its correction loop is
author -> validate -> fix, so a diagnostic only reaches it if it can fail the
build. The three spellings the suggestion table covers — {current_user},
{user_id}, {organization_id} — are each correct somewhere else in the platform,
which is exactly why authors reach for them.

Also fixes a ViewSchema JSDoc example documenting `{user_id}`, which resolves
nowhere, and adds the Context Tokens reference to the objectstack-ui skill.

Runtime resolution ships in objectui (@object-ui/core resolveFilterPlaceholders).

Co-Authored-By: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 27, 2026 8:59am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling size/xl labels Jul 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/cli, @objectstack/lint, @objectstack/spec.

110 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via packages/cli, @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/api/data-flow.mdx (via @objectstack/cli)
  • content/docs/api/environment-routing.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/cli, packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/backup-restore.mdx (via @objectstack/cli)
  • content/docs/deployment/cli.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/cli)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/cli, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/cli)
  • content/docs/permissions/authorization.mdx (via @objectstack/lint, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…ports (#3574)

The public-API ratchet flagged 10 additions, 0 breaking — the new
context-token vocabulary. Snapshot regenerated with
`pnpm --filter @objectstack/spec gen:api-surface`.

Co-Authored-By: Claude <noreply@anthropic.com>
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 size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dashboard widget filters do not interpolate {current_user_id} — user-scoped widgets silently render 0

1 participant