Skip to content

feat(source-control): default repository follows gh and is asked when adding a folder - #661

Merged
incognitojam merged 1 commit into
mainfrom
feat/default-repository-follows-gh
Oct 7, 2026
Merged

incognitojam merged 1 commit into
mainfrom
feat/default-repository-follows-gh

Conversation

@incognitojam

Copy link
Copy Markdown
Owner

Note

Repository identity now follows the same rule as gh: the gh repo set-default choice, then the upstream remote, then origin. Adding an existing folder whose remotes point at more than one GitHub repository asks for that default on web, desktop, and mobile.

styal identified a checkout by the remote its current branch tracks. That could disagree with gh, and so with agents running gh in the checkout. For example, a branch tracking the fork gave the project the fork's identity while gh targeted the parent, and the parent's pull request was then dropped as belonging to another repository. The lookup also ran git for-each-ref on every identity check. Upstream has moved its GitHub API calls to the gh rule (pingdotgg#16321, pingdotgg#16322), and we decided to follow it, with the default set per project.

Changes

  • Identity order. Repository identity uses the remote pinned by gh repo set-default (remote.<name>.gh-resolved, including an owner/repo value), then upstream, then origin, then the first remote. The remote the current branch tracks no longer counts, and cached lookups no longer run git.
  • Asked when adding a folder. Add Project → Local folder asks for the default repository when the checkout's remotes point at more than one distinct GitHub repository and no default is set. Each repository is offered once, and the one that applies without a default is listed first. Decide later writes nothing. The web and desktop command palette reuse the fork clone's step, and the mobile Local folder screen reuses the clone screen's rows. Cloning is unchanged. styal project add, styal app, and --auto-bootstrap-project-from-cwd do not ask.
  • Changes apply immediately. Setting the default refreshes the project's repository identity and re-emits the project, so a checkout moves to its new project group without waiting for the identity cache. Clients also re-read the checkout's default after setting it, so the moved checkout's settings row shows the new value.
  • Ledger and docs. pull-request-association-continuity goes back to maintained with the new rule, and source-control-fork-remotes covers the add-folder prompt. The source control guide gets a "Choose the default repository" section, and the styal differences page is updated. fix(server): repository identity prefers the gh default remote pingdotgg/t3code#14291, which would make upstream's identity honour gh repo set-default, is added to the tracked upstream PRs.
Web and desktop: Add Project Mobile: Local folder
Command palette asking for the default repository with pingdotgg/t3code (upstream), incognitojam/styal (origin), and Decide later Mobile Local folder screen listing pingdotgg/t3code, incognitojam/styal, and Decide later under Default repository

Before this change, adding either folder added it immediately without asking. The checkout's path is covered in both images.

Validation

Web (dev server for this branch, headless Chromium), using synthetic checkouts whose origin is incognitojam/styal:

  • A, with upstream set to pingdotgg/t3code and no default: Add Project listed pingdotgg/t3code (upstream), incognitojam/styal (origin), and Decide later. Decide later wrote no gh-resolved, and the project's key became github.com/pingdotgg/t3code.
  • A2, with the same remotes: choosing incognitojam/styal wrote remote.origin.gh-resolved base, and the project joined the incognitojam/styal group.
  • B (same remotes, default already set), C (origin only), and D (two remotes for the same repository): added with no prompt.
  • Project settings for A: changing Default repository to incognitojam/styal moved A into that group straight away, and the settings page followed it. Its row there showed incognitojam/styal (origin). Clearing the default moved A back, and its own group showed "Not set (GitHub CLI decides)". No reload was needed at any step. Without the client re-read, the moved checkout's row showed the previous value.
  • No page errors.

Android emulator (Pixel 7, Android 17), paired with the same server:

  • MA (two GitHub repositories, no default): Local folder showed both repositories and Decide later. Choosing pingdotgg/t3code wrote remote.upstream.gh-resolved base and opened the new thread draft.
  • MB (origin only) and MC (default already set): went straight to the draft without asking.
  • The project picker then showed pingdotgg/t3code with 3 checkouts (A, MA, MC) and incognitojam/styal with 6.

Tests: RepositoryIdentityResolver.test.ts (adds a test that the branch's tracking remote is ignored), server.test.ts (setting the default re-emits the project with its new identity), and projects.test.ts (which remotes produce a prompt). 236 tests passed. The ledger check passed, and the web, mobile, server, and client-runtime typechecks passed.

Not verified: changing the default from the mobile Git sheet, because the only thread on the test server was in a checkout whose git config is shared with a real repository. The desktop shell was not run separately; it uses the same command palette code as web.

Known differences and limits

  • gh ranks a remote named github before origin. styal does not.
  • The prompt counts GitHub repositories only. For a GitHub fork as origin with a non-GitHub upstream, nothing is asked, and identity follows upstream while gh uses origin.
  • When the default is changed from a worktree thread's Git sheet, the project's own identity updates when its cache entry expires (up to a minute), because the change is keyed to the worktree path.
  • A checkout left on Decide later groups under the parent repository, because upstream applies when no default is set.

Written by an agent (Claude Code, claude-opus-5-5).

Repository identity now prefers the `gh repo set-default` choice
(`remote.<name>.gh-resolved`), then the `upstream` remote, then `origin`,
then the first remote. It no longer prefers the remote the current branch
tracks. That rule could disagree with `gh`, and so with agents running
`gh` in the checkout: a branch tracking the fork gave the project the
fork's identity while `gh` read the parent, and the parent's pull request
was then dropped as belonging to another repository. It also ran
`git for-each-ref` on every lookup; identity lookups are cache hits again.

Because the default now decides identity, adding an existing folder asks
for it when the checkout's remotes point at more than one GitHub
repository and no default is set. The web and desktop command palette
reuse the fork clone's default-repository step, and the mobile Local
folder screen reuses the clone screen's rows. Each repository is offered
once, the one that applies without a default first. "Decide later" writes
nothing and leaves the choice in project settings. Cloning is unchanged,
and `styal project add`, `styal app`, and
`--auto-bootstrap-project-from-cwd` do not ask.

Setting the default over RPC now refreshes the project's repository
identity and re-emits the project, so project settings, the mobile Git
sheet, and the new prompt apply without waiting for the identity cache.
Clients also re-read the checkout's default after setting it, so a
checkout that moves to another project group shows the new default
there.

Pull request association still rejects a same-named branch from another
fork and still survives hosted branch pruning; neither depended on the
branch remote rule.
@incognitojam
incognitojam merged commit c24772a into main Oct 7, 2026
18 checks passed
@incognitojam
incognitojam deleted the feat/default-repository-follows-gh branch October 7, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant