Skip to content

feat(lint): flag dead action/route references in dashboard header actions (#3367) - #3383

Merged
os-zhuang merged 3 commits into
mainfrom
claude/dead-dashboard-header-refs-7zo1ut
Jul 21, 2026
Merged

feat(lint): flag dead action/route references in dashboard header actions (#3367)#3383
os-zhuang merged 3 commits into
mainfrom
claude/dead-dashboard-header-refs-7zo1ut

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

What

Closes#3367. Dashboard header actions[] and widget actionUrl can reference a script/modal action or a url route that exists nowhere. Nothing in os validate / os build flags them, so they ship as buttons that render and silently do nothing when clicked — a false affordance. This is ADR-0049's "declared ≠ enforced" gate applied to references rather than to properties.

Nothing in the protocol schema can express this: actionUrl is a free string, so { actionType: 'script', actionUrl: 'export_dashboard_pdf' } parses and ships even when no such action is defined.

Changes

  • @objectstack/lint — new pure validateDashboardActionRefs(stack) => Finding[] rule (validate-dashboard-action-refs.ts, exported from index.ts). Walks every dashboard header.actions[] and widget actionUrl.
  • @objectstack/cli — wired into bothos validate and os build (compile), errors gate / warnings advisory, mirroring the existing validateWidgetBindings step.
  • docsvalidating-metadata.mdx: new "Dead action/route references" section, gate-table row, and step-output line.
  • changesetminor for the fixed group.

Resolution (mirrors the objectui runtime dispatch so lint flags exactly what would fail to resolve)

actionTyperesolves when…severity if dangling
scriptactionUrl names a defined action (stack.actions or an object's actions)error
modala defined action, or the runtime <verb>_<object> convention (create_/new_/add_/edit_/update_ + a real object), or a bare object nameerror
urla relative in-app path whose objects/reports/dashboards/pages/views route exists in the stackwarning
flow / api— (out of scope: engine/opaque targets)

External URLs (http(s)://, //), interpolated targets (${…}), and opaque routes are skipped to keep false positives near zero — the same conservative posture as the sibling lint-view-refs and validate-capability-references rules.

Severity rationale follows the issue's acceptance criteria: a dead script/modal target fails validation (it fails open at runtime as a dead button); an unresolved url route is at least warned (route resolution is app-context-dependent and a path may be served by another installed package or a host/console route).

Acceptance criteria

  • ✅ A dashboard action referencing an undefined script/modal action fails validation, naming the dashboard, button label, and unresolved target.
  • ✅ A url action pointing at a non-existent in-app route is warned.
  • ✅ Valid references (defined action / real route / <verb>_<object> against a real object) pass.
  • ✅ Fixture coverage for defined vs dangling targets (including the exact Flag dead action/route references in dashboard header actions (ADR-0049 for references) #3367 repro).

Tests

🤖 Generated with Claude Code

https://claude.ai/code/session_018KSiJuXDAi78CQD3Lv2TNJ


Generated by Claude Code

…ions
Dashboard header `actions[]` and widget `actionUrl` can name a script/modal
action or a url route that does not exist. Nothing in `os validate` / `os build`
flags them, so they ship as buttons that render and silently do nothing when
clicked — a false affordance. This applies ADR-0049's "declared ≠ enforced" gate
to references rather than properties (#3367).
New `validateDashboardActionRefs` rule in @objectstack/lint, wired into
`os validate` and `os build`:
- actionType 'script' | 'modal' → ERROR unless `actionUrl` resolves to a defined
action (stack.actions or an object's actions). 'modal' also resolves via the
runtime `<verb>_<object>` convention (create_/new_/add_/edit_/update_ + a real
object) and bare object names, mirroring the objectui modalHandler.
- actionType 'url' → WARNING when a relative in-app path names an
objects/reports/dashboards/pages/views route absent from the stack. External,
interpolated (${…}), and opaque routes are skipped to keep false positives near
zero — the same conservative posture as lint-view-refs / capability-references.
Covers dashboard `header.actions[]` and widget `actionUrl`. Fixture coverage for
defined vs dangling script/modal targets, the modal convention, and resolved vs
unresolved url routes, plus the #3367 repro. Docs + changeset included.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018KSiJuXDAi78CQD3Lv2TNJ
@vercel

vercelBot commented Jul 21, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 21, 2026 2:52pm

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling size/l labels Jul 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/ai/skills-reference.mdx(via packages/cli)
  • content/docs/api/client-sdk.mdx(via @objectstack/cli)
  • content/docs/api/data-flow.mdx(via @objectstack/cli)
  • content/docs/api/environment-routing.mdx(via @objectstack/cli)
  • content/docs/api/error-catalog.mdx(via @objectstack/cli)
  • content/docs/automation/hook-bodies.mdx(via packages/cli)
  • content/docs/deployment/backup-restore.mdx(via @objectstack/cli)
  • content/docs/deployment/self-hosting.mdx(via @objectstack/cli)
  • content/docs/getting-started/cli.mdx(via @objectstack/cli)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/cli)
  • content/docs/kernel/runtime-services/data-service.mdx(via packages/cli)
  • content/docs/kernel/runtime-services/index.mdx(via packages/cli)
  • content/docs/permissions/authentication.mdx(via @objectstack/cli)
  • content/docs/permissions/authorization.mdx(via @objectstack/lint)
  • content/docs/plugins/packages.mdx(via @objectstack/cli)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/cli)
  • content/docs/protocol/kernel/realtime-protocol.mdx(via @objectstack/cli)
  • content/docs/releases/implementation-status.mdx(via @objectstack/cli)
  • content/docs/releases/v16.mdx(via @objectstack/cli)

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.

The `os build` sample output in cli.mdx enumerates each compile step; the new
ADR-0049 dashboard action-reference check landed between the widget-binding and
SDUI-styling steps, so list it there too. Addresses the docs-drift advisory on
#3383. Docs-only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018KSiJuXDAi78CQD3Lv2TNJ
…-header-refs-7zo1ut
# Conflicts:
#	content/docs/getting-started/validating-metadata.mdx
#	packages/cli/src/commands/validate.ts
@os-zhuang
os-zhuang merged commit db160dd into mainJul 21, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/dead-dashboard-header-refs-7zo1ut branch July 21, 2026 14:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flag dead action/route references in dashboard header actions (ADR-0049 for references)

2 participants

@os-zhuang@claude