# PR #23326 (GitCafe 23316) evidence capture: Cursor skill discovery scan budget

All fixtures are **SYNTHETIC**. They were generated by `driver/make-fixtures.mjs` under `/tmp/ev-23326/cap/fx/<row>/`, and each one carries a `SYNTHETIC-FIXTURE.txt` marker. Every `SKILL.md` is `---\nname: <dir>\ndescription: synthetic\n---\n`.

## Environment
- Machine fs-28, uid 1000 (non-root), 2026-10-22
- Node v24.22.1, kernel 6.1.102
- `df -T /tmp`: /dev/root ext4 mounted on /
- **after** = `~/wt/pr-23326` at b528fd6a841c85080490f6bbd47bdfd0dd721f8b
- **before** = a real detached worktree at `/tmp/ev-23326/cap/wt-before` on 8d3b061121fe65669d5d43d011b48cd3027a4108 (`git worktree add --detach`, then `pnpm install --frozen-lockfile`, 16 s). This is not the copy fallback. `packages/provider-cursor` at 8d3b equals the branch base defb7e6e6b except for a one-line `package.json` script change.
- Every shell ran `for v in $(env | grep -o '^T3CODE_[A-Z_]*'); do unset $v; done`. `~/.t3` was not touched.

## Driver sha256
```
df112230aad3514eac6bc31821a72f71a27777b46c8f89e5e30ba035680f0ac4  count-stats.mjs
c424361ed8b6e1776c0ffe34a8827f9bbf4ba2026c9c9bfbe9a330d42ef5410a  make-fixtures.mjs
7edb43e18e95443a1d02f5f2e9e1752400f51f258f7d2f76f74403ef09fc9c9c  probe.mjs
8d7e512f41fe868bd8b3eb5624f05f7919cf2acc9201972957ecbad14d350b38  rpc-refresh.mjs
```

## Commands
```
C=/tmp/ev-23326/cap
node $C/driver/make-fixtures.mjs $C/fx R1 R2-9995 R2-9996 R2-9997 R4 R5 R7
chmod 000 $C/fx/R7/.cursor/skills/a-large/locked
# A) package entry point (discoverCursorSkills + probeCursorSkills, environment={HOME:$C/fakehome}, NodeServices.layer as in skills.test.ts)
(cd <wt>/packages/provider-cursor && node $C/driver/probe.mjs <wt> $C/fx $C/fakehome R1 R2-9995 R2-9996 R2-9997 R4 R5 R7 R5)
chmod 755 $C/fx/R7/.cursor/skills/a-large/locked
# A') FileSystem.stat/readDirectory/realPath call counts (wraps NodeServices FileSystem)
node $C/driver/count-stats.mjs <wt> $C/fx/{R1,R4,R5} $C/fakehome
# B) real server: apps/server/src/bin.ts start --bootstrap-fd 3 (desktop bootstrap token, redacted), HOME=$C/fakehome,
#    isolated T3 home $C/t3home-<sha> with userdata/settings.json = {"providerInstances":{"cursor":{"driver":"cursor","enabled":true}}}
#    → /oauth/token → /api/auth/websocket-ticket → WS RpcClient(WsRpcGroup) server.refreshProviders {instanceId:"cursor",cwd,fresh:true}
node $C/driver/rpc-refresh.mjs ~/wt/pr-23326 $C/t3home-b528fd6a84 $C/fakehome $C/logs/server-after.log 47326 $C/fx/R1 $C/fx/R5 $C/fx/R4
node $C/driver/rpc-refresh.mjs $C/wt-before $C/t3home-8d3b061121 $C/fakehome $C/logs/server-before.log 47327 $C/fx/R1 $C/fx/R5
```
The server was killed by the PID captured at spawn (`server.kill` on the child handle). Afterwards `ss` showed nothing listening on either port.

## Results

