From daea02ebd7f9907bb3b6d31c9cd2c77c38fbc49b Mon Sep 17 00:00:00 2001
From: rory <rory@expensify.com>
Date: Mon, 25 May 2026 18:40:58 -0700
Subject: [PATCH 1/8] Add victory-chart-renderer Bun CLI scaffold

Introduce the server subproject with a hello-world CLI, Bun pin,
npm scripts, and a bun:test integration test for issue #91528 PR 2.

Co-authored-by: Cursor <cursoragent@cursor.com>
---
 .bun-version                                  |  1 +
 .gitignore                                    |  3 ++
 package-lock.json                             | 22 +++++++++++++++
 package.json                                  |  8 +++++-
 server/victory-chart-renderer/README.md       | 28 +++++++++++++++++++
 .../__tests__/cli.test.ts                     | 25 +++++++++++++++++
 server/victory-chart-renderer/src/cli.ts      | 12 ++++++++
 server/victory-chart-renderer/tsconfig.json   |  8 ++++++
 8 files changed, 106 insertions(+), 1 deletion(-)
 create mode 100644 .bun-version
 create mode 100644 server/victory-chart-renderer/README.md
 create mode 100644 server/victory-chart-renderer/__tests__/cli.test.ts
 create mode 100644 server/victory-chart-renderer/src/cli.ts
 create mode 100644 server/victory-chart-renderer/tsconfig.json

diff --git a/.bun-version b/.bun-version
new file mode 100644
index 000000000000..085c0f2666ac
--- /dev/null
+++ b/.bun-version
@@ -0,0 +1 @@
+1.3.14
diff --git a/.gitignore b/.gitignore
index 63c8c7c6ee4c..cd22a294834b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -172,3 +172,6 @@ agent-device-output/
 
 # cspell cache
 .cspellcache
