Repository navigation
[Feature]: Inherit a default provider and model from a parent directory (e.g. ~/Developer/Work vs ~/Developer/Personal) #6179
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I am describing a concrete problem or use case, not just a vague idea.
Area
apps/web
Problem or use case
My projects live in two trees that mean different things:
~/Developer/Personal/...
~/Developer/Work/...
Everything under Work should start on my work provider account and its approved model. Everything under Personal should start on my personal one. The split is a property of where the project lives, not of any individual project.
Today the default provider and model is a per-project field. defaultModelSelection already exists on the project record (packages/contracts/src/orchestration.ts:225, :424, :1086), project.meta.update accepts it (:629, :639), and draft/thread creation already consumes it. So the mechanism works — but it has to be set once per project, by hand, and again for every new project I add.
That does not scale past a handful of repos, and the failure mode is quiet: a new project under Work inherits nothing, so the first thread starts on whichever selection was last used, which may be the personal account. The mistake is usually only obvious after the turn has already run.
Proposed solution
Let a parent directory carry a default ModelSelection that projects underneath inherit.
- In sidebar/project settings, allow setting a default provider + model on a directory scope (e.g.
~/Developer/Work), not just on a single project. - Resolve a project's effective default by walking its
workspaceRootup to the nearest ancestor that has a default configured; the longest matching path prefix wins, so~/Developer/Work/client-acan still override~/Developer/Work. - An explicit per-project
defaultModelSelectioncontinues to win over any inherited one. - New threads and drafts consume the resolved value through the path that already exists — no change to thread creation.
Path prefix matching already has a home: normalizeProjectPathForComparison and the ancestor/prefix logic in packages/client-runtime/src/state/projectGrouping.ts do this kind of comparison for the sidebar's directory grouping today.
Why this matters
It turns "keep work and personal accounts separate" from a per-project chore that silently fails on every new repo into a rule I state once. That matters for cost attribution, for subscription and rate limits landing on the right account, and for organizational boundaries where running a work repo through a personal account is a real problem rather than an inconvenience.
It is also the natural way to express the intent. I do not want to configure thirty projects; I want to say "this tree is work".
Smallest useful scope
One inherited ModelSelection per directory, resolved by longest matching path prefix, with per-project settings taking precedence.
No new grouping entity, no per-folder settings UI tree, no allow-list of permitted models, no server or provider changes. Reuse the existing defaultModelSelection contract and the existing project-settings dialog; add the directory scope and the resolution step.
Alternatives considered
- Set it per project — what I do now. It works, but every new repo starts wrong until I notice, which is exactly the case I want to eliminate.
- Wait for project folders (Add folders of projects #289, [Feature]: Group Side Menu by Directory Structure #3722, [Feature]: Support full folder management and drag-and-drop folder import #6138) and hang settings off a folder entity. Cleaner conceptually, but those are about sidebar organisation and folder management, and this does not need to wait for them — a path prefix is enough and needs no new entity.
- Per-project provider UI ([Feature]: Expose the existing per-project default provider in Project settings #4847) would make the current per-project field editable. Useful and complementary, but orthogonal: it improves setting the value one project at a time, not inheriting it.
Risks or tradeoffs
- Resolution needs one clear rule. Longest matching prefix, project override wins, is the least surprising; anything fuzzier gets hard to reason about when trees nest.
- Path comparison has to stay consistent with existing normalisation — case sensitivity, trailing separators, and Windows separators.
normalizeProjectPathForComparisonalready handles this and should be the single source of truth rather than a second implementation. - Symlinked or moved projects can fall outside the tree they appear to be in. Matching on the stored
workspaceRootis predictable even if it occasionally surprises. - The UI needs to show why a project's default is what it is — an inherited value that looks unset is worse than no feature. Showing "inherited from ~/Developer/Work" alongside the control would cover it.
Examples or references
Relevant existing surface:
defaultModelSelectionon the project record —packages/contracts/src/orchestration.ts:225,:424,:1086project.meta.updateaccepting it —packages/contracts/src/orchestration.ts:629,:639- Path normalisation and ancestor matching —
packages/client-runtime/src/state/projectGrouping.ts - [Feature]: Expose the existing per-project default provider in Project settings #4847 — expose the existing per-project default provider in Project settings
- Add folders of projects #289, [Feature]: Group Side Menu by Directory Structure #3722, [Feature]: Support full folder management and drag-and-drop folder import #6138 — project folders / directory grouping
Contribution
- I would be open to helping implement this.
Activity