A repo cloned on a box no longer shows as a second project card - #218
Merged
Merged
Conversation
The board merges a repo's copies across engines by (githubOwner, githubName), but that identity was captured once at registration and never again. The phone's "New project" git-inits an empty folder and registers it BEFORE anything is cloned into it, so the row is born with no identity and never regains one: the repo shows twice, and "Run on <box>" re-clones a copy the box already has, into a second directory. Read the identity from the repo's `origin` URL instead of `gh repo view`, and re-derive it on every projects:list so a repo that gains a remote later repairs itself. `gh repo view` needed gh installed, authed and online, so a box missing any of the three silently lost its identity; with no argument it also resolves through BaseRepo(), which reports a fork's PARENT, so two engines could derive two different identities for the same clone. The repair writes through a new repo.updateProject rather than upsertProject, which would have bumped lastOpenedAt and reshuffled the sidebar on every board load. The card key is now lowercased: two clones of one repo really do disagree on casing (clawnify/taskwindow here against clawnify/TaskWindow on a box) and GitHub treats those as the same repo.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TaskWindow existed locally; a new project called TaskWindow was then created from the iOS app on
hetzner-devboxand the repo cloned into it. The desktop board came back showing them as two separate projects.Cause
The board merges a repo's copies across engines into one card by
(githubOwner, githubName)(unify.ts), and that identity was written once at registration and never again (dispatcher.ts, the only two writers). The phone's New project callsregister(path, { init: true }), which creates the folder,git inits it and registers it before anything is cloned in, sodetectGithubRepohad nooriginto read and the row was born with no identity.The timestamps on the box confirm it: the project row was created at
21:22:25Z, and~/TaskWindow/.git/configwas written at21:42:03Z, 20 minutes later.This is not cosmetic. Because the box's copy isn't a member of the local card, picking Run on hetzner-devbox takes the else branch in the composer and clones
clawnify/taskwindowto~/taskwindow, beside the~/TaskWindowthe box already has, plus a third project row.Fix
originURL, notgh repo view.ghneeds to be installed, authenticated and online, so a box missing any of the three silently lost its identity. With no argument it also resolves throughBaseRepo(), which reports a fork's parent, so two engines could derive two different identities for the same clone. One derivation, offline and instant, used by both the register and repair paths.cloneReponow reuses the same parser instead of its own inline copy of the regex.projects:listrepairs the identity before answering, so a repo that gains a remote later heals itself on the next board load. Measured at 30ms for 11 real projects, on a call made at board mount and on connect and never polled, which keeps it well inside the aggregate's 10s merge-read budget. That budget is why detection must not touch the network.repo.updateProject, notupsertProject, which setslastOpenedAtand would have reshuffled the sidebar (listProjectsorders by it) on every board load.clawnify/taskwindowlocally againstclawnify/TaskWindowon the box. GitHub treats those as one repo.Verification
contaktlygainingContaktly/contaktly), with row order and everylastOpenedAtunchanged.originURLs on the Mac and both boxes produce the intended board: TaskWindow, contaktly and Clawnify each merge Local withhetzner-devbox, Ateam spans all three engines, and the two repos that genuinely have nooriginstay honestly separate.bun test400 pass,bun run typecheckclean. The three lint errors in the touched files are byte-identical atmain; no new ones, and the unrelated reformats biome wanted were reverted.Repairs run per engine, so a box's rows heal once that box's server is on a build containing this. Documented in the troubleshooting table.