Conversation
Review: CHANGES-NEEDED — short list, and the design is rightI merged this into current One integration gap the merge surfaced, not a defect in what you wrote: Overlap with what shipped: none. This extends the same service.I checked specifically for a second link concept, a second cache, or a second client, and there is none of the three. Three things I want to name because they are the best decisions in the PR:
Claims I checked against the code and can confirm: Authentication is unchanged and correct: everything goes through Required before merge
Worth fixing while you are in there
Not verified here: the approved-repository flow on device, which your description already lists as owed. This is the cleanest external contribution in this area I have reviewed — it extends the shipped service rather than shadowing it, obeys the explicit-link rule from #462 exactly, and its tests are load-bearing rather than decorative. The list above is small and none of it touches the design. |
Storage for issue eneskirca#462 phase 3: a node carries the issues and pull requests the user explicitly attached to it. The array is git-shared content in .nodeterm/project.json, so it is sanitized as hostile input.
…re path Both directions of .nodeterm/project.json plus the canvas-sync inbound hook, mirroring the trigger sanitizer.
getIssueOrPull admits a pull request where getIssue must keep refusing one; query's filtering is extracted to a column-blind `candidates` so the picker can ask about the whole repository. lookup returns typed refusals as values because both transports flatten a thrown code.
Canvas chips hold no host subscription, so freshness is bounded by the lookup TTL plus whatever the last board load seeded.
61e84f1 to
58164aa
Compare
One picker, one chip and one write funnel, reached from the canvas node menu, the group frame, the board card, the card modal and its metadata strip. The relationship is only ever made by a click; nothing is inferred.
lookup is relay-reachable and reaches the network, with nothing bounding it beyond the 404 memo. Concurrent lookups of one (repository, number) now share one read, and an API answer (found or not) is remembered for a minute: the same reasoning as REFRESH_MIN_INTERVAL_MS. The miss memo is written inside the flight, so it is in place before any caller resumes.
ensureCard recorded nothing for failed / configuration-changed answers or a rejected call, so every chip remount while offline re-issued its lookup. It now records a 15 s back-off. A link whose number is the other kind records a miss under the asked key instead of retrying forever, and invalidate clears pending and the back-off, with a comment on why it does not re-fetch.
58164aa to
07e5b22
Compare
|
@eneskirca Thanks for the review! Addressed all of it, PTAL:
|
Phase 3a of #462: a node can be attached to a GitHub issue or pull request, explicitly.
The rule this is built on: a link exists only because the user made it. Nothing is inferred from terminal output, a transcript, or a branch name — so there is no path here that can put a wrong chip on someone's canvas.
What to review
CanvasNodeState.github?: GitHubLink[](kind + number + a title snapshot). The repository is the project's and is never stored per link, so the same committedproject.jsonresolves correctly in any clone. It is git-shared content, sosanitizeNodeGitHubLinksruns on every crossing in both directions, exactly likesanitizeNodeTriggers— plus at the canvas-sync inbound hook.lookupreports refusals as VALUES, not throws. Electroninvokeand the ws-bridge'sRpcErrboth flatten a typed code, and the picker has to rendernot-approvedas a disabled row rather than as a failure.searchis a method, not acolumnIdsentinel onquery. A sentinel would collide with a real column id, leak into thecountskeys and the relay jail's shape, and force every existingquerycaller to handle a third state.queryandsearchshare the new column-blindcandidates, soquery's output is unchanged — its existing tests pass untouched.getIssuestill refuses a pull request.moveIssuerelies on that guard;getIssueOrPullis an opt-in sibling for the one caller that wants either kind.setNodeGitHubLinksin Canvas does the node write, the dirty mark and the board-log event together; every surface (node menu, group frame, board card, card modal, metadata strip) reaches it through a module bridge, the same indirectionsetWorktreeActionHandleruses.Canvas chips deliberately hold no host subscription and do not refresh while mounted: a chip looks its item up when painted (reusing an answer under 5 minutes old), and a board open re-seeds them. Host-side,
lookupsingle-flights per repository+number and memoises API answers for a minute. Documented; a later phase can hold a subscription while a chip is visible.Surfaces: Desktop and Server Edition both work (the handlers are registered by
registerGitHubIntegration, which both shells boot). The Omni (all-projects) board works too: each lane's chips, card modal and card menu act on that lane's project. Relay guests are jailed to the shared project:lookup/searchhave rows inrelay-project-scope.ts, added in the same commit as the channels. Mobile: N/A — the agent-status mirror carries no node data. cc @eneskirca for the mobile call.Tests
New:
shared/github-link.test.ts,core/workspace-files.github-links.test.ts,renderer/state/workspace.github-links.test.ts,renderer/lib/githubLinks.test.ts,renderer/state/githubLinks.test.ts,GitHubLinkChip.test.tsx,GitHubLinkPicker.test.tsx,CardMetaBar.test.tsx.Extended:
node-exec,client,service,handlers,relay-host,relay-project-scope,KanbanView.accountMenu,toKanbanSession,board-log-panel,ui-visibility.npm run typecheckclean. Failures in this checkout are environmental only (node-pty not built, jsdom localStorage, BSDmv) and in files this PR does not touch.Not in this PR
A reverse indicator on the GitHub card, and an agent-facing canvas-control
attachverb — both land on this same write funnel later. Device verification of the approved-repository flow is still owed.