Skip to content

Commit f5b4596

Browse files
committed
Merge branch 'main' into chore/66456-remove-onyx-connect-shared-billing-grace-period-subscription-utils-2
2 parents 259bd34 + a152afa commit f5b4596

File tree

750 files changed

+21950
-9091
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

750 files changed

+21950
-9091
lines changed

.claude/agents/code-inline-reviewer.md

Lines changed: 17 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
name: code-inline-reviewer
44
description: Reviews code and creates inline comments for specific rule violations.
5-
tools: Glob, Grep, Read, TodoWrite, Bash, BashOutput, KillBash
5+
tools: Glob, Grep, Read, Bash, BashOutput
66
model: inherit
77
---
88

@@ -41,48 +41,26 @@ Each rule file contains:
4141
- **For large files (>5000 lines):** Use the Grep tool with Search Patterns from each rule's Review Metadata to locate potential violations. Focus on changed portions shown in the diff.
4242
- **For smaller files:** You may read the full file using the Read tool
4343
- **If a Read fails with token limit error:** Immediately switch to using Grep with targeted patterns for the rules you're checking
44-
- For each rule: evaluate the Condition and "DO NOT flag" exceptions. Mark the rule as checked on the checklist. A single rule **can produce multiple violations** — flag each separately.
45-
5. **For each violation found, immediately create an inline comment** using the available GitHub inline comment tool. Do not batch — create the comment as soon as you confirm a violation.
46-
6. **Required parameters for each inline comment:**
47-
- `path`: Full file path (e.g., "src/components/ReportActionsList.tsx")
44+
3. **Search strategy for large files:** Use the search patterns defined in each rule's "Search patterns" field to efficiently locate potential violations with Grep.
45+
4. **Return your findings as structured JSON output.** Your response must be a JSON object matching this schema:
46+
```json
47+
{ "violations": [ { "ruleId": "...", "path": "...", "line": ..., "body": "..." } ] }
48+
```
49+
- `ruleId`: The rule ID (e.g., `PERF-1`, `CONSISTENCY-2`)
50+
- `path`: Full file path (e.g., `src/components/ReportActionsList.tsx`)
4851
- `line`: Line number where the issue occurs
49-
- `body`: Concise and actionable description of the violation and fix, following the below Comment Format
50-
7. **Each comment must reference exactly one Rule ID.**
51-
8. **Output must consist exclusively of calls to createInlineComment.sh in the required format.** No other text, Markdown, or prose is allowed.
52-
9. **If no violations are found, add a reaction to the PR**:
53-
Add a +1 reaction to the PR using the `addPrReaction` script (available in PATH from `.claude/scripts/`). The script takes ONLY the PR number as argument - it always adds a "+1" reaction, so do NOT pass any reaction type or emoji.
54-
10. **Add reaction if and only if**:
55-
- You examined EVERY changed line in EVERY changed file (via diff + targeted grep/read)
56-
- You checked EVERY changed file against ALL rules
57-
- You found ZERO violations matching the exact rule criteria
58-
- You verified no false negatives by checking each rule systematically
59-
If you found even ONE violation or have ANY uncertainty do NOT add the reaction - create inline comments instead.
60-
11. **DO NOT invent new rules, stylistic preferences, or commentary outside the listed rules.**
61-
12. **DO NOT describe what you are doing, create comments with a summary, explanations, extra content, comments on rules that are NOT violated or ANYTHING ELSE.**
62-
Only inline comments regarding rules violations are allowed. If no violations are found, add a reaction instead of creating any comment.
63-
EXCEPTION: If you believe something MIGHT be a Rule violation but are uncertain, err on the side of creating an inline comment with your concern rather than skipping it.
64-
65-
## Tool Usage Example
66-
67-
For each violation, call the createInlineComment.sh script like this:
68-
69-
```bash
70-
createInlineComment.sh 'src/components/ReportActionsList.tsx' '<Body of the comment according to the Comment Format>' 128
71-
```
72-
73-
**IMPORTANT**: Always use single quotes around the body argument to properly handle special characters and quotes.
74-
75-
If ZERO violations are found, use the Bash tool to add a reaction to the PR body:
76-
77-
```bash
78-
addPrReaction.sh <PR_NUMBER>
79-
```
80-
81-
**IMPORTANT**: Always use the `addPrReaction.sh` script (available in PATH from `.claude/scripts/`) instead of calling `gh api` directly.
52+
- `body`: Concise and actionable description of the violation and fix, formatted per the Comment Format below
53+
5. **Each violation must reference exactly one Rule ID.**
54+
6. **If no violations are found, return an empty violations array:** `{ "violations": [] }`
55+
7. **Do NOT post comments, call scripts, or add reactions.** Only return the structured JSON.
56+
8. **DO NOT invent new rules, stylistic preferences, or commentary outside the listed rules.**
57+
9. **DO NOT describe what you are doing or add extra content.**
58+
EXCEPTION: If you believe something MIGHT be a Rule violation but are uncertain, err on the side of including it in the violations array rather than skipping it.
59+
10. **Reality check before posting**: Before creating each inline comment, re-read the specific code one more time and confirm the violation is real. If upon re-reading you realize the code is actually correct, **do NOT post the comment** — silently skip it and move on. Never post a comment that flags a violation and then concludes it is not actually a problem.
8260