| row | fixture | before 8d3b061121 | after b528fd6a84 | probe wallMs before / after |
|---|---|---|---|---|
| R1 | 11,000 files in a-large/artifacts | discover [a-large]; probe **scan-budget-exhausted** | discover [a-large, b-small]; probe **success** [a-large, b-small] | 993 / 1,042 |
| R2 | N=9,995 | success [a-large, b-small] | success [a-large, b-small] | 938 / 826 |
| R2 | N=9,996 | discover [a-large, b-small]; probe scan-budget-exhausted | success | 866 / 1,069 |
| R2 | N=9,997 | discover [a-large]; probe scan-budget-exhausted | success | 1,015 / 810 |
| R4 | 10,001 empty dirs in a-large/dirs | discover [a-large]; scan-budget-exhausted | discover [a-large]; **scan-budget-exhausted** (directory cap still bounds) | 10,115 / 8,699 |
| R5 | 100,500 files | discover [a-large]; scan-budget-exhausted | discover [a-large]; scan-budget-exhausted after **exactly 100,000 stats** | run 1: 999 / 6,504; run 2: 866 / 5,987 |
| R7 | chmod 000 dir inside a-large (uid 1000) | discover [a-large, b-small]; probe **filesystem-error** | same | 7 / 7 |
| R10 | real server, R1 | RPC Success, **no workspace snapshot**, logged `provider registry refresh failed; preserving cached providers` ← CursorSkillsProbeError(scan-budget-exhausted) | RPC Success, **workspaceSnapshots[R1].skills published** = [a-large, b-small, +5 Codex system user skills] | RPC 859 / 1,551 |
| R10 | real server, R5 | no snapshot, error logged | no snapshot, same error logged (failure path) | RPC 688 / 8,553 |
| R10 | real server, R4 | (not run) | no snapshot, same error logged | – / 13,814 |

Stat counts (count-stats.mjs):

| fixture | before stats/readDir/realPath, ms | after stats/readDir/realPath, ms |
|---|---|---|
| R1 | 10,003 / 3 / 4, 847 ms, exhausted | 11,009 / 4 / 12, 1,751 ms, success |
| R4 | 20,000 / 10,000 / 10,001, 9,792 ms, exhausted | 20,004 / 10,001 / 10,002, 14,791 ms, exhausted |
| R5 | 10,003 / 3 / 4, 1,201 ms, exhausted | **100,000** / 3 / 4, 8,244 ms, exhausted |

Note: the count-stats timings include the proxy-wrapper overhead and run separately from the timings in the probe table.

