Skip to content

Workspace index opts into filesystem-root and home-dir scanning, which the library documents as off by default #6

Description

@Francois3d

Problem

WorkspaceSearchIndex.createFinder (apps/server/src/workspace/WorkspaceSearchIndex.ts) passes both:

enableFsRootScanning: true,
enableHomeDirScanning: true,

The library documents both as off by default, for watcher-cost reasons. From @ff-labs/fff-node InitOptions:

Allow indexing the filesystem root (/). Off by default — root is rarely the intended target and floods the watcher with churn-prone events. Setting this true is opt-in and the caller is responsible for the resulting fs-event volume.

enableHomeDirScanning carries the same trade-off by reference.

These only bite when a workspace root actually is / or $HOME, so most projects are unaffected. But T3 lets a user add any directory as a project, and $HOME is a plausible thing for someone to point at. When they do, we opt into unbounded watcher churn on their behalf, silently.

Questions

  1. Was enabling these deliberate, and if so what was the case for it? If a user genuinely needs to index $HOME, that argues for keeping them — but knowingly.
  2. If deliberate, should it be bounded — a warning when a project root resolves to / or $HOME, or a refusal with an explanation, rather than a silent opt-in?
  3. Does anything today actually depend on being able to add / or $HOME as a project root?

Notes

Surfaced while scoping #2. That ticket explicitly put this out of scope and noted it belongs in its own ticket — this is it. Not urgent, and not blocking anything.