8361
## Comment Format
8462

85-
Build the docs link by mapping the ruleId to its rule filename:
63+
Use this format for the `body` field of each violation:
8664

8765
```
8866
### ❌ <Rule ID> [(docs)](https://github.com/Expensify/App/blob/main/.claude/skills/coding-standards/rules/<rule-filename>.md)
@@ -91,8 +69,3 @@ Build the docs link by mapping the ruleId to its rule filename:
9169
9270
<Suggested, specific fix preferably with a code snippet>
9371
```
94-
95-
For example, a PERF-1 violation links to:
96-
`https://github.com/Expensify/App/blob/main/.claude/skills/coding-standards/rules/perf-1-no-spread-in-renderitem.md`
97-
98-
**CRITICAL**: You must actually call the createInlineComment.sh script for each violation. Don't just describe what you found - create the actual inline comments!

.claude/commands/review-code-pr.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
allowed-tools: Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(addPrReaction.sh:*),Bash(createInlineComment.sh:*)
2+
allowed-tools: Bash(gh pr diff:*),Bash(gh pr view:*)
33
description: Review a code contribution pull request
44
---
55

@@ -8,10 +8,13 @@ Perform a comprehensive PR review using a specialized subagent:
88
## Inline Review
99
Use the code-inline-reviewer agent to:
1010
- Scan all changed source code files
11-
- Create inline comments for specific review rule violations
12-
- Focus on line-specific, actionable feedback
11+
- Detect review rule violations with line-specific, actionable feedback
1312

14-
Run the agent and ensure its feedback is posted to the PR.
13+
Run the agent. It will return structured JSON with any violations found.
14+
15+
## Output
16+
Return the subagent's violations JSON as your structured output unchanged.
17+
Do NOT post comments or reactions yourself - the workflow handles that.
1518

1619
<important>
1720
Keep feedback concise.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"violations": {
5+
"type": "array",
6+
"items": {
7+
"type": "object",
8+
"properties": {
9+
"ruleId": {
10+
"type": "string"
11+
},
12+
"path": {
13+
"type": "string"
14+
},
15+
"line": {
16+
"type": "integer"
17+
},
18+
"body": {
19+
"type": "string"
20+
}
21+
},
22+
"required": ["ruleId", "path", "line", "body"]
23+
}
24+
}
25+
},
26+
"required": ["violations"]
27+
}

