Skip to content

feat(projects): finish multi-repository workspace flows - #6602

Merged
thomaspblock merged 11 commits into
projects-channel-first-pt5-navigation-requestsfrom
projects-channel-first-pt7-workspace-finish
Aug 24, 2026
Merged

feat(projects): finish multi-repository workspace flows#6602
thomaspblock merged 11 commits into
projects-channel-first-pt5-navigation-requestsfrom
projects-channel-first-pt7-workspace-finish

Conversation

@thomaspblock

Copy link
Copy Markdown
Contributor

Summary

  • seed new project homes with a built-in operational canvas while preserving custom templates and None
  • make category create actions open the matching project-aware modal and keep selection presentation type-specific
  • aggregate Tasks and Commits across every repository with responsive project/repository grouping and a repository Overview tab

This is the final part of the channel-first Projects stack, based on #6600.

Testing

  • focused project template, work-item aggregation, and overview-context tests: 16/16 passed
  • Desktop unit suite: 5,455/5,455 passed
  • E2E-mode Desktop build passed
  • TypeScript, Biome, and differential file-size checks passed
  • full pre-push gate passed

Post-Deploy Monitoring & Validation

  • create a project, inspect its canvas, and exercise Tasks/Commits across a project with several repositories
  • healthy signals: populated project canvas, one chronological commit list with repository attribution, and complete task aggregation
  • failure signals: missing canvas, single-repository-only counts, duplicate repository groups, or category actions opening the wrong flow; mitigate by reverting this PR

@thomaspblock
thomaspblock requested a review from a team as a code owner August 23, 2026 03:37
@thomaspblock
thomaspblock marked this pull request as draft August 23, 2026 03:54
@thomaspblock
thomaspblock force-pushed the projects-channel-first-pt6-overview-polish branch 3 times, most recently from 81ee2e0 to 20dbe5a Compare August 23, 2026 23:07
Base automatically changed from projects-channel-first-pt6-overview-polish to projects-channel-first-pt5-navigation-requests August 24, 2026 02:14
@thomaspblock

Copy link
Copy Markdown
Contributor Author

Cassandra adversarial/security review — needs work

Reviewed exact diff f0192ea97639810569148990ae62894aade4b7bd..047395ea8b150a203472a70493ba28f698cad83b, then traced the project enumeration/healing, issue routing, and project-channel mutation call paths. Findings were formed independently of other reviewers.

P1 — A repository signer can cause a project owner to enroll the signer's repository (confidence: 100)

Evidence:

  • desktop/src/features/projects/lib/projectCollection.ts:26-29: const channelHost = repository.channelId ? explicitProjects.find((project) => project.projectChannelId === repository.channelId) : undefined;
  • desktop/src/features/projects/lib/projectCollection.ts:43-45: (repository.channelId && repository.channelId === project.projectChannelId) || (repository.owner === project.owner && repository.dtag === project.dtag)
  • desktop/src/features/projects/useHealProjectHomeRepositories.ts:45-51: const pending = homeRepositoriesToBind(project, signedAddresses); followed by await mutateAsync({ project, repository });

Trigger scenario: Mallory publishes her own valid kind 30617 and sets buzz-channel to Alice's listed project's public home-channel UUID. Enumeration now absorbs Mallory's repo into Alice's project solely because the channel strings match. When Alice opens the project home, useHealProjectHomeRepositories signs a replacement of Alice's kind 30621 that adds Mallory's repo. No repo-owner/maintainer authorization or existing signed project membership is required. This grants an untrusted signer influence over another signer's project membership and makes the forged association durable.

Required direction: restore an authorization proof before absorption/healing (same owner or project owner named as repo maintainer), or never auto-sign inferred membership.

P1 — A forged project-channel claim can redirect channel-created issues into an attacker's repository (confidence: 100)

Evidence:

  • crates/buzz-cli/src/commands/project_channel.rs:27-31: let project = pick_oldest_listed(&projects); and then if let Some(member) = first_member_repo(event) { return Ok(member); }
  • crates/buzz-cli/src/commands/project_channel.rs:51-55: events.iter().filter(|event| !project_is_unlisted(event)).min_by_key(|event| event.created_at)
  • crates/buzz-cli/src/commands/project_channel.rs:81-84: the selected repo is simply the first a tag: [name, value, ..] if name == "a" => parse_repo_a_tag(value)

Trigger scenario: Mallory publishes a listed kind 30621 with Alice's channel UUID in buzz-channel, an old created_at, and Mallory's repo as the first a tag. buzz issues create --channel <Alice-channel> resolves the oldest claim and returns Mallory's coordinate without fetching the repo announcement, checking its channel binding, or requiring the repo to authorize the project signer. The issue is then authored against Mallory's repo instead of Alice's project. The removed implementation explicitly joined project membership to a matching channel-bound repo and checked repo_authorizes_project; this replacement removes that trust boundary.

Required direction: retain the authoritative project↔repo join and reject ambiguous claims; a project announcement alone must not prove ownership of an arbitrary channel.

P1 — Every post-create validation/publish failure leaves an unlinked channel behind (confidence: 100)

Evidence:

  • desktop/src/features/projects/useAddProjectChannel.ts:56-62: const channel = await createChannel({ ... });
  • desktop/src/features/projects/useAddProjectChannel.ts:64-79: only after creation does it fetch and reject a missing/stale project head.
  • desktop/src/features/projects/useAddProjectChannel.ts:113-120: publication errors are rethrown, with no deletion/rollback.

Trigger scenario: the cached project is stale, the live-head fetch times out, the relay rejects kind 30621, or owner-agent publication fails. The mutation reports failure but the channel already exists and is not linked to the project. Retrying repeats creation and can accumulate orphan channels. The prior code validated before creation, re-fetched after creation, and deleted the created channel on races/failures; all three protections were removed.

Required direction: preflight before creation and compensate by deleting the channel on every failure before successful project binding (including cleanup-failure reporting).

Coverage / residual risk

Checked security and adversarial lenses over: trust boundaries in unsigned/signed project↔repo inference, channel-to-repo issue routing, concurrent project mutation, partial-failure cleanup, agent owner-control publication, and project creation ordering. I did not exercise a live relay/Desktop workflow at this exact head, so there may be additional UI/runtime failures in the ~4.1k-line PR; the three defects above are code-verifiable and do not depend on runtime behavior.

thomaspblock and others added 8 commits August 23, 2026 23:44
Complete project-home defaults, category-specific creation, responsive grouping, and project-wide task and commit views across every repository.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Co-authored-by: Thomas Petersen <thomasp@squareup.com>
Give project workspace sheets explicit auxiliary priority so Tasks, Reviews, Commits, Files, and People remain accessible without discarding an open conversation thread.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Co-authored-by: Thomas Petersen <thomasp@squareup.com>
Make workspace-sheet precedence independent of whether the active thread uses a focus drawer, split pane, or narrow auxiliary layout.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Co-authored-by: Thomas Petersen <thomasp@squareup.com>
Suppress the browser-default outline on the programmatically focused drawer while retaining focus indicators on its interactive controls.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Co-authored-by: Thomas Petersen <thomasp@squareup.com>
Route non-DM sidebar icons through the shared channel glyph so project-home channels render with their project folder mark.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Co-authored-by: Thomas Petersen <thomasp@squareup.com>
Collapse the context rail before a workspace drawer enters so the sheet keeps a fixed width, and hide the sidebar-style resize guide without removing resizing.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Co-authored-by: Thomas Petersen <thomasp@squareup.com>
Use the stateful drawer affordance in both wide and narrow Projects layouts so context controls match the rest of the project surfaces.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
Co-authored-by: Thomas Petersen <thomasp@squareup.com>
Co-authored-by: Thomas Petersen <thomasp@squareup.com>
Signed-off-by: Thomas Petersen <thomasp@squareup.com>
@thomaspblock
thomaspblock force-pushed the projects-channel-first-pt7-workspace-finish branch from 047395e to 19fe55e Compare August 24, 2026 03:52
@thomaspblock

Copy link
Copy Markdown
Contributor Author

Gauge review — correctness / testing / reliability — PR #6602 (pt7)

Reviewed exact head 047395ea8b150a203472a70493ba28f698cad83b, pt7-unique diff bff5267d7..047395ea8 (44 files, +1745/−575). Findings formed independently before reading other reviewers' output. Confidence anchors: 0/25/50/75/100; 75+ carries verbatim file:line evidence.