## Raw JSON
### entry-after.jsonl
```
{"row":"R1","sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","discoverSkills":["a-large","b-small"],"probe":{"success":["a-large","b-small"]},"wallMs":{"discover":1196,"probe":1042}}
{"row":"R2-9995","sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","discoverSkills":["a-large","b-small"],"probe":{"success":["a-large","b-small"]},"wallMs":{"discover":967,"probe":826}}
{"row":"R2-9996","sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","discoverSkills":["a-large","b-small"],"probe":{"success":["a-large","b-small"]},"wallMs":{"discover":1138,"probe":1069}}
{"row":"R2-9997","sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","discoverSkills":["a-large","b-small"],"probe":{"success":["a-large","b-small"]},"wallMs":{"discover":783,"probe":810}}
{"row":"R4","sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","discoverSkills":["a-large"],"probe":{"error":"scan-budget-exhausted","tag":"CursorSkillsProbeError"},"wallMs":{"discover":11885,"probe":8699}}
{"row":"R5","sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","discoverSkills":["a-large"],"probe":{"error":"scan-budget-exhausted","tag":"CursorSkillsProbeError"},"wallMs":{"discover":6681,"probe":6504}}
{"row":"R7","sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","discoverSkills":["a-large","b-small"],"probe":{"error":"filesystem-error","tag":"CursorSkillsProbeError"},"wallMs":{"discover":7,"probe":7}}
{"row":"R5","sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","discoverSkills":["a-large"],"probe":{"error":"scan-budget-exhausted","tag":"CursorSkillsProbeError"},"wallMs":{"discover":6522,"probe":5987}}
```
### entry-before.jsonl
```
{"row":"R1","sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","discoverSkills":["a-large"],"probe":{"error":"scan-budget-exhausted","tag":"CursorSkillsProbeError"},"wallMs":{"discover":911,"probe":993}}
{"row":"R2-9995","sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","discoverSkills":["a-large","b-small"],"probe":{"success":["a-large","b-small"]},"wallMs":{"discover":907,"probe":938}}
{"row":"R2-9996","sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","discoverSkills":["a-large","b-small"],"probe":{"error":"scan-budget-exhausted","tag":"CursorSkillsProbeError"},"wallMs":{"discover":983,"probe":866}}
{"row":"R2-9997","sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","discoverSkills":["a-large"],"probe":{"error":"scan-budget-exhausted","tag":"CursorSkillsProbeError"},"wallMs":{"discover":518,"probe":1015}}
{"row":"R4","sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","discoverSkills":["a-large"],"probe":{"error":"scan-budget-exhausted","tag":"CursorSkillsProbeError"},"wallMs":{"discover":12663,"probe":10115}}
{"row":"R5","sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","discoverSkills":["a-large"],"probe":{"error":"scan-budget-exhausted","tag":"CursorSkillsProbeError"},"wallMs":{"discover":1091,"probe":999}}
{"row":"R7","sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","discoverSkills":["a-large","b-small"],"probe":{"error":"filesystem-error","tag":"CursorSkillsProbeError"},"wallMs":{"discover":9,"probe":7}}
{"row":"R5","sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","discoverSkills":["a-large"],"probe":{"error":"scan-budget-exhausted","tag":"CursorSkillsProbeError"},"wallMs":{"discover":999,"probe":866}}
```
### count-stats.jsonl
```
{"sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","cwd":"/tmp/ev-23326/cap/fx/R1","probe":{"success":["a-large","b-small"]},"counts":{"stat":11009,"readDirectory":4,"realPath":12},"wallMs":1751}
{"sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","cwd":"/tmp/ev-23326/cap/fx/R4","probe":{"error":"scan-budget-exhausted"},"counts":{"stat":20004,"readDirectory":10001,"realPath":10002},"wallMs":14791}
{"sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","cwd":"/tmp/ev-23326/cap/fx/R5","probe":{"error":"scan-budget-exhausted"},"counts":{"stat":100000,"readDirectory":3,"realPath":4},"wallMs":8244}
{"sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","cwd":"/tmp/ev-23326/cap/fx/R1","probe":{"error":"scan-budget-exhausted"},"counts":{"stat":10003,"readDirectory":3,"realPath":4},"wallMs":847}
{"sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","cwd":"/tmp/ev-23326/cap/fx/R4","probe":{"error":"scan-budget-exhausted"},"counts":{"stat":20000,"readDirectory":10000,"realPath":10001},"wallMs":9792}
{"sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","cwd":"/tmp/ev-23326/cap/fx/R5","probe":{"error":"scan-budget-exhausted"},"counts":{"stat":10003,"readDirectory":3,"realPath":4},"wallMs":1201}
```
### rpc-after.jsonl
```
{"event":"server-spawned","pid":795443,"sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b"}
{"sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","cwd":"/tmp/ev-23326/cap/fx/R1","rpc":"Success","wallMs":1551,"cursor":{"enabled":true,"status":"error","installed":true},"workspaceSnapshotPublished":true,"workspaceSkills":["a-large","b-small","imagegen","openai-docs","review-agent","skill-creator","skill-installer"],"workspaceSnapshotCwds":["/tmp/ev-23326/cap/fx/R1"]}
{"sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","cwd":"/tmp/ev-23326/cap/fx/R5","rpc":"Success","wallMs":8553,"cursor":{"enabled":true,"status":"error","installed":true},"workspaceSnapshotPublished":false,"workspaceSkills":null,"workspaceSnapshotCwds":["/tmp/ev-23326/cap/fx/R1"]}
{"sha":"b528fd6a841c85080490f6bbd47bdfd0dd721f8b","cwd":"/tmp/ev-23326/cap/fx/R4","rpc":"Success","wallMs":13814,"cursor":{"enabled":true,"status":"error","installed":true},"workspaceSnapshotPublished":false,"workspaceSkills":null,"workspaceSnapshotCwds":["/tmp/ev-23326/cap/fx/R1"]}
{"event":"server-killed","pid":795443}
```
### rpc-before.jsonl
```
{"event":"server-spawned","pid":796783,"sha":"8d3b061121fe65669d5d43d011b48cd3027a4108"}
{"sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","cwd":"/tmp/ev-23326/cap/fx/R1","rpc":"Success","wallMs":859,"cursor":{"enabled":true,"status":"error","installed":true},"workspaceSnapshotPublished":false,"workspaceSkills":null,"workspaceSnapshotCwds":[]}
{"sha":"8d3b061121fe65669d5d43d011b48cd3027a4108","cwd":"/tmp/ev-23326/cap/fx/R5","rpc":"Success","wallMs":688,"cursor":{"enabled":true,"status":"error","installed":true},"workspaceSnapshotPublished":false,"workspaceSkills":null,"workspaceSnapshotCwds":[]}
{"event":"server-killed","pid":796783}
```
### server-after.log excerpt (R5 failure)
```
33:[12:25:30.711] ERROR (#6886): provider registry refresh failed; preserving cached providers
34-  {
35-    cause: "ProviderDriverError: Provider driver 'cursor' failed to create instance 'cursor': Failed to discover Cursor skills for '/tmp/ev-23326/cap/fx/R5'\n" +
36-      '    at file:///home/ubuntu/wt/pr-23326/packages/provider-cursor/src/server/driver.ts:302:21\n' +
37-      '    at file:///home/ubuntu/wt/pr-23326/node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/internal/effect.js:1483:96\n' +
38-      '    at refreshWorkspaceSnapshot (file:///home/ubuntu/wt/pr-23326/apps/server/src/provider/ProviderRegistry.ts:1018:9)\n' +
39-      '    at refreshWorkspaceSnapshot (definition) (file:///home/ubuntu/wt/pr-23326/apps/server/src/provider/ProviderRegistry.ts:916:45)\n' +
40-      '    at ws.rpc.server.refreshProviders (file:///home/ubuntu/wt/pr-23326/apps/server/src/observability/RpcInstrumentation.ts:247:12)\n' +
41-      '    at server.runServerCommand (file:///home/ubuntu/wt/pr-23326/apps/server/src/cli/server.ts:73:34)\n' +
42-      '    at server.runServerCommand (definition) (file:///home/ubuntu/wt/pr-23326/apps/server/src/cli/server.ts:23:33) {\n' +
43-      "  [cause]: CursorSkillsProbeError: Cursor skill discovery for '/tmp/ev-23326/cap/fx/R5' was incomplete (scan-budget-exhausted).\n" +
44-      '      at file:///home/ubuntu/wt/pr-23326/packages/provider-cursor/src/server/skills.ts:291:19\n' +
45-      '}'
46-  }
47-[12:25:36.412] DEBUG (#6773): agent activity snapshot skipped; relay link credentials unavailable
```
### server-before.log excerpt (R1 failure)
```
301:[12:26:14.236] ERROR (#7177): provider registry refresh failed; preserving cached providers
302-  {
303-    cause: "ProviderDriverError: Provider driver 'cursor' failed to create instance 'cursor': Failed to discover Cursor skills for '/tmp/ev-23326/cap/fx/R1'\n" +
304-      '    at file:///tmp/ev-23326/cap/wt-before/packages/provider-cursor/src/server/driver.ts:302:21\n' +
305-      '    at file:///tmp/ev-23326/cap/wt-before/node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/internal/effect.js:1483:96\n' +
306-      '    at refreshWorkspaceSnapshot (file:///tmp/ev-23326/cap/wt-before/apps/server/src/provider/ProviderRegistry.ts:1018:9)\n' +
307-      '    at refreshWorkspaceSnapshot (definition) (file:///tmp/ev-23326/cap/wt-before/apps/server/src/provider/ProviderRegistry.ts:916:45)\n' +
308-      '    at ws.rpc.server.refreshProviders (file:///tmp/ev-23326/cap/wt-before/apps/server/src/observability/RpcInstrumentation.ts:247:12)\n' +
309-      '    at server.runServerCommand (file:///tmp/ev-23326/cap/wt-before/apps/server/src/cli/server.ts:73:34)\n' +
310-      '    at server.runServerCommand (definition) (file:///tmp/ev-23326/cap/wt-before/apps/server/src/cli/server.ts:23:33) {\n' +
311-      "  [cause]: CursorSkillsProbeError: Cursor skill discovery for '/tmp/ev-23326/cap/fx/R1' was incomplete (scan-budget-exhausted).\n" +
312-      '      at file:///tmp/ev-23326/cap/wt-before/packages/provider-cursor/src/server/skills.ts:275:19\n' +
313-      '}'
314-  }
315-[12:26:14.267] DEBUG (#6999): agent activity snapshot skipped; relay link credentials unavailable
```

## Caveats
- **Fake HOME was not empty during the server runs.** The server's own Codex health probe ran with HOME=fakehome and installed Codex's bundled `~/.codex/skills/.system/*` (imagegen, openai-docs, review-agent, skill-creator, skill-installer). Cursor scans `~/.codex/skills` as a user root, so those 5 appear in the after R1 snapshot. They cost a few dozen stats, which does not change any outcome. The entry-point runs (A) happened before any server start, so the fake HOME was empty for them.
- Cursor CLI reports `installed: true, status: "error"` (presumably unauthenticated). The filesystem scan is independent of that, and snapshot publication happened anyway, as the registry comment says.
- A fresh T3 home has the Cursor instance disabled. Without the `providerInstances.cursor.enabled=true` settings.json, `snapshotForCwd` returns the machine snapshot without scanning (observed in a first run, not kept).
- Timings are single runs (R5 twice) on a shared machine, with a warm page cache after fixture creation. Treat them as order-of-magnitude. R5 after costs about 6 s for the probe vs about 1 s before. Via RPC it is 8.5 s vs 0.7 s. R4 costs about 9–14 s on **both** sides (10k readDirectory+realPath), so it is not a regression.
- R10 R4 was not run before. The before R1 run shows the failure path already.
