Merge remote-tracking branch 'origin/main' into AI-105/worktree-thread-scope

Richard Feldman created

Change summary

.github/pull_request_template.md    |  21 --
.github/workflows/pr-size-check.yml | 109 -----------------
.github/workflows/pr-size-label.yml | 195 -------------------------------
3 files changed, 3 insertions(+), 322 deletions(-)

Detailed changes

.github/pull_request_template.md 🔗

@@ -1,28 +1,13 @@
-## Context
+Self-Review Checklist:
 
-<!-- What does this PR do, and why? How is it expected to impact users?
-     Not just what changed, but what motivated it and why this approach.
-
-     Link to Linear issue (e.g., ENG-123) or GitHub issue (e.g., Closes #456)
-     if one exists — helps with traceability. -->
-
-## How to Review
-
-<!-- Help reviewers focus their attention:
-     - For small PRs: note what to focus on (e.g., "error handling in foo.rs")
-     - For large PRs (>400 LOC): provide a guided tour — numbered list of
-       files/commits to read in order. (The `large-pr` label is applied automatically.)
-     - See the review process guidelines for comment conventions -->
-
-## Self-Review Checklist
-
-<!-- Check before requesting review: -->
 - [ ] I've reviewed my own diff for quality, security, and reliability
 - [ ] Unsafe blocks (if any) have justifying comments
 - [ ] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
 - [ ] Tests cover the new/changed behavior
 - [ ] Performance impact has been considered and is acceptable
 
+Closes #ISSUE
+
 Release Notes:
 
 - N/A or Added/Fixed/Improved ...

.github/workflows/pr-size-check.yml 🔗

@@ -1,109 +0,0 @@
-# PR Size Check — Compute
-#
-# Calculates PR size and saves the result as an artifact. A companion
-# workflow (pr-size-label.yml) picks up the artifact via workflow_run
-# and applies labels + comments with write permissions.
-#
-# This two-workflow split is required because fork PRs receive a
-# read-only GITHUB_TOKEN. The compute step needs no write access;
-# the label/comment step runs via workflow_run on the base repo with
-# full write permissions.
-#
-# Security note: This workflow only reads PR file data via the JS API
-# and writes a JSON artifact. No untrusted input is interpolated into
-# shell commands.
-
-name: PR Size Check
-
-on:
-  pull_request:
-    types: [opened, synchronize]
-
-permissions:
-  contents: read
-  pull-requests: read
-
-jobs:
-  compute-size:
-    if: github.repository_owner == 'zed-industries'
-    runs-on: ubuntu-latest
-    timeout-minutes: 5
-    steps:
-      - name: Calculate PR size
-        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
-        with:
-          script: |
-            const fs = require('fs');
-
-            const { data: files } = await github.rest.pulls.listFiles({
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              pull_number: context.issue.number,
-              per_page: 300,
-            });
-
-            // Sum additions + deletions, excluding generated/lock files
-            const IGNORED_PATTERNS = [
-              /\.lock$/,
-              /^Cargo\.lock$/,
-              /pnpm-lock\.yaml$/,
-              /\.generated\./,
-              /\/fixtures\//,
-              /\/snapshots\//,
-            ];
-
-            let totalChanges = 0;
-            for (const file of files) {
-              const ignored = IGNORED_PATTERNS.some(p => p.test(file.filename));
-              if (!ignored) {
-                totalChanges += file.additions + file.deletions;
-              }
-            }
-
-            // Assign size bracket
-            const SIZE_BRACKETS = [
-              ['Size S',  0,       100,  '0e8a16'],
-              ['Size M',  100,     400,  'fbca04'],
-              ['Size L',  400,     800,  'e99695'],
-              ['Size XL', 800, Infinity, 'b60205'],
-            ];
-
-            let sizeLabel = 'Size S';
-            let labelColor = '0e8a16';
-            for (const [label, min, max, color] of SIZE_BRACKETS) {
-              if (totalChanges >= min && totalChanges < max) {
-                sizeLabel = label;
-                labelColor = color;
-                break;
-              }
-            }
-
-            // Check if the author wrote content in the "How to Review" section.
-            const rawBody = context.payload.pull_request.body || '';
-            const howToReview = rawBody.match(/## How to Review\s*\n([\s\S]*?)(?=\n## |$)/i);
-            const hasReviewGuidance = howToReview
-              ? howToReview[1].replace(/<!--[\s\S]*?-->/g, '').trim().length > 0
-              : false;
-
-            const result = {
-              pr_number: context.issue.number,
-              total_changes: totalChanges,
-              size_label: sizeLabel,
-              label_color: labelColor,
-              has_review_guidance: hasReviewGuidance,
-            };
-
-            console.log(`PR #${result.pr_number}: ${totalChanges} LOC, ${sizeLabel}`);
-
-            fs.mkdirSync('pr-size', { recursive: true });
-            fs.writeFileSync('pr-size/result.json', JSON.stringify(result));
-
-      - name: Upload size result
-        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
-        with:
-          name: pr-size-result
-          path: pr-size/
-          retention-days: 1
-defaults:
-  run:
-    shell: bash -euxo pipefail {0}

.github/workflows/pr-size-label.yml 🔗

@@ -1,195 +0,0 @@
-# PR Size Check — Label & Comment
-#
-# Triggered by workflow_run after pr-size-check.yml completes.
-# Downloads the size result artifact and applies labels + comments.
-#
-# This runs on the base repo with full GITHUB_TOKEN write access,
-# so it works for both same-repo and fork PRs.
-#
-# Security note: The artifact is treated as untrusted data — only
-# structured JSON fields (PR number, size label, color, boolean) are
-# read. No artifact content is executed or interpolated into shell.
-
-name: PR Size Label
-
-on:
-  workflow_run:
-    workflows: ["PR Size Check"]
-    types: [completed]
-
-jobs:
-  apply-labels:
-    if: >
-      github.repository_owner == 'zed-industries' &&
-      github.event.workflow_run.conclusion == 'success'
-    permissions:
-      contents: read
-      pull-requests: write
-      issues: write
-    runs-on: ubuntu-latest
-    timeout-minutes: 5
-    steps:
-      - name: Download size result artifact
-        id: download
-        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
-        with:
-          script: |
-            const fs = require('fs');
-            const path = require('path');
-
-            const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              run_id: context.payload.workflow_run.id,
-            });
-
-            const match = allArtifacts.data.artifacts.find(a => a.name === 'pr-size-result');
-            if (!match) {
-              console.log('No pr-size-result artifact found, skipping');
-              core.setOutput('found', 'false');
-              return;
-            }
-
-            const download = await github.rest.actions.downloadArtifact({
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              artifact_id: match.id,
-              archive_format: 'zip',
-            });
-
-            const temp = path.join(process.env.RUNNER_TEMP, 'pr-size');
-            fs.mkdirSync(temp, { recursive: true });
-            fs.writeFileSync(path.join(temp, 'result.zip'), Buffer.from(download.data));
-            core.setOutput('found', 'true');
-
-      - name: Unzip artifact
-        if: steps.download.outputs.found == 'true'
-        env:
-          ARTIFACT_DIR: ${{ runner.temp }}/pr-size
-        run: unzip "$ARTIFACT_DIR/result.zip" -d "$ARTIFACT_DIR"
-
-      - name: Apply labels and comment
-        if: steps.download.outputs.found == 'true'
-        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
-        with:
-          script: |
-            const fs = require('fs');
-            const path = require('path');
-
-            const temp = path.join(process.env.RUNNER_TEMP, 'pr-size');
-            const resultPath = path.join(temp, 'result.json');
-            if (!fs.existsSync(resultPath)) {
-              console.log('No result.json found, skipping');
-              return;
-            }
-
-            const result = JSON.parse(fs.readFileSync(resultPath, 'utf8'));
-
-            // Validate artifact data (treat as untrusted)
-            const prNumber = Number(result.pr_number);
-            const totalChanges = Number(result.total_changes);
-            const sizeLabel = String(result.size_label);
-            const labelColor = String(result.label_color);
-            const hasReviewGuidance = Boolean(result.has_review_guidance);
-
-            if (!prNumber || !sizeLabel.startsWith('Size ')) {
-              core.setFailed(`Invalid artifact data: pr=${prNumber}, label=${sizeLabel}`);
-              return;
-            }
-
-            console.log(`PR #${prNumber}: ${totalChanges} LOC, ${sizeLabel}`);
-
-            // --- Size label (idempotent) ---
-            const existingLabels = (await github.rest.issues.listLabelsOnIssue({
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              issue_number: prNumber,
-            })).data.map(l => l.name);
-
-            const existingSizeLabels = existingLabels.filter(l => l.startsWith('Size '));
-            const alreadyCorrect = existingSizeLabels.length === 1 && existingSizeLabels[0] === sizeLabel;
-
-            if (!alreadyCorrect) {
-              for (const label of existingSizeLabels) {
-                await github.rest.issues.removeLabel({
-                  owner: context.repo.owner,
-                  repo: context.repo.repo,
-                  issue_number: prNumber,
-                  name: label,
-                });
-              }
-
-              try {
-                await github.rest.issues.createLabel({
-                  owner: context.repo.owner,
-                  repo: context.repo.repo,
-                  name: sizeLabel,
-                  color: labelColor,
-                });
-              } catch (e) {
-                if (e.status !== 422) throw e;
-              }
-
-              await github.rest.issues.addLabels({
-                owner: context.repo.owner,
-                repo: context.repo.repo,
-                issue_number: prNumber,
-                labels: [sizeLabel],
-              });
-            }
-
-            // --- Large PR handling (400+ LOC) ---
-            if (totalChanges >= 400) {
-              if (!existingLabels.includes('large-pr')) {
-                try {
-                  await github.rest.issues.createLabel({
-                    owner: context.repo.owner,
-                    repo: context.repo.repo,
-                    name: 'large-pr',
-                    color: 'e99695',
-                  });
-                } catch (e) {
-                  if (e.status !== 422) throw e;
-                }
-
-                await github.rest.issues.addLabels({
-                  owner: context.repo.owner,
-                  repo: context.repo.repo,
-                  issue_number: prNumber,
-                  labels: ['large-pr'],
-                });
-              }
-
-              // Comment once with guidance
-              const MARKER = '<!-- pr-size-check -->';
-              const { data: comments } = await github.rest.issues.listComments({
-                owner: context.repo.owner,
-                repo: context.repo.repo,
-                issue_number: prNumber,
-              });
-
-              const alreadyCommented = comments.some(c => c.body.includes(MARKER));
-              if (!alreadyCommented) {
-                let body = `${MARKER}\n`;
-                body += `### :straight_ruler: PR Size: **${totalChanges} lines changed** (${sizeLabel})\n\n`;
-                body += `Please note: this PR exceeds the 400 LOC soft limit.\n`;
-                body += `- Consider **splitting** into separate PRs if the changes are separable\n`;
-                body += `- Ensure the PR description includes a **guided tour** in the "How to Review" section so reviewers know where to start\n`;
-
-                if (hasReviewGuidance) {
-                  body += `\n:white_check_mark: "How to Review" section appears to include guidance — thank you!\n`;
-                }
-
-                await github.rest.issues.createComment({
-                  owner: context.repo.owner,
-                  repo: context.repo.repo,
-                  issue_number: prNumber,
-                  body: body,
-                });
-              }
-            }
-
-            console.log(`PR #${prNumber}: labeled ${sizeLabel}, done`);
-defaults:
-  run:
-    shell: bash -euxo pipefail {0}