Skip to content

chore(ui): add shared tool contract export#669

Merged
Astro-Han merged 2 commits into
devfrom
codex/frontend-interface-contracts
May 16, 2026
Merged

chore(ui): add shared tool contract export#669
Astro-Han merged 2 commits into
devfrom
codex/frontend-interface-contracts

Conversation

@Astro-Han
Copy link
Copy Markdown
Owner

@Astro-Han Astro-Han commented May 16, 2026

Summary

Add a small public tool-name contract for frontend consumers:

  • export @opencode-ai/ui/tool-contract
  • move the app session status extractor to that shared contract while preserving its old re-export surface
  • use the same constants in UI message-part helpers for question, websearch, todowrite, and agent/task compatibility
  • add contract tests for app public import compatibility and opencode tool-name literals

Why

#638 calls out hardcoded tool/event names drifting silently across packages. This PR starts with the smallest reversible contract boundary: shared tool-name constants that both packages/app and packages/ui can import, with tests that fail if the public export disappears or the opencode tool literal changes.

Related Issue

Related to #638 and #599.

Human Review Status

Pending. A human should make the final merge decision after reviewing the final diff and verification evidence.

Review Focus

  • Is @opencode-ai/ui/tool-contract the right public home for frontend tool-name constants?
  • Are the compatibility tests narrow but useful for [Task] Audit interface inconsistencies across packages #638's hardcoded-name drift risk?
  • Confirm this PR is contract-only and does not refactor message flow, settings, visual shell, or runtime behavior.

Topology

Touched Files

  • packages/ui/package.json
  • packages/ui/src/components/tool-contract.ts
  • packages/ui/src/components/tool-contract.test.ts
  • packages/app/src/ui-tool-contract.test.ts
  • packages/app/src/pages/session/session-status-extractors.ts
  • packages/ui/src/components/message-part-registry.test.ts
  • packages/ui/src/components/message-part-stale.test.ts
  • packages/ui/src/components/message-part/grouping.ts
  • packages/ui/src/components/message-part/parts/tool.tsx
  • packages/ui/src/components/message-part/shared-utils.ts
  • packages/ui/src/components/tool-info.ts

Public Contract Diff

  • Added @opencode-ai/ui/tool-contract
  • Exported tool constants: TOOL_TODOWRITE, TOOL_WEBFETCH, TOOL_WEBSEARCH, TOOL_QUESTION, TOOL_AGENT_LEGACY, TOOL_AGENT
  • Exported TOOL_CONTRACT_NAMES, ToolContractName, and HIDDEN_TOOL_NAMES