+
+# Server CLI build artifacts
+server/*/dist/
diff --git a/package-lock.json b/package-lock.json
index 0d3e354d2d54..be312e269201 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -193,6 +193,7 @@
         "@testing-library/react-native": "13.2.0",
         "@trivago/prettier-plugin-sort-imports": "^6.0.2",
         "@types/base-64": "^1.0.2",
+        "@types/bun": "^1.3.14",
         "@types/canvas-size": "^1.2.2",
         "@types/concurrently": "^7.0.3",
         "@types/d3-scale": "^4.0.9",
@@ -299,6 +300,7 @@
         "workbox-webpack-plugin": "^7.4.0"
       },
       "engines": {
+        "bun": "1.3.14",
         "node": "20.20.0",
         "npm": "10.8.2"
       }
@@ -16731,6 +16733,16 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/bun": {
+      "version": "1.3.14",
+      "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.14.tgz",
+      "integrity": "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bun-types": "1.3.14"
+      }
+    },
     "node_modules/@types/canvas-size": {
       "version": "1.2.2",
       "dev": true,
@@ -19606,6 +19618,16 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/bun-types": {
+      "version": "1.3.14",
+      "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.14.tgz",
+      "integrity": "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/bundle-name": {
       "version": "4.1.0",
       "dev": true,
diff --git a/package.json b/package.json
index c30604e8c982..0041385d6966 100644
--- a/package.json
+++ b/package.json
@@ -71,7 +71,10 @@
     "generate-autocomplete-parser": "peggy --format es -o src/libs/SearchParser/autocompleteParser.js src/libs/SearchParser/autocompleteParser.peggy src/libs/SearchParser/baseRules.peggy && ./scripts/parser-workletization.sh src/libs/SearchParser/autocompleteParser.js",
     "web:dist": "concurrently \"npm:web-proxy\" \"http-server ./dist --cors --port 8080 -P http://localhost:9000\"",
     "octokit": "cd scripts && npx ts-node -i -e \"$(cat ./octokit.ts)\"",
-    "compress-svg": "npx ts-node scripts/compressSvg.ts --dir assets/images && npx ts-node scripts/compressSvg.ts --dir docs/assets/images"
+    "compress-svg": "npx ts-node scripts/compressSvg.ts --dir assets/images && npx ts-node scripts/compressSvg.ts --dir docs/assets/images",
+    "server:vcr:dev": "bun run server/victory-chart-renderer/src/cli.ts",
+    "server:vcr:build:linux": "bun build --compile --target=bun-linux-x64 server/victory-chart-renderer/src/cli.ts --outfile server/victory-chart-renderer/dist/victory-chart-renderer-linux-x64",
+    "server:vcr:build:macos": "bun build --compile --target=bun-darwin-arm64 server/victory-chart-renderer/src/cli.ts --outfile server/victory-chart-renderer/dist/victory-chart-renderer-darwin-arm64"
   },
   "dependencies": {
     "@babel/runtime": "^7.25.0",
@@ -257,6 +260,7 @@
     "@testing-library/react-native": "13.2.0",
     "@trivago/prettier-plugin-sort-imports": "^6.0.2",
     "@types/base-64": "^1.0.2",
+    "@types/bun": "^1.3.14",
     "@types/canvas-size": "^1.2.2",
     "@types/concurrently": "^7.0.3",
     "@types/d3-scale": "^4.0.9",
@@ -386,6 +390,7 @@
     "react": "19.2.0",
     "react-dom": "19.2.0",
     "eslint-config-airbnb-typescript": {
+      "@types/bun": "^1.3.14",
       "@typescript-eslint/eslint-plugin": "^8.45.0",
       "@typescript-eslint/parser": "^8.45.0",
       "eslint": "^9.36.0",
@@ -429,6 +434,7 @@
     }
   },
   "engines": {
+    "bun": "1.3.14",
     "node": "20.20.0",
     "npm": "10.8.2"
   }
diff --git a/server/victory-chart-renderer/README.md b/server/victory-chart-renderer/README.md
new file mode 100644
index 000000000000..5dec5314f9ca
--- /dev/null
+++ b/server/victory-chart-renderer/README.md
@@ -0,0 +1,28 @@
+# Victory Chart Renderer
+
+Standalone Bun CLI that will render Expensify chart XML to PNG using the same chart code as the App.
+
+This directory is introduced in PR 2 of the [Victory Chart Renderer rollout](https://github.com/Expensify/App/issues/91528) as a scaffold only: no Skia, no `victory-native`, and no chart rendering yet.
+
+## Development
+
+From the App repository root:
+
+```bash
+npm run server:vcr:dev /tmp/out.txt
+```
+
+## Tests
+
+```bash
+cd server/victory-chart-renderer && bun test
+```
+
+## Compiled binaries
+
+```bash
+npm run server:vcr:build:linux
+npm run server:vcr:build:macos
+```
+
+Binaries are written to `server/victory-chart-renderer/dist/` (gitignored).
diff --git a/server/victory-chart-renderer/__tests__/cli.test.ts b/server/victory-chart-renderer/__tests__/cli.test.ts
new file mode 100644
index 000000000000..af8ade0c59ca
--- /dev/null
+++ b/server/victory-chart-renderer/__tests__/cli.test.ts
@@ -0,0 +1,25 @@
+import {expect, test} from 'bun:test';
+import {spawnSync} from 'node:child_process';
+import {mkdtempSync, readFileSync, rmSync} from 'node:fs';
+import {tmpdir} from 'node:os';
+import {join} from 'node:path';
+
+const projectRoot = join(import.meta.dir, '../../..');
+const cliPath = join(projectRoot, 'server/victory-chart-renderer/src/cli.ts');
+
+test('CLI writes the scaffold message to outPath', () => {
+    const tempDir = mkdtempSync(join(tmpdir(), 'vcr-cli-test-'));
+    const outPath = join(tempDir, 'out.txt');
+
+    try {
+        const result = spawnSync('bun', ['run', cliPath, outPath], {
+            cwd: projectRoot,
+            encoding: 'utf8',
+        });
+
+        expect(result.status).toBe(0);
+        expect(readFileSync(outPath, 'utf8')).toBe('victory-chart-renderer scaffolded\n');
+    } finally {
+        rmSync(tempDir, {recursive: true, force: true});
+    }
+});
diff --git a/server/victory-chart-renderer/src/cli.ts b/server/victory-chart-renderer/src/cli.ts
new file mode 100644
index 000000000000..59adda34eb26
--- /dev/null
+++ b/server/victory-chart-renderer/src/cli.ts
@@ -0,0 +1,12 @@
+import CLI from '@scripts/utils/CLI';
+
+const cli = new CLI({
+    positionalArgs: [
+        {
+            name: 'outPath',
+            description: 'Output file path for the scaffold message',
+        },
+    ],
+});
+
+await Bun.write(cli.positionalArgs.outPath, 'victory-chart-renderer scaffolded\n');
diff --git a/server/victory-chart-renderer/tsconfig.json b/server/victory-chart-renderer/tsconfig.json
new file mode 100644
index 000000000000..e4c635a3a0a7
--- /dev/null
+++ b/server/victory-chart-renderer/tsconfig.json
@@ -0,0 +1,8 @@
+{
+    "extends": "../../tsconfig.json",
+    "compilerOptions": {
+        "types": ["@types/bun"],
+        "rootDir": "./src"
+    },
+    "include": ["src", "__tests__"]
+}

From 745c7abcefb477b144bcf3c43c07b1c3eab5dede Mon Sep 17 00:00:00 2001
From: rory <rory@expensify.com>
Date: Mon, 25 May 2026 18:41:01 -0700
Subject: [PATCH 2/8] Configure ESLint and TypeScript for server CLI

Point server sources at the subproject tsconfig and relax App-only
lint rules so the Bun CLI can use node/bun imports cleanly.

Co-authored-by: Cursor <cursoragent@cursor.com>
---
 config/eslint/eslint.config.mjs | 19 +++++++++++++++++--
 tsconfig.json                   |  1 +
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/config/eslint/eslint.config.mjs b/config/eslint/eslint.config.mjs
index 25500517ee64..d1418c4069b7 100644
--- a/config/eslint/eslint.config.mjs
+++ b/config/eslint/eslint.config.mjs
@@ -631,7 +631,7 @@ const config = defineConfig([
     },
 
     {
-        files: ['.github/**/*', 'scripts/**/*'],
+        files: ['.github/**/*', 'scripts/**/*', 'server/**/*'],
         rules: {
             // For all these Node.js scripts, we do not want to disable `console` statements
             'no-console': 'off',
@@ -639,7 +639,7 @@ const config = defineConfig([
     },
 
     {
-        files: ['.github/**/*', 'scripts/**/*', 'tests/**/*'],
+        files: ['.github/**/*', 'scripts/**/*', 'server/**/*', 'tests/**/*'],
         rules: {
             'no-await-in-loop': 'off',
             'no-restricted-syntax': ['error', 'ForInStatement', 'LabeledStatement', 'WithStatement'],
@@ -735,6 +735,20 @@ const config = defineConfig([
         },
     },
 
+    {
+        files: ['server/**/*.ts'],
+        languageOptions: {
+            parserOptions: {
+                project: path.resolve(projectRoot, 'server/victory-chart-renderer/tsconfig.json'),
+            },
+        },
+        rules: {
+            'no-restricted-imports': 'off',
+            'rulesdir/no-default-props': 'off',
+            'rulesdir/prefer-type-fest': 'off',
+        },
+    },
+
     globalIgnores([
         '!**/.storybook',
         '!**/.github',
@@ -743,6 +757,7 @@ const config = defineConfig([
         '**/*.config.mjs',
         '**/node_modules/**/*',
         '**/dist/**/*',
+        'server/**/dist/**',
         '.eslint-reports/**/*',
         'android/**/build/**/*',
         'docs/vendor/**/*',
diff --git a/tsconfig.json b/tsconfig.json
index e3e7c61ff2d3..d7ad1dfe4332 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -46,6 +46,7 @@
         ".storybook/**/*",
         ".claude/**/*",
         "scripts",
+        "server",
         "modules",
         "**/*.nitro/*.ts",
         "**/*.nitro/*.tsx"

From 10396ca135586caf3aa1fb53a32cc364910c46bf Mon Sep 17 00:00:00 2001
From: rory <rory@expensify.com>
Date: Tue, 26 May 2026 08:20:40 -0700
Subject: [PATCH 3/8] Fix typecheck for victory-chart-renderer Bun CLI

Typecheck server tools with a dedicated tsconfig that enables @types/bun
instead of pulling them into the root React Native tsconfig include.

Co-authored-by: Cursor <cursoragent@cursor.com>
---
 .gitignore    | 1 +
 package.json  | 4 ++--
 tsconfig.json | 1 -
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.gitignore b/.gitignore
index cd22a294834b..9b179a0dc0d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,6 +114,7 @@ tests/e2e/results/
 # Typescript
 tsconfig.tsbuildinfo
 tsconfig.tsgo.tsbuildinfo
+**/tsconfig.tsgo.tsbuildinfo
 
 # Mock-github
 /repo/
diff --git a/package.json b/package.json
index 0041385d6966..cbd676d50507 100644
--- a/package.json
+++ b/package.json
@@ -43,8 +43,8 @@
     "test:verbose": "TZ=utc NODE_OPTIONS=\"--experimental-vm-modules --max_old_space_size=4096\" JEST_VERBOSE=true jest",
     "test:debug": "TZ=utc NODE_OPTIONS='--inspect-brk --experimental-vm-modules' jest --runInBand",
     "perf-test": "NODE_OPTIONS=--experimental-vm-modules npx reassure",
-    "typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc",
-    "typecheck-tsgo": "tsgo --noEmit --incremental --tsBuildInfoFile tsconfig.tsgo.tsbuildinfo",
+    "typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc && tsc -p server/victory-chart-renderer/tsconfig.json",
+    "typecheck-tsgo": "tsgo --noEmit --incremental --tsBuildInfoFile tsconfig.tsgo.tsbuildinfo && tsgo --noEmit -p server/victory-chart-renderer/tsconfig.json --incremental --tsBuildInfoFile server/victory-chart-renderer/tsconfig.tsgo.tsbuildinfo",
     "lint": "./scripts/lint.sh",
     "lint-changed": "./scripts/lintChanged.sh",
     "lint-watch": "onchange '**/*.{js,jsx,ts,tsx,mjs,cjs}' -- ./scripts/lint.sh {{changed}}",
diff --git a/tsconfig.json b/tsconfig.json
index d7ad1dfe4332..e3e7c61ff2d3 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -46,7 +46,6 @@
         ".storybook/**/*",
         ".claude/**/*",
         "scripts",
-        "server",
         "modules",
         "**/*.nitro/*.ts",
         "**/*.nitro/*.tsx"

From bcf7c270073dc2133d3bf3abca97b2b99b47661a Mon Sep 17 00:00:00 2001
From: rory <rory@expensify.com>
Date: Tue, 26 May 2026 08:21:12 -0700
Subject: [PATCH 4/8] Move server Bun tsconfig to server/tsconfig.json

Share one server-side TypeScript project across victory-chart-renderer
and future sibling CLI tools under server/.

Co-authored-by: Cursor <cursoragent@cursor.com>
---
 config/eslint/eslint.config.mjs             | 2 +-
 package.json                                | 4 ++--
 server/tsconfig.json                        | 7 +++++++
 server/victory-chart-renderer/tsconfig.json | 8 --------
 4 files changed, 10 insertions(+), 11 deletions(-)
 create mode 100644 server/tsconfig.json
 delete mode 100644 server/victory-chart-renderer/tsconfig.json

diff --git a/config/eslint/eslint.config.mjs b/config/eslint/eslint.config.mjs
index d1418c4069b7..329caf5f6170 100644
--- a/config/eslint/eslint.config.mjs
+++ b/config/eslint/eslint.config.mjs
@@ -739,7 +739,7 @@ const config = defineConfig([
         files: ['server/**/*.ts'],
         languageOptions: {
             parserOptions: {
-                project: path.resolve(projectRoot, 'server/victory-chart-renderer/tsconfig.json'),
+                project: path.resolve(projectRoot, 'server/tsconfig.json'),
             },
         },
         rules: {
diff --git a/package.json b/package.json
index cbd676d50507..20af0ba76808 100644
--- a/package.json
+++ b/package.json
@@ -43,8 +43,8 @@
     "test:verbose": "TZ=utc NODE_OPTIONS=\"--experimental-vm-modules --max_old_space_size=4096\" JEST_VERBOSE=true jest",
     "test:debug": "TZ=utc NODE_OPTIONS='--inspect-brk --experimental-vm-modules' jest --runInBand",
     "perf-test": "NODE_OPTIONS=--experimental-vm-modules npx reassure",
-    "typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc && tsc -p server/victory-chart-renderer/tsconfig.json",
-    "typecheck-tsgo": "tsgo --noEmit --incremental --tsBuildInfoFile tsconfig.tsgo.tsbuildinfo && tsgo --noEmit -p server/victory-chart-renderer/tsconfig.json --incremental --tsBuildInfoFile server/victory-chart-renderer/tsconfig.tsgo.tsbuildinfo",
+    "typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc && tsc -p server/tsconfig.json",
+    "typecheck-tsgo": "tsgo --noEmit --incremental --tsBuildInfoFile tsconfig.tsgo.tsbuildinfo && tsgo --noEmit -p server/tsconfig.json --incremental --tsBuildInfoFile server/tsconfig.tsgo.tsbuildinfo",
     "lint": "./scripts/lint.sh",
     "lint-changed": "./scripts/lintChanged.sh",
     "lint-watch": "onchange '**/*.{js,jsx,ts,tsx,mjs,cjs}' -- ./scripts/lint.sh {{changed}}",
diff --git a/server/tsconfig.json b/server/tsconfig.json
new file mode 100644
index 000000000000..0c0135bb370d
--- /dev/null
+++ b/server/tsconfig.json
@@ -0,0 +1,7 @@
+{
+    "extends": "../tsconfig.json",
+    "compilerOptions": {
+        "types": ["@types/bun"]
+    },
+    "include": ["victory-chart-renderer/src", "victory-chart-renderer/__tests__"]
+}
diff --git a/server/victory-chart-renderer/tsconfig.json b/server/victory-chart-renderer/tsconfig.json
deleted file mode 100644
index e4c635a3a0a7..000000000000
--- a/server/victory-chart-renderer/tsconfig.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-    "extends": "../../tsconfig.json",
-    "compilerOptions": {
-        "types": ["@types/bun"],
-        "rootDir": "./src"
-    },
-    "include": ["src", "__tests__"]
-}

From f11c7d9813a61c442b206bdf21667045e2016454 Mon Sep 17 00:00:00 2001
From: rory <rory@expensify.com>
Date: Tue, 26 May 2026 14:27:35 -0700
Subject: [PATCH 5/8] Add target for linux-arm

---
 package.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/package.json b/package.json
index 54374a339092..51db52e6f7a2 100644
--- a/package.json
+++ b/package.json
@@ -74,6 +74,7 @@
     "compress-svg": "npx ts-node scripts/compressSvg.ts --dir assets/images && npx ts-node scripts/compressSvg.ts --dir docs/assets/images",
     "server:vcr:dev": "bun run server/victory-chart-renderer/src/cli.ts",
     "server:vcr:build:linux": "bun build --compile --target=bun-linux-x64 server/victory-chart-renderer/src/cli.ts --outfile server/victory-chart-renderer/dist/victory-chart-renderer-linux-x64",
+    "server:vcr:build:linux-arm": "bun build --compile --target=bun-linux-arm64 server/victory-chart-renderer/src/cli.ts --outfile server/victory-chart-renderer/dist/victory-chart-renderer-linux-arm64",
     "server:vcr:build:macos": "bun build --compile --target=bun-darwin-arm64 server/victory-chart-renderer/src/cli.ts --outfile server/victory-chart-renderer/dist/victory-chart-renderer-darwin-arm64"
   },
   "dependencies": {

From 3f732ee61284bf36dcaf521426ee25fd50f75f85 Mon Sep 17 00:00:00 2001
From: rory <rory@expensify.com>
Date: Tue, 26 May 2026 14:33:06 -0700
Subject: [PATCH 6/8] Remove unnecessary lint config changes

---
 config/eslint/eslint.config.mjs | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/config/eslint/eslint.config.mjs b/config/eslint/eslint.config.mjs
index 329caf5f6170..3fcb2ccdfa32 100644
--- a/config/eslint/eslint.config.mjs
+++ b/config/eslint/eslint.config.mjs
@@ -742,11 +742,6 @@ const config = defineConfig([
                 project: path.resolve(projectRoot, 'server/tsconfig.json'),
             },
         },
-        rules: {
-            'no-restricted-imports': 'off',
-            'rulesdir/no-default-props': 'off',
-            'rulesdir/prefer-type-fest': 'off',
-        },
     },
 
     globalIgnores([

From 7cf499852d843b97103e8b3cc9a8a2ebfdd11e3e Mon Sep 17 00:00:00 2001
From: Rory Abraham <47436092+roryabraham@users.noreply.github.com>
Date: Tue, 26 May 2026 16:02:44 -0700
Subject: [PATCH 7/8] Update server/victory-chart-renderer/README.md

Co-authored-by: Carlos Martins <cmartins@expensify.com>
---
 server/victory-chart-renderer/README.md | 1 -
 1 file changed, 1 deletion(-)

diff --git a/server/victory-chart-renderer/README.md b/server/victory-chart-renderer/README.md
index 5dec5314f9ca..f14512005e1b 100644
--- a/server/victory-chart-renderer/README.md
+++ b/server/victory-chart-renderer/README.md
@@ -2,7 +2,6 @@
 
 Standalone Bun CLI that will render Expensify chart XML to PNG using the same chart code as the App.
 
-This directory is introduced in PR 2 of the [Victory Chart Renderer rollout](https://github.com/Expensify/App/issues/91528) as a scaffold only: no Skia, no `victory-native`, and no chart rendering yet.
 
 ## Development
 

From 51e9b3992675cbafb0c9f17df2c757e19d3ffdac Mon Sep 17 00:00:00 2001
From: rory <rory@expensify.com>
Date: Tue, 26 May 2026 16:09:53 -0700
Subject: [PATCH 8/8] Rename victory-chart-renderer tests directory to tests

Use tests/ instead of __tests__, add server:vcr:test npm script, and
update README and server tsconfig to match.

Co-authored-by: Cursor <cursoragent@cursor.com>
---
 package.json                                      |  1 +
 server/tsconfig.json                              |  2 +-
 server/victory-chart-renderer/README.md           | 15 ++++++++++++++-
 .../{__tests__ => tests}/cli.test.ts              |  0
 4 files changed, 16 insertions(+), 2 deletions(-)
 rename server/victory-chart-renderer/{__tests__ => tests}/cli.test.ts (100%)

diff --git a/package.json b/package.json
index 51db52e6f7a2..e85eb21727b3 100644
--- a/package.json
+++ b/package.json
@@ -73,6 +73,7 @@
     "octokit": "cd scripts && npx ts-node -i -e \"$(cat ./octokit.ts)\"",
     "compress-svg": "npx ts-node scripts/compressSvg.ts --dir assets/images && npx ts-node scripts/compressSvg.ts --dir docs/assets/images",
     "server:vcr:dev": "bun run server/victory-chart-renderer/src/cli.ts",
+    "server:vcr:test": "bun test server/victory-chart-renderer/tests",
     "server:vcr:build:linux": "bun build --compile --target=bun-linux-x64 server/victory-chart-renderer/src/cli.ts --outfile server/victory-chart-renderer/dist/victory-chart-renderer-linux-x64",
     "server:vcr:build:linux-arm": "bun build --compile --target=bun-linux-arm64 server/victory-chart-renderer/src/cli.ts --outfile server/victory-chart-renderer/dist/victory-chart-renderer-linux-arm64",
     "server:vcr:build:macos": "bun build --compile --target=bun-darwin-arm64 server/victory-chart-renderer/src/cli.ts --outfile server/victory-chart-renderer/dist/victory-chart-renderer-darwin-arm64"
diff --git a/server/tsconfig.json b/server/tsconfig.json
index 0c0135bb370d..03533aa786e5 100644
--- a/server/tsconfig.json
+++ b/server/tsconfig.json
@@ -3,5 +3,5 @@
     "compilerOptions": {
         "types": ["@types/bun"]
     },
-    "include": ["victory-chart-renderer/src", "victory-chart-renderer/__tests__"]
+    "include": ["victory-chart-renderer/src", "victory-chart-renderer/tests"]
 }
diff --git a/server/victory-chart-renderer/README.md b/server/victory-chart-renderer/README.md
index f14512005e1b..8de24f6ddde6 100644
--- a/server/victory-chart-renderer/README.md
+++ b/server/victory-chart-renderer/README.md
@@ -2,6 +2,11 @@
 
 Standalone Bun CLI that will render Expensify chart XML to PNG using the same chart code as the App.
 
+## Project layout
+
+- `src/` — CLI source
+- `tests/` — Bun integration tests (`bun:test`)
+- `dist/` — compiled binaries (gitignored)
 
 ## Development
 
@@ -13,8 +18,16 @@ npm run server:vcr:dev /tmp/out.txt
 
 ## Tests
 
+From the App repository root:
+
+```bash
+npm run server:vcr:test
+```
+
+Or from this directory:
+
 ```bash
-cd server/victory-chart-renderer && bun test
+bun test
 ```
 
 ## Compiled binaries
diff --git a/server/victory-chart-renderer/__tests__/cli.test.ts b/server/victory-chart-renderer/tests/cli.test.ts
similarity index 100%
rename from server/victory-chart-renderer/__tests__/cli.test.ts
rename to server/victory-chart-renderer/tests/cli.test.ts