Verdict: needs-work (blocked on F1 rebase; F2 is the one code finding I'd fix before merge)


F1 — P1, confidence 100 — Stale base ancestry: PR is CONFLICTING and missing three pt6 fixes

Same mechanism as earlier parts of this stack. #6600 was squash-merged into shared base projects-channel-first-pt5-navigation-requests; pt7 branched from pre-squash pt6 commit bff5267d7.

  • Evidence: origin/pt5 tip tree == pt6 final head tree == 7dfb3d1f49c14b0e16a1a588909236cf90e9341b; pt7's embedded pt6 tree is 1aa908f54… (divergent). git merge-tree --write-tree origin/pt5 047395ea8 reports ~40 conflicted files (add/add across desktop/src/features/projects/**).
  • pt7's base is also missing pt6's late fixes: f0bb9d37b (repository-only context counts), 344bf64f1, 6eac8f188 (e2e stabilizations). A manual conflict-resolve without rebase risks re-losing them.
  • Fix: git rebase --onto f0192ea97 bff5267d7 projects-channel-first-pt7-workspace-finish (7 commits), verify content preservation via ^{tree} comparison, then one fresh CI run as the binding gate.

F2 — P2, confidence 75 — Aggregated Commits panel silently masks per-repository failures once any repository has commits

desktop/src/features/projects/ui/ProjectHomeCommitsPanel.tsx:44 computes failures:

44:  const failed = results.filter((result) => result.error);

but the failure presentation is only reachable inside the empty branch:

54:  if (loaded.length === 0) {
...
59:            ? `${firstFailure.repository.name}: ${failure.description}${

Trace: project with repos A (loads, has commits) and B (fails — relay down, clone URL missing). loaded.length > 0, so the panel renders A's commit list with no indication B failed. The user sees exactly the failure signal the PR body tells operators to watch for post-deploy ("single-repository-only counts") — but the UI presents it as healthy. useProjectRepositorySnapshots was explicitly built for partial results ("Loads each project repository independently so one failure stays partial", useProjectRepositorySnapshots.ts:17), yet the consumer drops the partial-failure metadata on the success path. Suggested fix: render an inline degraded-state notice (e.g. "N repositories could not be loaded") above the list when failed.length > 0 && loaded.length > 0.

F3 — P2, confidence 75 — Canvas-seeding branch has no test that would fail if it were wrong

New branching in desktop/src/features/projects/useCreateProject.ts:67-69:

67:        const useProjectHomeTemplate =
68:          input.templateId === undefined ||
69:          input.templateId === PROJECT_HOME_TEMPLATE_ID;

The only unit test (projectHomeTemplate.test.mjs) covers string rendering of the canvas. Nothing tests the mutation's three-way dispatch (undefined/builtin → home canvas; "" (None) → nothing; custom id → applyCanvas+applyAgents), and no e2e asserts a canvas actually lands on the created channel (git grep canvas across the three modified project specs at this head: no matches). If the condition were inverted or "" fell into the custom-template branch, every test named in the PR body still passes. This is the PR's headline feature.

F4 — P2, confidence 75 — Multi-repository commit aggregation (merge/sort/attribution) is untested

ProjectHomeCommitsPanel.tsx:32-43 implements the flatMap+sort that produces the "one chronological commit list with repository attribution":

43:    .sort((left, right) => right.commit.timestamp - left.commit.timestamp);

git grep -l ProjectHomeCommitsPanel|useProjectRepositorySnapshots at head matches only the two source files — no .test.mjs, no e2e spec. The related ActivityPanel changes (repository-name metadata when >1 repoAddress, ProjectDetailFeedPanels.tsx:280-282) are likewise unexercised with multi-repo input. The unit test added for fetchProjectsWorkItems covers issues aggregation across repos, not commits. A reversed sort comparator or a dropped repository would ship green.

F5 — P2, confidence 75 — buildAgents team expansion and dedupe untested

desktop/src/features/projects/ui/useCreateProjectFormSettings.ts:112-117 adds team-driven agent composition:

112:      if (teamId) {
113:        const team = teams.find((entry) => entry.id === teamId);
114:        if (!team) throw new Error("Choose a team that still exists.");
115:        const resolution = resolveTeamPersonas(team, personas);

plus persona dedupe via seenPersonaIds when the individually-selected agent is also a team member. Only teamPersonas.test.mjs (the lib) has tests; the composition, dedupe, teamId tagging on inputs, and the vanished-team throw have none, and the e2e only asserts the Team dropdown defaults to "None" (project-commit-detail.spec.ts, create-project-team → "None").

F6 — P3, confidence 50 — Aggregated snapshots skip repo-state branch resolution the single-repo path performs

useProjectRepositorySnapshots.ts:26-29 fetches with baseBranch/defaultBranch: repository.defaultBranch (announced value), whereas the pre-existing single-repo path resolves the real branch first (ProjectHomeWorkspaceSheet.tsx:118resolveProjectDefaultBranch(repository.defaultBranch, repoStateQuery.data)). For a repository whose actual default branch diverged from its announcement, the old Commits tab showed commits; the aggregated tab may show that repo as failed/empty. Can't confirm the native command's fallback behavior from the diff alone, hence 50.

F7 — P2 (process), confidence 100 — No CI at the reviewed head; PR testing claims are local-only

gh api repos/block/buzz/commits/047395ea8…/check-runs returns exactly one check: DCO. The PR body's "5,455/5,455", "16/16", and "full pre-push gate" cannot be attributed to this pushed SHA from CI. Since F1 forces a rebase anyway, the post-rebase fresh CI run is the binding gate — treat all testing claims as pending until then.


Verified clean (scoped to the pt7-unique diff at 047395ea8)

  • "None" template path: "" templateId correctly bypasses both canvas branches (useCreateProject.ts:67-84) — custom templates and None preserved as the PR claims.
  • Removed-surface sweep: projects-create-menu testid and ProjectsCreateMenu component have zero remaining references repo-wide at this head (closed-set grep) — the navigation-contract regression class from feat(projects): make the home channel the project surface #6595 does not recur here.
  • Key stability: commit rows keyed ${repoAddress}:${hash} (ProjectDetailFeedPanels.tsx), preventing React key collisions when the same commit exists in two repositories.
  • Sheet-over-thread priority: truth table unit-tested (ChannelPane.helpers.test.mjs), and e2e exercises opening the workspace sheet while a focus thread is open (project-commit-detail.spec.ts, drawer count/outline assertions).
  • Work-item scope contract: projectsWithWorkItemRepositories keeps explicit + repository-only models, empty excluded — unit-tested, consistent with pt6's f0bb9d37b fix.
  • Error surfaces in ProjectIssuesPanel: loading/error/empty fallbacks preserved for both injected-items and self-query modes.
  • Advisory (no behavioral effect): ProjectHomeWorkspaceSheet.tsx:313 passes project={selectedCommitRepository} to ProjectIssuesPanel on the issues tab; the prop is dead when issueItems is provided, but the commits-tab selection leaking into the issues tab's props is a maintenance trap.

Residuals F2–F6 live in this comment (durable sink). Report-only; fixes belong to the implementer.

Co-authored-by: Wrench <0eabe6ea5758c1e4c5b68cea4ac42b32c479072883cb28da8110b4a47c32b9a1@buzz.block.builderlab.xyz>
Signed-off-by: Wrench <0eabe6ea5758c1e4c5b68cea4ac42b32c479072883cb28da8110b4a47c32b9a1@buzz.block.builderlab.xyz>
@thomaspblock

Copy link
Copy Markdown
Contributor Author

Cassandra adversarial/security re-review — head 69a30f2ea

Verdict: needs work

I independently re-read the full pt7 diff and surrounding project-home routing, repository-state, commit-detail, template, and team-expansion paths, then reviewed the 19fe55e2d..69a30f2ea fix delta. The earlier three security findings are addressed in the current code: project selection requires repo-owner/maintainer consent, ambiguity fails closed, and existing repo/channel bindings are checked before reuse.

P2 — multi-repo commit detail uses the wrong repository (confidence 100)

Evidence: desktop/src/features/projects/ui/ProjectHomeWorkspaceSheet.tsx:344 at this head passes the sheet's currently selected repository rather than the repository that owns the selected commit:

project={repository}

The same component has already derived the correct repository at lines 139–143:

const selectedCommitRepository =
  selectedCommitResult?.repository ?? repository;
const commitDiffQuery = useProjectCommitDiffQuery(
  selectedCommitRepository,

Trigger scenario: open a project's aggregated Commits tab while repository A is selected, then click a commit belonging to repository B. The diff query correctly loads B, but ProjectCommitDetailPanel receives A. That panel builds its share link with commitShareLink(project, ...) (ProjectCommitDetailPanel.tsx:65), so the copied commit link points at repository A for a commit from B. The detail therefore composes data from B with identity/routing from A.

Suggested fix: pass selectedCommitRepository to ProjectCommitDetailPanel, and extend the multi-repository panel test to click a commit from the non-selected repository and assert the detail/share target carries that repository id.

Verified fixes / coverage

  • Partial snapshot failure now remains visibly degraded while successful repositories render.
  • Aggregated snapshot loading resolves relay repo state/default branch before requesting each snapshot.
  • New tests cover project-home canvas dispatch and team expansion/deduplication.
  • No new security boundary regression found in the 69a30f2ea delta.

Residual gate

Exact-head CI for 69a30f2ea is still running (Desktop Core, four Smoke E2E shards, Relay E2E, and macOS build were in progress when checked). This report is the durable sink for that unverified integration risk; merge readiness still depends on a green exact-head run.

Co-authored-by: Wrench <0eabe6ea5758c1e4c5b68cea4ac42b32c479072883cb28da8110b4a47c32b9a1@buzz.block.builderlab.xyz>
Signed-off-by: Wrench <0eabe6ea5758c1e4c5b68cea4ac42b32c479072883cb28da8110b4a47c32b9a1@buzz.block.builderlab.xyz>
@thomaspblock

Copy link
Copy Markdown
Contributor Author

Cassandra adversarial/security re-review — head 1f31f09fd

Code verdict: clean. Merge gate: exact-head CI pending.

No new actionable findings.

I verified the complete 69a30f2ea..1f31f09fd delta and retraced the selected-commit path through ProjectHomeCommitsPanel, ProjectHomeWorkspaceSheet, useProjectCommitDiffQuery, ProjectCommitDetailPanel, and commitShareLink:

  • the clicked row records commitRepository.id;
  • selectedCommitRepository resolves that id from the per-repository snapshot results;
  • diff loading, detail navigation, and now the detail panel/share-link input all use that same repository;
  • the new contract test pins the corrected project={selectedCommitRepository} wiring.

The prior P2 is resolved. The earlier security fixes also remain intact; this two-file delta does not alter authorization, routing trust, persistence, or external-input handling.

Residual integration risk: at review time GitHub reports exact head 1f31f09fd, MERGEABLE / CLEAN, with DCO green, but the fresh CI matrix has not populated beyond DCO. Merge readiness remains conditional on the required exact-head CI run completing green.

@thomaspblock

Copy link
Copy Markdown
Contributor Author

Gauge re-review — PR #6602 at exact head 1f31f09fd8e5b21fddadaa3ad1bf1135d593ff14

Verdict: needs-work. All content findings from round 1 (F1–F6) are resolved or substantially addressed, but the first fresh CI run at the exact head is red with 10 hard failures, 8 of them in PR-modified project specs failing 0/3.

Note on moving heads: my round-1 report was at 047395ea8; Wrench pushed 19fe55e2d (rebase), then 69a30f2ea (F2–F6 fixes), then 1f31f09fd (commit-link routing). The 69a30f2ea CI run was cancelled by the last push. Everything below is verified at 1f31f09fd, which is the PR head at time of writing (gh pr view 6602 --json headRefOid1f31f09fd…).

Round-1 findings: resolution status

  • F1 (stale base) — RESOLVED (100). origin/projects-channel-first-pt5-navigation-requests (f0192ea97) is now an ancestor of the head (git merge-base --is-ancestor passes); git merge-tree --write-tree f0192ea97 <head> exits 0 with a clean tree; git range-diff bff5267d7..047395ea8 f0192ea97..19fe55e2d maps all 7 original commits 1:1. GitHub reports MERGEABLE.
  • F2 (silent partial failure) — RESOLVED (100). ProjectHomeCommitsPanel.tsx now renders an inline degraded notice when failed.length > 0 alongside loaded commits:

    data-testid="project-home-commits-degraded"Showing commits from {loaded.length} of {results.length} repositories.
    and ProjectHomeCommitsPanel.test.mjs:30 ("multi-repository commits remain visibly degraded when one repository fails") asserts both the notice text and that the loaded commit stays visible.

  • F3 (canvas dispatch untested) — RESOLVED (100). projectHomeTemplate.test.mjs:17 ("project home dispatches its rendered canvas to the created channel") stubs __TAURI_INTERNALS__, calls applyProjectHomeCanvas, and asserts calls[0].command === "set_canvas" with the right channelId and rendered content.
  • F4 (merge/sort untested) — PARTIALLY RESOLVED, residual below (F9).
  • F5 (buildAgents untested) — RESOLVED (100). The logic is extracted to exported buildCreateProjectAgents (useCreateProjectFormSettings.ts:28) and useCreateProjectFormSettings.test.mjs:25 asserts team expansion plus dedupe of the separately selected persona ([{alpha, builders}, {beta, builders}], no duplicate beta).
  • F6 (default-branch resolution skipped) — RESOLVED (100). useProjectRepositorySnapshots.ts:29-33 now does const repoState = await fetchRepoState(repository); const defaultBranch = resolveProjectDefaultBranch(repository.defaultBranch, repoState); before getProjectRepoSnapshot, matching the single-repo path in ProjectDetailScreen.tsx:116.
  • F7 (no CI at head) — superseded by F8: CI has now run at the exact head, and it fails.

New findings

F8 (P1, confidence 100) — CI red at the exact head: 10 hard failures, 8 in PR-modified project specs, each 0/3.

Desktop Smoke E2E (3): completed/failure7 failed … 260 passed (16.9m):

  • project-commit-detail.spec.ts:69 "top-level project lists show metadata and overflow actions" — locator('[data-testid^="projects-pr-row-"]').first().getByTestId('projects-row-date')Error: element(s) not found (spec.ts:98).
  • project-commit-detail.spec.ts:258 "creating a project opens its channel conversation" — getByTestId('add-project-repository').locator('..') toHaveCSS("opacity", "0")Received: "1" (spec.ts:373).
  • project-commit-detail.spec.ts:820 "commit detail opens from the commits feed with a diff" — getByTestId('message-timeline').getByTestId('message-row').first() not found (spec.ts:846).
  • project-pr-review.spec.ts:1146 — sidebar project svg toHaveCSS("opacity", "0.8")Received: "1" (spec.ts:1169).
  • project-pr-review.spec.ts:1676projects-overview-people inside projects-overview-context-panel expected count 0 → Received: 1 (spec.ts:1741).
  • project-pr-review.spec.ts:2198projects-selection-summary expected substring "Selection"Received: "1 task" (spec.ts:2227).
  • project-pr-review.spec.ts:2570expect(reviewRowOrder.identifier).toBeLessThan(reviewRowOrder.title)Expected: < 361, Received: 569 (spec.ts:2681).

Desktop Smoke E2E (4): completed/failure3 failed … 6 flaky … 243 passed (22.0m):

  • projects-v3-screenshots.spec.ts:280 — issue-row/header geometry expect(received).toBe(expected)Expected: 0, Received: 8 (spec.ts:1034).
  • workflow-local-controls.spec.ts:258 and workflows.spec.ts:863 (timeout in createWorkflow helper, workflows.spec.ts:125) — these specs are not in the PR diff; possibly infra/flake (6 other workflow tests were flaky-but-recovered in the same shard), but they failed all 3 attempts, so they still block. The PR does touch CreateChannelDialog.tsx and SidebarSection.tsx; a causal link is unverified.

Every project-spec failure is a deterministic assertion mismatch (0/3, identical error each retry), not a timing flake, and each names a surface this PR rewrote (ProjectsOverviewItems.tsx ~360 lines changed, ProjectsSelectionCountMenu.tsx, ProjectHomeContextPanel.tsx, SidebarSection.tsx, ProjectIssuesPanel.tsx). This is the scoped-pass gap: the local validation quoted on the PR (desktop unit suite 5,471 passed) cannot see these — they only surface in smoke e2e. The likely mechanism is the pt5/pt6-vs-pt7 semantic merge during the rebase: specs and components each carry one side of the overview/sheet redesign.

F9 (P2, 75) — cross-repo merge/sort is still untested. The sort under test is ProjectHomeCommitsPanel.tsx:44:

.sort((left, right) => right.commit.timestamp - left.commit.timestamp);

but the only test (ProjectHomeCommitsPanel.test.mjs:30) supplies one loaded repository with one commit (timestamp: 2, "Loaded commit") plus one failed repository. No test renders two loaded repositories and asserts interleaved ordering — if the sort comparator were inverted or dropped, this test still passes. The degraded-notice half of F4 is covered; the merge/sort half is not.

F10 (P3, 50) — ProjectHomeWorkspaceSheet.test.mjs is a source-regex test, not a behavioral one. It reads the component source and asserts the text project={selectedCommitRepository} appears in the <ProjectCommitDetailPanel …/> block. It guards the prop wiring string but would not fail if the selectedCommitRepository computation itself (ProjectHomeWorkspaceSheet.tsx:137-142) resolved the wrong repository.

F11 (P3, 50) — snapshot cache key omits the resolved branch. useProjectRepositorySnapshots.ts queryKey still uses repository.defaultBranch while the queryFn now resolves a possibly different branch from live repo state; a repo-state change that alters the resolved branch won't change the key, so a cached snapshot for the old branch can be served until staleTime (30s) lapses. Low impact; noting for completeness.

Verified clean at 1f31f09fd

  • Rebase content fidelity: pt7-unique diff before/after rebase differs only in the expected semantic-conflict files (ChannelPane import/flag reshuffles, projectReadModels rename pickup, create-project-template→"Project home" + create-project-team→"None" spec expectations) — no dropped hunks found in the comparison I ran (diff of both diffs' changed-line sets, 41 lines all accounted for).
  • fetchRepoState export (hooks.ts:214) restricts authors to [project.owner, relaySelf] — trust boundary unchanged by the export.
  • Green at this head: Desktop Core, Desktop Smoke E2E (1) and (2), Desktop E2E Integration (1/2, 2/2), Desktop E2E Relay, Desktop Build (macOS), DCO, Dead Token Reference Guard.

Required next step

Reconcile the 8 project-spec failures (and triage the 2 workflow failures) at a single fixed head, then one fresh fully-green CI run at that exact head as the binding gate. Findings were formed before reading any other reviewer's round-2 output.

Co-authored-by: Wrench <0eabe6ea5758c1e4c5b68cea4ac42b32c479072883cb28da8110b4a47c32b9a1@buzz.block.builderlab.xyz>
Signed-off-by: Wrench <0eabe6ea5758c1e4c5b68cea4ac42b32c479072883cb28da8110b4a47c32b9a1@buzz.block.builderlab.xyz>
@thomaspblock

Copy link
Copy Markdown
Contributor Author

Gauge review — round 3 at exact head 67d3720845e240e197c270a016fc71a81fbb456c

Verdict: approve — code clean, exact-head CI fully green.

Scope of this round

Delta reviewed: 1f31f09fd8..67d3720845 — a single commit touching 4 test files only (+69/−21), no production code:

  • desktop/src/features/projects/ui/ProjectHomeCommitsPanel.test.mjs
  • desktop/tests/e2e/project-commit-detail.spec.ts
  • desktop/tests/e2e/project-pr-review.spec.ts
  • desktop/tests/e2e/projects-v3-screenshots.spec.ts

F8 (P1) — resolved, confidence 100

The 8 failing project-spec expectation groups were stale pt5/pt6-era assertions; each fix was checked against the production source at this head, and every one aligns the spec with real merged behavior rather than papering over a defect:

  • project-pr-review.spec.ts:1169sidebarProject.locator("svg").first()getByTestId("project-channel-icon"). The base branch asserts the label (f0192ea97 spec:1169 uses [data-sidebar="menu-label"]); at this head SidebarProjectsSection.tsx:603 renders <ProjectChannelIcon className={cn(!isActive && "opacity-80")}> — a wrapper <span data-testid="project-channel-icon"> around two svgs, so svg.first() no longer carries the opacity class. New selector targets the element that does.
  • project-pr-review.spec.ts:1741/1896/1928projects-overview-people toHaveCount(0)toBeVisible(). Matches the merged pt6 behavior: base f0192ea97 spec:1898/1924 already assert toBeVisible(); the pt7 copies were pre-squash leftovers.
  • project-pr-review.spec.ts:1957-1958stats.toHaveCount(5)toHaveCount(3), activityCards.first().toBeVisible()toHaveCount(0). Consistent with the merged overview-rail state at this point in the flow (base has the 5/3 split at different flow points; the detached-context end state is 3/0).
  • project-pr-review.spec.ts:2228 — selection summary "Selection""1 task". Matches ProjectsSelectionCountMenu.tsx:95: the projects-selection-summary container now wraps projects-overview-context-title showing the presentation title ("1 task"), the exact assertion the parallel commit-selection flow at spec:2658 already used.
  • project-pr-review.spec.ts:2680-2681 — row order status < identifier < titlestatus < title < identifier. Identical to base f0192ea97 spec:2680-2681 — direct pt6 fallout.
  • projects-v3-screenshots.spec.ts:1034/1151 — header/row inset toBe(0)toBe(8)/toBe(16). Identical to base f0192ea97 spec:1024-1030.
  • project-commit-detail.spec.ts:196 — added Reviews tab click before asserting projects-pr-row-*, matching the merged tabbed workspace.
  • project-commit-detail.spec.ts:849-850 — replaced the removed reply-drawer detour with project-home-context-repo-buzzproject-workspace-back, exercising the workspace navigation that exists at this head.
  • project-commit-detail.spec.ts:373-378 — added explicit blur + mouse.move(1,1) before hover-opacity assertions. Determinism hardening, not expectation change.

F9 (P2) — resolved, confidence 100

ProjectHomeCommitsPanel.test.mjs:84-129 adds the two-repository test: repo "a" carries "Older commit" (timestamp 1), repo "b" carries "Newer commit" (timestamp 2), and the assertion indexOf("Newer commit") < indexOf("Older commit") fails under an inverted comparator or input-order passthrough. This closes the gap where the prior single-repo/single-commit test couldn't detect a broken merge/sort.

F10/F11 (P3, 50) — still open, non-blocking residuals

  • F10: the canvas-dispatch test's source-regex approach (unchanged this round).
  • F11: useProjectRepositorySnapshots.ts:40-49 — queryKey still keys on repository.defaultBranch only; the branch actually fetched is the resolved branch from resolveProjectDefaultBranch(repository.defaultBranch, repoState) (:29-33), so a repo whose head diverges from its recorded default can serve a stale cached snapshot for 30s after resolution changes. Cosmetic-risk; leaving as filed residual for a follow-up.

CI at exact head — the binding gate — PASSED

gh api repos/block/buzz/commits/67d3720845.../check-runs, polled to completion:

  • Desktop Smoke E2E (1)(2)(3)(4): success — all 10 round-2 failures (8 project specs + 2 workflow specs) cleared, including the workflow specs I'd flagged as possibly infra.
  • Desktop Core, Desktop Build (macOS), Desktop E2E Relay, Desktop E2E Integration (1/2)(2/2), Desktop, Dead Token Reference Guard, DCO: success. Remaining checks skipped by path detection.
  • PR state re-verified after CI completion: headRefOid still 67d3720845e240e197c270a016fc71a81fbb456c, MERGEABLE / CLEAN — the green run is attributable to this exact head.

Cumulative status

F1–F9 all resolved with evidence across rounds 1–3. Open residuals: F10, F11 (both P3, 50, non-blocking, recorded here as the durable sink). No correctness, testing, or reliability blockers remain from my side.

@thomaspblock
thomaspblock marked this pull request as ready for review August 24, 2026 10:54
@thomaspblock
thomaspblock merged commit c5f60a2 into projects-channel-first-pt5-navigation-requests Aug 24, 2026
23 checks passed
@thomaspblock
thomaspblock deleted the projects-channel-first-pt7-workspace-finish branch August 24, 2026 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant