Skip to content

feat(habits): inline create-habit AI suggestion (#220) - #318

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

thomasluizon merged 2 commits into
mainfrom
issue-220

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

What

Adds a "Suggest with AI" affordance to the create-habit form on web and mobile. Type a name, tap the ghost pill, and the new POST /api/habits/suggest-setup endpoint returns a suggested emoji + schedule + sub-habit breakdown that is applied into the editable form (nothing is auto-committed).

  • Shared (packages/shared): habitSetupSuggestionRequestSchema / habitSetupSuggestionSchema Zod types, the habits.suggestSetup endpoint constant, a pure buildHabitFormPatchFromSuggestion mapper (decides recurring vs one-time, keeps days only for a daily schedule), and habits.form.aiSuggest* keys in both en.json and pt-BR.json.
  • Web: suggestHabitSetup Server Action + useHabitSuggestion mutation hook (parses the response, invalidates subscription + profile so the remaining-allowance UI refreshes); GhostPill in habit-form-fields.tsx; apply + toast wiring in create-habit-modal.tsx.
  • Mobile: mirror via apiClient (read-style call — not routed through the SQLite offline queue) and the RN PillButton ghost variant.
  • Sub-habit suggestions apply for Pro users only (free users keep the existing paywall); emoji + schedule apply for everyone. Allowance limit (403) → friendly "limit reached" toast; other failures → error toast; a parsed-but-empty suggestion → "add the details yourself" toast.

Why

Implements #220 (inline create-habit AI). The trigger is explicit (a tap, disabled while empty/pending — never per keystroke), and the backend bounds cost via allowance gate + server cache + a habit-suggest rate-limit policy + the cheap SubTask model tier.

Parity & compatibility

Identical logic on web + mobile (transport adapter only); both locales covered; "Orbit"/"Astra" stay literal. Additive shared contract — no breaking change for old mobile clients.

Tests

Vitest/RTL — shared mapper + schema (habit-suggestion.test.ts), web hook, mobile hook. Full suites green (shared 1021, web 1643, mobile 626).

Closes #220
Paired API PR: thomasluizon/orbit-api#257

🤖 Generated with Claude Code

Add a "Suggest with AI" affordance to the create-habit form on web and mobile. Tapping it calls the new POST /api/habits/suggest-setup endpoint for the entered title and applies the suggested emoji, schedule (recurring vs one-time, frequency, days) and — for Pro users — a sub-habit breakdown into the editable form; everything stays editable before submit.

Shared: habitSetupSuggestionRequest/Response Zod schemas, the suggestSetup endpoint constant, a pure buildHabitFormPatchFromSuggestion mapper, and aiSuggest* i18n keys in both en and pt-BR. Web uses a Server Action; mobile calls apiClient directly (never the offline queue). Both surface the allowance limit (403) and error/empty states via toasts. Closes #220.

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:04pm

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.

Code Review: PR #318

Scope: PR #318 — feat(habits): inline create-habit AI suggestion (#220)
Recommendation: APPROVE

Summary

This PR implements the "Suggest with AI" affordance on the create-habit form across both web and mobile. A new POST /api/habits/suggest-setup endpoint is called when the user taps the ghost pill; the response (emoji + schedule + sub-habit breakdown) is applied into the editable form without auto-committing. The implementation is additive, well-tested, and behaviorally symmetric across platforms. No Critical or High findings survive adversarial review.

Findings

Critical

None.

High

None.

Medium

None.

Low / Info

None.


Subagents

Agent Verdict
parity-checker PAIRED
i18n-syncer IN SYNC
contract-aligner MATCH (frontend-side; orbit-api paired at thomasluizon/orbit-api#257)
security-reviewer N/A (no orbit-api code in diff)

Parity: Both use-habit-suggestion.ts hooks are identical in mutation signature, schema parsing, and onSuccess invalidation. Both create-habit-modal.tsx files wire handleSuggest identically (PAY_GATE → limit toast, network error → error toast, empty patch → info toast, non-empty → success toast). Both habit-form-fields.tsx files accept and render onSuggestSetup/isSuggesting with the same disabled-when-empty logic. Platform-adapter seam only (Server Action vs apiClient).

i18n: All 6 new keys (aiSuggest, aiSuggesting, aiSuggestApplied, aiSuggestError, aiSuggestEmpty, aiSuggestLimitReached) exist in both en.json and pt-BR.json under habits.form, in identical order, with no orphaned callsites.

Contract: API.habits.suggestSetup = /api/habits/suggest-setup. Both new Zod schemas are purely additive — no existing fields renamed or removed. No breaks to old mobile clients.


Backward-compat guard

All shared-type changes are additive. No field was removed or renamed in any existing schema. No breaking change for old mobile clients.


Validation

Check Result
Lint N/A (CI-only)
Type check N/A (CI-only)
Tests PASS — shared 6, web 3, mobile 4; modal mocks updated correctly
Build (api) N/A (orbit-api in paired PR #257)

Deferred

  • Dimension 13 (Backend hard rules): N/A — no orbit-api code in this diff.
  • Dimension 12 (Security — API side): N/A; frontend checks pass.
  • API DTO shape: orbit-api not mounted; C# DTO and FluentValidation deferred to thomasluizon/orbit-api#257.

What is good

  • Zero trust-boundary leaks: habitSetupSuggestionSchema.parse() runs inside mutationFn on both platforms before data reaches form state.
  • PAY_GATE path is correct and tested on both platforms.
  • Offline-queue excluded deliberately — suggestion has no offline value; JSDoc WHY note present.
  • Sub-habit gating: hasProAccess && patch.subHabitTitles.length > 0 — free users never get sub-habit suggestions.
  • buildHabitFormPatchFromSuggestion is pure and covers all edge cases (daily/non-daily/one-time/null-quantity).
  • i18n-first: all toasts and button labels go through t().
  • No comment-policy violations.

Recommendation

Merge as-is. Confirm the API contract in thomasluizon/orbit-api#257 before deploying.

@sonarqubecloud

Copy link
Copy Markdown

@thomasluizon
thomasluizon merged commit 387edbe into main Jun 26, 2026
10 checks passed
@thomasluizon
thomasluizon deleted the issue-220 branch June 26, 2026 16:09

@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.

APPROVE - Two Medium findings (no Critical/High). [1] Em dashes in en.json + pt-BR.json aiSuggestEmpty violate DESIGN.md:169 ban - replace with period. [2] Web create-habit-modal.test.tsx missing mock for useHabitSuggestion and missing showSuccess/showInfo in useAppToast mock - tests still pass but handleSuggest form-wiring is untested. Parity PAIRED, i18n IN SYNC, backward-compat CLEAR, security clean.

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.

Inline create-habit AI (schedule/emoji/breakdown)

1 participant