.claude/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"hooks": [
77
{
88
"type": "command",
9-
"command": "FILE=$(jq -r '.tool_input.file_path') && [ -f \"$FILE\" ] && ./node_modules/.bin/prettier --experimental-cli --write --ignore-unknown \"$FILE\""
9+
"command": "FILE=$(jq -r '.tool_input.file_path') && [ -f \"$FILE\" ] && ./node_modules/.bin/prettier --experimental-cli --no-cache --write --ignore-unknown \"$FILE\""
1010
}
1111
]
1212
}

.claude/skills/onyx/SKILL.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
---
2+
name: onyx
3+
description: Onyx state management patterns — useOnyx hook, action files, optimistic updates, collections, and offline-first architecture. Use when working with Onyx connections, writing action files, debugging state, or implementing API calls with optimistic data.
4+
---
5+
6+
## Core Concepts
7+
8+
Onyx is a **persistent storage solution wrapped in a Pub/Sub library** that enables reactive, offline-first data management — key-value storage with automatic AsyncStorage persistence, reactive subscriptions, and collection management.
9+
10+
For the full API reference (initialization, storage providers, cache eviction, benchmarks, Redux DevTools), see https://github.com/Expensify/react-native-onyx/blob/main/README.md.
11+
12+
## Common Patterns
13+
14+
### Action File Pattern
15+
16+
**IMPORTANT:** Onyx state must only be modified from action files (`src/libs/actions/`). Never call `Onyx.merge`, `Onyx.set`, `Onyx.clear`, or `API.write` directly from a component.
17+
18+
```typescript
19+
import Onyx from 'react-native-onyx';
20+
import ONYXKEYS from '@src/ONYXKEYS';
21+
22+
function setIsOffline(isNetworkOffline: boolean, reason = '') {
23+
if (reason) {
24+
Log.info(`[Network] Client is ${isNetworkOffline ? 'offline' : 'online'} because: ${reason}`);
25+
}
26+
Onyx.merge(ONYXKEYS.NETWORK, {isOffline: isNetworkOffline});
27+
}
28+
29+
export {setIsOffline};
30+
```
31+
32+
### Optimistic Updates Pattern
33+
34+
Optimistic updates allow users to see changes immediately while the API request is queued. This is fundamental to Expensify's offline-first architecture.
35+
36+
For **which pattern to use** (A / B / C / D) and UX behavior for each, see https://github.com/Expensify/App/blob/main/contributingGuides/philosophies/OFFLINE.md.
37+
38+
#### Understanding the Three Data Sets
39+
40+
**CRITICAL:** Backend response data is automatically applied via Pusher updates or HTTPS responses. You do NOT manually set backend data in `successData`/`failureData` — only UI state cleanup goes there.
41+
42+
1. **optimisticData** (Applied immediately, before the API call)
43+
- Mirrors what the backend would return on success
44+
- Gives the user instant feedback without waiting for the server
45+
- Often includes `pendingAction` to flag the change as in-flight (e.g. greying out a comment while offline)
46+
- `pendingAction` is cleared once `successData` or `failureData` is applied
47+
48+
2. **successData** (Applied when API succeeds)
49+
- Used for UI state cleanup: clearing `pendingAction`, setting `isLoading: false`
50+
- For `add` actions: often not needed (optimisticData already set the right state)
51+
- For `update`/`delete` actions: include to clear pending state
52+
53+
3. **failureData** (Applied when API fails)
54+
- Reverts optimisticData changes
55+
- Clears `pendingAction`.
56+
- Adds `errors` field for the user to see
57+
- Always include this to handle unexpected failures
58+
59+
For code examples of each pattern (A/B, loading state, `finallyData`), see [offline-patterns.md](offline-patterns.md).
60+
61+
## Performance Optimization
62+
63+
### 1. Subscribe to Specific Collection Members
64+
65+
```typescript
66+
// BAD: re-renders on any report change
67+
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
68+
const myReport = allReports[`report_${reportID}`];
69+
70+
// GOOD: re-renders only when this report changes
71+
const [myReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
72+
```
73+
74+
### 2. Use Selectors to Narrow Re-renders
75+
76+
```typescript
77+
const accountIDSelector = (account: Account) => account?.accountID;
78+
const [accountID] = useOnyx(ONYXKEYS.ACCOUNT, {selector: accountIDSelector});
79+
```
80+
81+
`useOnyx` caches by selector reference — a new function reference on every render bypasses the cache and causes unnecessary re-renders. Prefer pure selectors defined in `src/selectors/` over inline functions. If a selector must be defined inside a component, ensure referential stability: React Compiler handles this automatically, but in components that are not compiled, wrap the selector in `useMemo`.
82+
83+
```typescript
84+
// BAD: new function reference on every render defeats caching
85+
const [accountID] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => account?.accountID});
86+
87+
// GOOD: stable reference defined outside the component
88+
// src/selectors/accountSelectors.ts
89+
const selectAccountID = (account: Account) => account?.accountID;
90+
91+
// GOOD: stable reference via useMemo (for non-React-Compiler components)
92+
const selector = useMemo(() => (account: Account) => account?.accountID, []);
93+
const [accountID] = useOnyx(ONYXKEYS.ACCOUNT, {selector});
94+
```
95+
96+
For `skipCacheCheck` (large objects) and batch collection update patterns, see https://github.com/Expensify/react-native-onyx/blob/main/README.md.
97+
98+
## Common Pitfalls
99+
100+
### Mixing set and merge on the Same Key
101+
102+
`Onyx.set()` calls are not batched with `Onyx.merge()` calls, which can produce race conditions:
103+
104+
```typescript
105+
// BAD: merge may execute before set resolves
106+
Onyx.set(ONYXKEYS.ACCOUNT, null);
107+
Onyx.merge(ONYXKEYS.ACCOUNT, {validated: true});
108+
109+
// GOOD: use one operation
110+
Onyx.set(ONYXKEYS.ACCOUNT, {validated: true});
111+
```
112+
113+
## Common Tasks Quick Reference
114+
115+
```typescript
116+
// Update a single field
117+
Onyx.merge(ONYXKEYS.NETWORK, {isOffline: true});
118+
119+
// Delete data
120+
Onyx.set(ONYXKEYS.ACCOUNT, null);
121+
122+
// Subscribe in component
123+
const [data] = useOnyx(ONYXKEYS.SOME_KEY);
124+
125+
// Subscribe with selector
126+
const [field] = useOnyx(ONYXKEYS.SOME_KEY, {selector: (data) => data?.specificField});
127+
128+
// Update collection member
129+
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {unread: false});
130+
131+
// Batch update collection
132+
Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, updates);
133+
134+
// API call with optimistic update
135+
API.write('SomeCommand', params, {optimisticData, successData, failureData});
136+
```
137+
138+
## Related Files
139+
140+
- https://github.com/Expensify/react-native-onyx/blob/main/README.md - Full Onyx API reference (initialization, merge/set/connect, collections, loading state, cache eviction, Redux DevTools, benchmarks)
141+
- https://github.com/Expensify/App/blob/main/contributingGuides/philosophies/OFFLINE.md - Full offline UX patterns, decision flowchart, and when to use each pattern (A/B/C/D)
142+
- [offline-patterns.md](offline-patterns.md) - Code examples for each optimistic update pattern
143+
- https://github.com/Expensify/App/blob/main/src/ONYXKEYS.ts - All Onyx key definitions
144+
- https://github.com/Expensify/App/tree/main/src/libs/actions - Action files that update Onyx
145+
- https://github.com/Expensify/App/blob/main/src/hooks/useOnyx.ts - useOnyx hook implementation
146+
- https://github.com/Expensify/App/tree/main/src/types/onyx - TypeScript types for Onyx data

0 commit comments

Comments
 (0)