Activity

  1. added
    questionFurther information is requested
    needs-triageMaintainer needs to evaluate this issue
    on Aug 29, 2026
  2. Francois3d commented on Aug 29, 2026

    @Francois3d
    OwnerAuthor

    This was generated by AI during triage.

    Agent Brief

    Category: bug
    Summary: Stop silently opting every workspace index into filesystem-root and home-directory scanning; give a project root of / or $HOME a refusal the user can act on

    Scope note: this brief covers only the fork-local, answerable half of the ticket. The question of why upstream enabled these flags is out of scope here — see "Out of scope" below.

    Current behavior:
    Every workspace search index is created through a single FileFinder.create call (@ff-labs/fff-node) that passes enableFsRootScanning: true and enableHomeDirScanning: true. The library documents both as off by default because they flood the watcher with churn-prone events, and states that a caller setting them true owns the resulting fs-event volume.

    The flags only take effect when a workspace root actually resolves to / or the home directory, so most projects are unaffected. But a project root of ~ expands to the home directory in the workspace path resolvers, so a user can reach this by typing a single character. When they do, they get the unbounded watcher cost with no warning and no way to know they opted in.

    Both flags came from upstream (pingdotgg/t3code PR pingdotgg#3099, carried through a refactor in pingdotgg#3352). They are not a fork decision.

    Desired behavior:
    The workspace index no longer enables either flag. A project root that resolves to / or the user's home directory fails to index with a distinct, readable error naming the path and explaining that indexing the filesystem root or the home directory is not supported, rather than a generic create failure. Every other project root behaves exactly as it does today.

    First task — verify the library's actual behavior.
    Install dependencies and confirm empirically what FileFinder.create does with both flags absent and basePath set to / and to the home directory. There are two possible outcomes and the rest of the work depends on which one holds:

    • It refuses (returns a non-ok result): the library is already doing the gating. The work is to detect that case and surface a clear message instead of the generic failure.
    • It scans a degraded subset without refusing: the library will not gate for us, so add an explicit guard that rejects these roots before the finder is constructed.

    Record which one you observed in a comment on this issue before implementing, and note the exact library version you tested against.

    Key interfaces:

    • The InitOptions passed to FileFinder.create: enableFsRootScanning and enableHomeDirScanning should both be gone. Leave the other options (base path, mmap cache, content indexing per index variant, AI mode) untouched.
    • The workspace search index's tagged create-failure error already carries the workspace path and a reason string. Either extend it with a distinguishable reason for this case, or add a sibling tagged error alongside it. Follow whichever fits the existing error-modelling convention for these services — do not introduce a thrown exception or a bare string.
    • Path resolution: the helper that expands a leading ~ in a workspace path is what makes $HOME reachable. The check must run on the resolved, absolute path, not the user's raw input, so ~, $HOME, an explicit absolute home path, and a trailing-slash variant all land in the same branch.

    Acceptance criteria:

    • Neither enableFsRootScanning nor enableHomeDirScanning appears anywhere in the workspace index source.
    • A workspace root that resolves to the home directory fails index creation with an error that names the path and states that home-directory indexing is unsupported.
    • A workspace root of / fails the same way, with wording appropriate to the filesystem root.
    • The check operates on the resolved absolute path: ~, an explicit absolute home path, and a trailing-slash variant of either all produce the same refusal.
    • A normal project root (a repo directory) creates its index and searches exactly as before — no behavior change, no new failure path reached.
    • A focused test covers the two refusal cases and at least one ordinary root that still succeeds. It must not depend on sleeps or polling; wait on the service's own result.
    • A comment on this issue records what FileFinder.create actually did with the flags off, and the library version tested.

    Out of scope:

    • Answering why upstream enabled these flags, or changing anything in pingdotgg/t3code. That is a separate conversation with the maintainer and is not part of this work.
    • Any change to watcher strategy, debouncing, ignore rules, or refresh coalescing. That is the territory of Scope: watch the workspace so externally-added files appear in the Files tree #2 and its follow-up.
    • Adding a general allowlist or policy layer for project roots. This ticket refuses exactly two roots and nothing else.
    • Changing the entry cap, the scan timeout, or the idle TTL on the index.
    • Making the refusal user-overridable via a setting. If that turns out to be wanted, it is a follow-up ticket.

    Conventions:
    Read docs/internals/code-conventions.md, and .macroscope/check-run-agents/effect-service-conventions.md before touching the server service.

  3. added
    bugSomething isn't working
    ready-for-agentFully specified, ready for an AFK agent
    and removed
    questionFurther information is requested
    needs-triageMaintainer needs to evaluate this issue
    on Aug 29, 2026
  4. Francois3d commented on Aug 29, 2026

    @Francois3d
    OwnerAuthor

    First task: what FileFinder.create actually does with the flags off

    Tested against @ff-labs/fff-node 0.9.4 (the version pinned in apps/server/package.json), on darwin, via a throwaway script calling FileFinder.create directly with the same options the workspace index passes, minus the two flags.

    Outcome: it refuses — but only partly. Neither of the two outcomes the ticket anticipated holds cleanly.

    With both flags absent, FileFinder.create returns a non-ok result:

    {"basePath":"/","flags":"off","ok":false,
     "error":"Failed to init file picker: Can not run certain FFF features in a file system root or home directories. Consider smaller per-project directories."}
    

    The home-directory case behaves the same, and it reads the real HOME env var (probed with HOME pointed at a temp dir, so nothing scanned the developer's actual home):

    {"basePath":"<fakeHome>","HOME":"<fakeHome>","flags":"off","ok":false,
     "error":"Failed to init file picker: Can not run certain FFF features in a file system root or home directories. Consider smaller per-project directories."}
    {"basePath":"<fakeHome>/proj","HOME":"<fakeHome>","flags":"off","ok":true,"error":null}
    

    The gap: the library's check is an exact string match, and a trailing separator defeats it.

    {"basePath":"<fakeHome>/","HOME":"<fakeHome>","flags":"off","ok":true,"error":null}
    

    So simply deleting the two flags would have satisfied most of the acceptance criteria but silently failed this one:

    The check operates on the resolved absolute path: ~, an explicit absolute home path, and a trailing-slash variant of either all produce the same refusal.

    That is why the change does both: the flags are gone, and WorkspaceSearchIndex resolves the root and refuses / and $HOME itself, before a finder is ever constructed. Resolving first collapses ~, $HOME, an explicit absolute home path, and any trailing-slash variant into one branch, and lets the failure carry a reason naming the path rather than the generic native diagnostic above.

    For completeness, with the flags on — today's behavior — every one of these roots is accepted:

    {"basePath":"/","flags":"on","ok":true,"error":null}
    {"basePath":"<fakeHome>","flags":"on","ok":true,"error":null}
    

    Incidentally, the cost shows up plainly in the test suite: with the fix reverted, the three refusal tests take 15s, 11s and 1s because the unfixed code genuinely starts scanning / and $HOME. With the fix they are instant.

    Error modelling

    Kept WorkspaceSearchIndexCreateFailed and gave it a distinguishable reason rather than adding a sibling tagged error. A new tag would have rippled through WorkspaceEntriesError, the ws.ts failure switch, and the client-facing failure enum for no behavioral gain — ws.ts already forwards error.reason as detail, so the explanation reaches the client with no contract change.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingready-for-agentFully specified, ready for an AFK agent

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions