Skip to content

feat(habits): AI tag suggestions (#223) - #320

Merged
thomasluizon merged 2 commits into
mainfrom
issue-223
Jun 26, 2026
Merged

feat(habits): AI tag suggestions (#223)#320
thomasluizon merged 2 commits into
mainfrom
issue-223

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

What

Adds AI tag suggestions to the habit create/edit form on both platforms (web + mobile). A "Suggest tags" button (sparkle) sits in the existing tag section; tapping it calls the new POST /api/tags/suggest with the habit's title + description, then renders 1–5 suggestion chips. Accepting a chip selects the existing tag (real id/color, no duplicate) or creates + selects a new one — flowing through the existing tagIds create payload / assign endpoint (no habit contract change).

Shared (packages/shared)

  • tags.suggest endpoint constant; suggestedTagSchema + suggestTagsResponseSchema Zod types next to habitTagSchema.
  • acceptSuggestedTagFlow in tag-selection-core (select existing id OR create+select new; honors maxTags, never duplicates) + tests.
  • 5 new habits.form.* i18n keys in en.json AND pt-BR.json.

Web (apps/web) — suggestTags Server Action (Zod-parsed at the boundary), useSuggestTags, acceptSuggestedTag on useTagSelection, SuggestedTagsRow component, wired into habit-form-fields.tsx.

Mobile (apps/mobile) — useSuggestTags (online apiClient POST, not offline-queued — an AI call needs connectivity), acceptSuggestedTag, SuggestedTagsRow (RN primitives + tokens), wired into tags-section.tsx + habit-form-fields.tsx.

Why

Implements #223. Suggestions are gated/metered server-side via the shared Astra chat allowance; the button is the only trigger (never per keystroke), disabled when the title is empty or the per-habit tag limit is reached. Errors surface as a non-blocking toast and the form stays fully usable manually.

Tests

  • Shared: acceptSuggestedTagFlow (existing-id select, new create+select, cap respected, no-duplicate, null-create skip).
  • Web: useSuggestTags hook; form-fields suggest → accept-existing flow + empty-state + disabled-when-empty.
  • Mobile: useSuggestTags (online apiClient POST); form-fields suggest → accept-existing flow + empty-state.
  • Green: shared 1018, web 1644, mobile suite; type-check + lint clean across all 3 workspaces.

Closes #223
Paired API PR: thomasluizon/orbit-api#259

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 26, 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)
orbit-ui-mobile-web Ignored Ignored Jun 26, 2026 4:19pm

Request Review

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

PR Review: feat(habits): AI tag suggestions (#223)

Decision: APPROVE

No Critical or High findings. One concretely-actionable Medium is noted below.

Rubric dimensions

Correctness: PASS - Core acceptSuggestedTagFlow logic is correct: max-tags guard, no-duplicate check for existing IDs, null-return guard for new-tag creation. Zod validates at the trust boundary (Server Action).

Cross-platform parity: PASS - Feature lands on both web and mobile with matching logic, identical component structure, parallel hook implementations.

i18n: PASS - All 5 new keys (suggestTags, suggestingTags, suggestedTagsLabel, noTagSuggestions, suggestTagsError) present in both en.json and pt-BR.json.

Backward compatibility: PASS - Additive only: new endpoint, new types, new i18n keys. Existing habits contract untouched.

API contract: NOT VERIFIABLE IN CI - orbit-api repo not mounted in this runner. Paired API PR #259 is cross-linked.

Security: PASS - suggestTags Server Action Zod-parses the raw response before returning it. Inputs serialized via JSON.stringify, no injection surface. Mobile uses apiClient (SecureStore JWT), not offline-queued, which is correct for an AI call requiring connectivity.

Tests: PASS - Shared: 5 acceptSuggestedTagFlow cases (existing-select, new-create, cap, no-duplicate, null-create-skip). Web: hook + form-fields (disabled-when-empty, suggest+accept, empty-state). Mobile: hook + form-fields (suggest+accept, empty-state).

Code standards: PASS - No any, no console.log, comments within policy.


Medium: unhandled promise rejection in handleAcceptSuggestion

Files:

  • apps/web/components/habits/habit-form-fields.tsx lines 167-177
  • apps/mobile/components/habits/habit-form-fields/tags-section.tsx lines 51-68

Both platforms void-discard the promise returned by tags.acceptSuggestedTag(...) while re-throwing inside the createTag callback. The throw propagates through acceptSuggestedTagFlow and surfaces as a rejected promise discarded via void, making it an unhandled rejection. In React Native this produces a yellow-box warning in dev and can be fatal in Hermes strict mode.

The UI behavior is already correct: the toast shows the error, and acceptSuggestedTagFlow guards on tagId being null before adding to selectedTagIds. Replacing "throw error" with "return null" achieves the same semantic outcome without the unhandled rejection.

This does not affect the happy path and the error is already surfaced to the user. Medium, not blocking.


Validation: Skipped per CI instructions. Build, Unit Tests, and SonarCloud run as separate required checks on this PR.

…dedupe watchedTitle (#223)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thomasluizon
thomasluizon merged commit 6c1b3ce into main Jun 26, 2026
2 checks passed
@thomasluizon
thomasluizon deleted the issue-223 branch June 26, 2026 16:19
@sonarqubecloud

Copy link
Copy Markdown

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approval review — see PR comments for full write-up

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AI auto-tagging for habits

1 participant