Compatibility:

  • session-status-extractors.ts still re-exports the four constants it previously owned, so existing app imports do not break.
  • The app test imports from @opencode-ai/ui/tool-contract, proving the new package export resolves for an app consumer.
  • The ui contract test checks the key constants against packages/opencode/src/tool/* source literals.

Risk Notes

Low runtime risk. This changes constants and imports only. The main review risk is public API shape: if @opencode-ai/ui should not own this contract, this PR should be redirected before more consumers depend on it.

How To Verify

bun --cwd packages/ui test src/components/tool-contract.test.ts src/components/message-part-registry.test.ts src/components/message-part-stale.test.ts: 20 pass, 0 fail
bun --cwd packages/app test --preload ./happydom.ts src/ui-tool-contract.test.ts src/pages/session/session-status-extractors.test.ts: 1109 pass, 0 fail
bun run typecheck: ok, 8 packages successful
git diff --check origin/dev..HEAD: pass

CI note, 2026-05-16: perf-probe-baseline failed in the base-side capture job, not in this PR's changed code path. The observed failure was session-scroll-reading-long base coverage 0.6457533077251387 < 0.95; this PR does not touch app perf/scroll/runtime files. The failed run was rerun after this body cleanup.

Screenshots or Recordings

Not applicable. No visible UI changes.

Checklist

  • Human review status is stated above as pending, approved, or not required
  • I linked the related issue, or stated why there is no issue
  • This PR has type, primary area, and priority labels, or I requested maintainer labeling
  • I described the review focus and any meaningful risks
  • I listed the relevant verification steps and the key result for each
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope
  • I manually checked visible UI or copy changes when needed, with screenshots or recordings
  • I considered macOS and Windows impact for platform, packaging, updater, signing, paths, shell, or permissions changes
  • I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant
  • I reviewed the final diff for unrelated changes and suspicious dependency changes
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English

Summary by CodeRabbit

  • Refactor
    • Consolidated tool name definitions into a centralized contract system for enhanced consistency across the application.
    • Updated all tool references throughout the codebase to use unified constants.
    • Expanded test coverage to validate tool contract definitions and ensure system integrity.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8930b769-f311-4ffb-9e03-98547308253b

📥 Commits

Reviewing files that changed from the base of the PR and between ee885f6 and d9a9d56.

📒 Files selected for processing (12)
  • packages/app/src/pages/session/session-status-extractors.ts
  • packages/app/src/ui-tool-contract.test.ts
  • packages/opencode/test/github/ci-workflow.test.ts
  • packages/ui/package.json
  • packages/ui/src/components/message-part-registry.test.ts
  • packages/ui/src/components/message-part-stale.test.ts
  • packages/ui/src/components/message-part/grouping.ts
  • packages/ui/src/components/message-part/parts/tool.tsx
  • packages/ui/src/components/message-part/shared-utils.ts
  • packages/ui/src/components/tool-contract.test.ts
  • packages/ui/src/components/tool-contract.ts
  • packages/ui/src/components/tool-info.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/ui/src/components/message-part-registry.test.ts
  • packages/ui/src/components/message-part/grouping.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/ui/package.json
  • packages/ui/src/components/message-part/shared-utils.ts
  • packages/app/src/ui-tool-contract.test.ts
  • packages/ui/src/components/message-part-stale.test.ts
  • packages/ui/src/components/tool-contract.ts
  • packages/app/src/pages/session/session-status-extractors.ts
  • packages/ui/src/components/message-part/parts/tool.tsx

📝 Walkthrough

Walkthrough

This PR centralizes tool-name string constants into a single tool-contract.ts module exported from the UI package, adds validation tests to ensure contract constants match implementation files, and updates UI rendering, dispatch, and app-layer components to import and use these constants instead of hardcoded strings.

Changes

Tool Contract Centralization and Adoption

Layer / File(s) Summary
Tool contract definition and package export
packages/ui/src/components/tool-contract.ts, packages/ui/package.json
Define TOOL_TODOWRITE, TOOL_WEBFETCH, TOOL_WEBSEARCH, TOOL_QUESTION, TOOL_AGENT_LEGACY, TOOL_AGENT constants; export TOOL_CONTRACT_NAMES list, derived ToolContractName union type, and HIDDEN_TOOL_NAMES; add ./tool-contract subpath export to package.json.
Contract validation tests
packages/ui/src/components/tool-contract.test.ts
Add Bun tests that read opencode implementation files and regex-match tool constants against Tool.define(...) calls; validate TOOL_AGENT_LEGACY equals "task" and HIDDEN_TOOL_NAMES equals ["todowrite"].
UI message-part rendering updates
packages/ui/src/components/message-part/grouping.ts, packages/ui/src/components/message-part/parts/tool.tsx
Update grouping.ts to check part.tool === TOOL_QUESTION in renderability logic; update tool.tsx to use tool constants for hiding todowrite parts, checking question dismissed/interrupted states, agent task-link memos, and websearch error rendering selection.
Shared utilities HIDDEN_TOOLS derivation
packages/ui/src/components/message-part/shared-utils.ts
Initialize HIDDEN_TOOLS from imported HIDDEN_TOOL_NAMES instead of hardcoding new Set(["todowrite"]).
Tool info dispatch switch updates
packages/ui/src/components/tool-info.ts
Update toolInfoForInput switch branches to match TOOL_WEBFETCH, TOOL_AGENT_LEGACY, TOOL_AGENT, and TOOL_TODOWRITE constants instead of string literals in case labels.
UI rendering test assertions
packages/ui/src/components/message-part-stale.test.ts, packages/ui/src/components/message-part-registry.test.ts
Update tool-error rendering tests to assert part().tool checks against TOOL_QUESTION and TOOL_WEBSEARCH constants; update HIDDEN_TOOLS assertion to expect derivation from HIDDEN_TOOL_NAMES.
App layer re-export and validation
packages/app/src/pages/session/session-status-extractors.ts, packages/app/src/ui-tool-contract.test.ts
Update session-status-extractors.ts to import and re-export tool constants from @opencode-ai/ui/tool-contract; add app-layer test to validate re-exported constants match expected string values.
CI workflow test updates
packages/opencode/test/github/ci-workflow.test.ts
Extend CI workflow test expectations to include unit-ui-focused job dependency for the aggregate check job; validate UNIT_UI_FOCUSED_RESULT environment variable wiring in the CI result validation step.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Astro-Han/pawwork#71: Both PRs modify packages/app/src/pages/session/session-status-extractors.ts to work with tool-name constants and extraction logic.
  • Astro-Han/pawwork#620: Both PRs update HIDDEN_TOOLS and tool-name handling in the UI message-part layer, where this PR changes the hidden-tool set to derive from centralized contract constants.

Poem

A rabbit hops through tool contracts bright,
Where strings once scattered, now centralize right.
Constants now unified, no more duplicates stray,
UI and app layer harmonize the way. 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title "chore(ui): add shared tool contract export" accurately summarizes the main change—introducing a shared tool-name contract export for the UI package.
Description check ✅ Passed The description is comprehensive and follows the template with all key sections completed: Summary, Why, Related Issue, Human Review Status, Review Focus, Risk Notes, How To Verify, Screenshots, and Checklist all properly addressed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/frontend-interface-contracts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Astro-Han Astro-Han added task Maintainer or agent execution task tech-debt Internal cleanup and maintainability debt ui Design system and user interface P2 Medium priority labels May 16, 2026
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request centralizes tool name constants by introducing a new @opencode-ai/ui/tool-contract module and replacing hardcoded string literals throughout the app and ui packages. It also adds contract tests to verify consistency with the opencode package and updates the frontend architecture manifest. I have no feedback to provide.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 16, 2026

Perf delta summary

Comparator: pass

Profile / Scenario interaction median interaction worst long task max tbt frame gap p95 frame gap max jank count cls status
default / homepage-cold 24 -> 32 (+8) 72 -> 56 (-16) 66 -> 63 (-3) 16 -> 13 (-3) 16.8 -> 16.8 (0) 166.7 -> 166.6 (-0.1) 4 -> 3 (-1) 0 -> 0 (0) pass
default / long-session-input-lag 40 -> 48 (+8) 48 -> 48 (0) 0 -> 0 (0) 0 -> 0 (0) 16.7 -> 16.8 (+0.1) 16.8 -> 16.8 (0) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-streaming-long 40 -> 48 (+8) 64 -> 64 (0) 0 -> 0 (0) 0 -> 0 (0) 16.7 -> 16.7 (0) 16.8 -> 33.4 (+16.6) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-call-expand 24 -> 16 (-8) 40 -> 24 (-16) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.7 (-0.1) 16.8 -> 16.7 (-0.1) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-default-open-heavy-bash 24 -> 24 (0) 32 -> 32 (0) 66 -> 64 (-2) 18 -> 14 (-4) 50 -> 33.4 (-16.6) 183.3 -> 166.6 (-16.7) 3 -> 2 (-1) 0 -> 0 (0) pass
default / terminal-side-panel-open 56 -> 56 (0) 56 -> 64 (+8) 0 -> 0 (0) 0 -> 0 (0) 33.4 -> 33.4 (0) 33.4 -> 33.4 (0) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-scroll-reading 24 -> 24 (0) 32 -> 40 (+8) 0 -> 0 (0) 0 -> 0 (0) 16.7 -> 16.7 (0) 16.7 -> 16.7 (0) 0 -> 0 (0) 0.505 -> 0.505 (0) warn: cls
low-end / session-scroll-reading-long 56 -> 64 (+8) 64 -> 96 (+32) 91 -> 88 (-3) 45 -> 75 (+30) 16.8 -> 16.8 (0) 216.6 -> 150 (-66.6) 4 -> 3 (-1) 0.011 -> 0.011 (0) pass
low-end / session-timeline-recompute 120 -> 120 (0) 144 -> 136 (-8) 109 -> 104 (-5) 174 -> 162 (-12) 83.4 -> 100 (+16.6) 183.3 -> 166.7 (-16.6) 3 -> 3 (0) 0.081 -> 0.081 (0) pass
low-end / concurrent-shimmer-extreme 0 -> 0 (0) 0 -> 0 (0) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.7 (-0.1) 16.8 -> 16.8 (0) 0 -> 0 (0) 0 -> 0 (0) pass

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/ui/src/components/tool-contract.test.ts (1)

25-26: ⚡ Quick win

Make literal matching quote-agnostic in compatibility checks.

The current assertion can fail on harmless quote-style changes (' vs "), creating noisy test failures.

Proposed change
-      expect(source.includes(`"${tool}"`)).toBe(true)
+      expect(source).toMatch(new RegExp(`["']${tool}["']`))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/components/tool-contract.test.ts` around lines 25 - 26, The
test assertion is brittle to quote-style changes: instead of checking
source.includes(`"${tool}"`), update the assertion in tool-contract.test.ts to
be quote-agnostic by matching the tool identifier with either single or double
quotes (e.g., use a regular-expression test or check both quoted variants) when
calling expect on source; reference the variables/source and the expect(...)
assertion so you replace the includes check with a regex that matches
['"]{tool}['"] (or a logical OR of `'${tool}'` and `"${tool}"`) to avoid failing
on harmless quote-style changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/ui/src/components/tool-contract.test.ts`:
- Around line 25-26: The test assertion is brittle to quote-style changes:
instead of checking source.includes(`"${tool}"`), update the assertion in
tool-contract.test.ts to be quote-agnostic by matching the tool identifier with
either single or double quotes (e.g., use a regular-expression test or check
both quoted variants) when calling expect on source; reference the
variables/source and the expect(...) assertion so you replace the includes check
with a regex that matches ['"]{tool}['"] (or a logical OR of `'${tool}'` and
`"${tool}"`) to avoid failing on harmless quote-style changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1113a46b-a377-417e-bbfe-cd5f716489cc

📥 Commits

Reviewing files that changed from the base of the PR and between 03140f7 and ee885f6.

📒 Files selected for processing (11)
  • packages/app/src/pages/session/session-status-extractors.ts
  • packages/app/src/ui-tool-contract.test.ts
  • packages/ui/package.json
  • packages/ui/src/components/message-part-registry.test.ts
  • packages/ui/src/components/message-part-stale.test.ts
  • packages/ui/src/components/message-part/grouping.ts
  • packages/ui/src/components/message-part/parts/tool.tsx
  • packages/ui/src/components/message-part/shared-utils.ts
  • packages/ui/src/components/tool-contract.test.ts
  • packages/ui/src/components/tool-contract.ts
  • packages/ui/src/components/tool-info.ts

@Astro-Han Astro-Han force-pushed the codex/frontend-interface-contracts branch from ee885f6 to c07ea8d Compare May 16, 2026 08:13
@github-actions github-actions Bot added the app Application behavior and product flows label May 16, 2026
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested priority: P2 (includes user-path files (packages/app/src/pages/session/session-status-extractors.ts, packages/app/src/ui-tool-contract.test.ts)).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

@Astro-Han Astro-Han force-pushed the codex/frontend-interface-contracts branch from c07ea8d to 7d909e7 Compare May 16, 2026 08:18
@github-actions github-actions Bot added the harness Model harness, prompts, tool descriptions, and session mechanics label May 16, 2026
@Astro-Han
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Astro-Han Astro-Han merged commit 187a929 into dev May 16, 2026
28 checks passed
@Astro-Han Astro-Han deleted the codex/frontend-interface-contracts branch May 16, 2026 09:30
Astro-Han added a commit that referenced this pull request May 16, 2026
Goal:
Extract the timeline staging owner out of MessageTimeline as the first #601 message-flow stack root.

Scope:
- Move createTimelineStaging into packages/app/src/pages/session/session-timeline-staging.ts.
- Keep MessageTimeline wired with the same sessionKey, turnStart, renderedUserMessages, and { init: 10, batch: 3 } config.
- Add browser-condition staging tests for non-windowed render, staged batches, active-session message growth, completed-session backfill, and session switch rAF cancellation.
- Preserve active staging when the same session receives more messages mid-stage so the historical window does not pop to full render.
- Add the concrete #670 boundary to the frontend architecture manifest.

Verification:
- bun --cwd packages/app test --preload ./happydom.ts src/pages/session/session-timeline-staging.test.ts src/pages/session/use-session-history-window.test.ts src/pages/session/session-timeline-scroll-controller.test.ts src/pages/session/session-timeline-scroll-anchors.test.ts -> 1110 pass / 2674 expects
- bun run typecheck -> 8 successful tasks
- git diff --check
- GitHub checks green, including ci, unit-app, unit-opencode, unit-desktop, unit-ui-focused, desktop smoke, e2e artifacts, perf-probe-baseline, CodeQL, and CodeRabbit.
- reviewThreads unresolved = 0

Review follow-ups:
- Fixed Gemini staging-pop thread and resolved it after replying in-thread.
- Added the missing manifest entry that the PR body claimed.
- Refreshed PR body verification after #667 and #669 landed on dev.

Residual risk:
- Electron manual verification was not run because this is scoped to behavior-preserving extraction plus tests, with no visible UI or copy change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Application behavior and product flows harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority task Maintainer or agent execution task tech-debt Internal cleanup and maintainability debt ui Design system and user interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant