Commit 12d19f5313 for woocommerce

commit 12d19f531317b2dba3cce48e0aa958bae890e0cc
Author: Jorge A. Torres <jorge.torres@automattic.com>
Date:   Wed Jan 28 12:26:43 2026 +0000

    Update release tracking templates for clarity and various improvements to kickoff workflow (#62837)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index feba0bae22..4af43dcee7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -545,7 +545,7 @@ jobs:
           list-files: shell
           filters: |
             needs-markdown-linting:
-              - added|modified: '!(.github/**)/**/*.md'
+              - added|modified: '!(.github/**|.linear/**)/**/*.md'
               - added|modified: '*.md'
             needs-docs-build-validation:
               - 'docs/**/*.md'
diff --git a/.github/workflows/release-assignment.yml b/.github/workflows/release-assignment.yml
index 01a0c27cf0..24d3a6f8dc 100644
--- a/.github/workflows/release-assignment.yml
+++ b/.github/workflows/release-assignment.yml
@@ -5,7 +5,8 @@ on:
   workflow_dispatch:

 permissions:
-  contents: write
+  contents: read
+  issues: read

 jobs:
   check-upcoming-release-events:
@@ -28,108 +29,114 @@ jobs:
         uses: actions/github-script@v7
         with:
           script: |
-            const ical = require('node-ical');
+            const ical = require( 'node-ical' );

             const isManualTrigger = context.eventName === 'workflow_dispatch';
-            console.log(`Workflow triggered by: ${context.eventName}`);
+            console.log( `Workflow triggered by: ${ context.eventName }` );

             try {
               const today = new Date();
-              let targetDate = new Date(today);
-              targetDate.setDate(today.getDate() + (8 * 7)); // 8 weeks = 56 days
-
-              const events = await ical.async.fromURL(`https://calendar.google.com/calendar/ical/${{ secrets.RELEASE_CALENDAR_ID }}/public/basic.ics`);
-              const codeFreezeEvents = Object.values(events)
-                .filter(event => event.type === 'VEVENT')
-                .filter(event => {
-                  if (!event.start) return false;
+              let targetDate = new Date( today );
+              targetDate.setDate( today.getDate() + ( 8 * 7 ) ); // 8 weeks = 56 days.
+
+              const events = await ical.async.fromURL( `https://calendar.google.com/calendar/ical/${{ secrets.RELEASE_CALENDAR_ID }}/public/basic.ics` );
+              const codeFreezeEvents = Object.values( events )
+                .filter( ( event ) => event.type === 'VEVENT' )
+                .filter( ( event ) => {
+                  if ( ! event.start ) return false;
                   const pattern = /^WooCommerce \d+\.\d+ Feature Freeze$/i;
-                  return pattern.test(event.summary || '');
-                });
+                  return pattern.test( event.summary || '' );
+                } );

-              const eventsInRange = codeFreezeEvents.filter(event => {
-                const eventDate = new Date(event.start);
-                const diffTime = Math.abs(eventDate - targetDate);
-                const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
+              const eventsInRange = codeFreezeEvents.filter( ( event ) => {
+                const eventDate = new Date( event.start );
+                const diffTime = Math.abs( eventDate - targetDate );
+                const diffDays = Math.ceil( diffTime / ( 1000 * 60 * 60 * 24 ) );

-                if (isManualTrigger) {
-                  // For manual triggers, include events within 2 weeks of the target date
+                if ( isManualTrigger ) {
+                  // For manual triggers, include events within 2 weeks of the target date.
                   return diffDays <= 14;
                 } else {
-                  // For scheduled triggers, include events within 1 day of 8 weeks
+                  // For scheduled triggers, include events within 1 day of 8 weeks.
                   return diffDays <= 1;
                 }
-              });
-
-              if (eventsInRange.length > 0) {
-                const date = eventsInRange[0].start.toISOString();
-                const version = eventsInRange[0].summary.match(/\d+\.\d+/)[0];
-                console.log(`Found Feature Freeze event: ${eventsInRange[0].summary}`);
-                console.log(`Date: ${date}`);
-                console.log(`Version: ${version}`);
-                core.setOutput('should-trigger-webhook', 'true');
-                core.setOutput('version', version);
-                core.setOutput('feature-freeze-date', date);
+              } );
+
+              if ( eventsInRange.length > 0 ) {
+                const date = eventsInRange[ 0 ].start.toISOString();
+                const version = eventsInRange[ 0 ].summary.match( /\d+\.\d+/ )[ 0 ];
+                console.log( `Found Feature Freeze event: ${ eventsInRange[ 0 ].summary }` );
+                console.log( `Date: ${ date }` );
+                console.log( `Version: ${ version }` );
+                core.setOutput( 'should-trigger-webhook', 'true' );
+                core.setOutput( 'version', version );
+                core.setOutput( 'feature-freeze-date', date );
               } else {
-                console.log(`No Feature Freeze events found in the specified range.`);
-                core.setOutput('should-trigger-webhook', 'false');
+                console.log( 'No Feature Freeze events found in the specified range.' );
+                core.setOutput( 'should-trigger-webhook', 'false' );
               }
-            } catch (error) {
-              core.setFailed(`Failed to fetch calendar events: ${error.message}`);
+            } catch ( error ) {
+              core.setFailed( `Failed to fetch calendar events: ${ error.message }` );
             }

       - name: Get all events for version
+        if: ${{ steps.check-code-freeze-8-weeks.outputs.version != '' }}
         id: get-all-events-for-version
         uses: actions/github-script@v7
         with:
           script: |
-            const ical = require('node-ical');
+            const ical = require( 'node-ical' );
             const version = '${{ steps.check-code-freeze-8-weeks.outputs.version }}';

             const releaseEvents = {
-              'beta-1': `WooCommerce ${version} Beta 1`,
-              'beta-2': `WooCommerce ${version} Beta 2`,
-              'rc-1': `WooCommerce ${version} RC 1`,
-              final: `WooCommerce ${version} Release`,
+              'beta-1': `WooCommerce ${ version } Beta 1`,
+              'beta-2': `WooCommerce ${ version } Beta 2`,
+              'rc-1': `WooCommerce ${ version } RC 1`,
+              final: `WooCommerce ${ version } Release`
             };

             const events = await ical.async.fromURL(
               `https://calendar.google.com/calendar/ical/${{ secrets.RELEASE_CALENDAR_ID }}/public/basic.ics`
             );

-            const calendarEvents = Object.values(events)
-              .filter(event => event.type === 'VEVENT')
-              .filter(event => event.summary && event.start);
+            const calendarEvents = Object.values( events )
+              .filter( ( event ) => event.type === 'VEVENT' )
+              .filter( ( event ) => event.summary && event.start );

             let teamName = null;

-            for (const [eventKey, eventTitle] of Object.entries(releaseEvents)) {
-              const matchingEvent = calendarEvents.find(event =>
+            for ( const [ eventKey, eventTitle ] of Object.entries( releaseEvents ) ) {
+              const matchingEvent = calendarEvents.find( ( event ) =>
                 event.summary.trim() === eventTitle
               );

-              const date = matchingEvent ? new Date(matchingEvent.start).toISOString().split('T')[0] : null;
-              core.setOutput(`event-${eventKey}-date`, date);
-              console.log(`Event: ${eventTitle}, Date: ${date}`);
+              const date = matchingEvent ? new Date( matchingEvent.start ).toISOString().split( 'T' )[ 0 ] : null;
+              core.setOutput( `event-${ eventKey }-date`, date );
+              console.log( `Event: ${ eventTitle }, Date: ${ date }` );

-              // Extract team name from final release event description
-              if (eventKey === 'final' && matchingEvent && matchingEvent.description) {
-                const teamMatch = matchingEvent.description.match(/Team:\s*(.+)/i);
-                if (teamMatch && teamMatch[1]) {
-                  teamName = teamMatch[1].trim().toLowerCase().replace(/\s+/g, '-');
-                  console.log(`Team name from the event description: ${teamName}`);
+              // Extract team name from final release event description.
+              if ( eventKey === 'final' && matchingEvent && matchingEvent.description ) {
+                const teamMatch = matchingEvent.description.match( /Team:\s*(.+)/i );
+                if ( teamMatch && teamMatch[ 1 ] ) {
+                  teamName = teamMatch[ 1 ].trim().toLowerCase().replace( /\s+/g, '-' );
+                  console.log( `Team name from the event description: ${ teamName }` );
                 }
               }
             }
-
-            core.setOutput('team-name', teamName);
-
+
+            if ( ! teamName ) {
+              core.setFailed( 'Team name not found in final release event description. Expected "Team: <name>" in event description.' );
+              return;
+            }
+            core.setOutput( 'team-name', teamName );
+
   trigger-upcoming-code-freeze-events:
     name: Assign a release lead
-    needs: check-upcoming-release-events
+    needs: [check-upcoming-release-events]
     if: ${{ needs.check-upcoming-release-events.outputs.should-trigger-webhook == 'true' }}
     runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
     outputs:
+      create-tracking-issues: ${{ steps.trigger-upcoming-code-freeze-events.outputs.create-tracking-issues }}
       post: ${{ steps.trigger-upcoming-code-freeze-events.outputs.post }}
       release-team: ${{ steps.trigger-upcoming-code-freeze-events.outputs.release-team }}
       release-lead: ${{ steps.trigger-upcoming-code-freeze-events.outputs.release-lead }}
@@ -140,7 +147,7 @@ jobs:
         id: trigger-upcoming-code-freeze-events
         with:
           script: |
-            const crypto = require('crypto');
+            const crypto = require( 'crypto' );

             const payload = {
               action: 'release-assignment',
@@ -150,50 +157,51 @@ jobs:
               status: process.env.ENVIRONMENT === 'production' ? 'publish' : 'draft'
             };

-            const requestBody = JSON.stringify({
+            const requestBody = JSON.stringify( {
               payload
-            });
+            } );

-            const hmac = crypto.createHmac('sha256', process.env.WPCOM_WEBHOOK_SECRET);
-            hmac.update(requestBody);
-            const signature = hmac.digest('hex');
+            const hmac = crypto.createHmac( 'sha256', process.env.WPCOM_WEBHOOK_SECRET );
+            hmac.update( requestBody );
+            const signature = hmac.digest( 'hex' );

-            const response = await fetch(process.env.WPCOM_RELEASE_WEBHOOK_URL, {
+            const response = await fetch( process.env.WPCOM_RELEASE_WEBHOOK_URL, {
               method: 'POST',
               headers: {
                 'Content-Type': 'application/json',
-                'X-Hub-Signature-256': `sha256=${signature}`
+                'X-Hub-Signature-256': `sha256=${ signature }`
               },
               body: requestBody
-            });
+            } );

-            if (!response.ok) {
+            if ( ! response.ok ) {
               const status = response.status;
-              let errorMessage = `Request failed with status ${status}`;
+              let errorMessage = `Request failed with status ${ status }`;

-              switch (status) {
+              switch ( status ) {
                 case 409:
-                  errorMessage = `Post already exists for this release (${status})`;
-                  console.log(errorMessage);
+                  errorMessage = `Post already exists for this release (${ status })`;
+                  console.log( errorMessage );
                   break;
                 case 403:
-                  errorMessage = `Invalid webhook secret or unauthorized access (${status})`;
-                  core.setFailed(errorMessage);
+                  errorMessage = `Invalid webhook secret or unauthorized access (${ status })`;
+                  core.setFailed( errorMessage );
                   break;
                 default:
-                  core.setFailed(errorMessage);
+                  core.setFailed( errorMessage );
                   break;
               }
             } else {
-              console.log('Successfully triggered upcoming release notification webhook');
+              console.log( 'Successfully triggered upcoming release notification webhook' );

               const responseBody = await response.json();
+              core.setOutput( 'create-tracking-issues', 'true' );

-              core.setOutput('release-team', responseBody.data.release_team);
-              core.setOutput('release-lead', responseBody.data.release_lead);
-              core.setOutput('release-lead-google-login', responseBody.data.release_lead_google_login);
-              core.setOutput('release-lead-slack-member-id', responseBody.data.release_lead_slack_member_id);
-              core.setOutput('post', responseBody.data.post.view_url);
+              core.setOutput( 'release-team', responseBody.data.release_team );
+              core.setOutput( 'release-lead', responseBody.data.release_lead );
+              core.setOutput( 'release-lead-google-login', responseBody.data.release_lead_google_login );
+              core.setOutput( 'release-lead-slack-member-id', responseBody.data.release_lead_slack_member_id );
+              core.setOutput( 'post', responseBody.data.post.view_url );
             }
         env:
           ENVIRONMENT: ${{ secrets.ENVIRONMENT }}
@@ -202,10 +210,8 @@ jobs:

   send-slack-notification:
     name: Send Slack notification
-    needs:
-      - trigger-upcoming-code-freeze-events
-      - check-upcoming-release-events
-    if: ${{ needs.check-upcoming-release-events.outputs.should-trigger-webhook == 'true' && needs.trigger-upcoming-code-freeze-events.outputs.post != '' }}
+    needs: [trigger-upcoming-code-freeze-events, check-upcoming-release-events, create-parent-tracking-issue, create-sub-tracking-issues]
+    if: ${{ needs.trigger-upcoming-code-freeze-events.outputs.create-tracking-issues == 'true' }}
     runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
     steps:
       - name: Send Slack notification
@@ -220,27 +226,16 @@ jobs:
             The team responsible for this release is *Team ${{ needs.check-upcoming-release-events.outputs.team-name }}*.
             <@${{ needs.trigger-upcoming-code-freeze-events.outputs.release-lead-slack-member-id }}>, make sure you choose a release DRI and update the release P2 accordingly.

-            :p2: <${{ needs.trigger-upcoming-code-freeze-events.outputs.post }}|Release P2 post>
+            :p2: <${{ needs.trigger-upcoming-code-freeze-events.outputs.post }}|Release P2 post> | :linear-logo: <${{ needs.create-parent-tracking-issue.outputs.issue-url }}|Linear tracking issue>

-  create-release-kickoff:
-    name: Create Release Kickoff in Linear
-    needs:
-      - trigger-upcoming-code-freeze-events
-      - check-upcoming-release-events
-    if: ${{ needs.check-upcoming-release-events.outputs.should-trigger-webhook == 'true' }}
+  create-parent-tracking-issue:
+    name: Create Parent Tracking Issue in Linear
+    needs: [trigger-upcoming-code-freeze-events, check-upcoming-release-events]
+    if: ${{ needs.trigger-upcoming-code-freeze-events.outputs.create-tracking-issues == 'true' }}
     runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
-    env:
-      RELEASE_LEAD_GOOGLE_LOGIN: ${{ needs.trigger-upcoming-code-freeze-events.outputs.release-lead-google-login }}
-      RELEASE_LEAD: ${{ needs.trigger-upcoming-code-freeze-events.outputs.release-lead }}
-      RELEASE_TEAM: ${{ needs.trigger-upcoming-code-freeze-events.outputs.release-team }}
-      POST: ${{ needs.trigger-upcoming-code-freeze-events.outputs.post }}
-      RELEASE_VERSION: ${{ needs.check-upcoming-release-events.outputs.version }}
-      FEATURE_FREEZE_DATE: ${{ needs.check-upcoming-release-events.outputs.feature-freeze-date }}
-      BETA_1_RELEASE_DATE: ${{ needs.check-upcoming-release-events.outputs.beta-1-release-date }}
-      BETA_2_RELEASE_DATE: ${{ needs.check-upcoming-release-events.outputs.beta-2-release-date }}
-      RC1_RELEASE_DATE: ${{ needs.check-upcoming-release-events.outputs.rc-1-release-date }}
-      RELEASE_DATE: ${{ needs.check-upcoming-release-events.outputs.final-release-date }}
-      MILESTONE_NUMBER: ${{ needs.check-upcoming-release-events.outputs.version }}.0
+    outputs:
+      sub-issues-matrix: ${{ steps.build-matrix.outputs.matrix }}
+      issue-url: ${{ steps.check-existing-issue.outputs.issue-url || steps.create-tracking-issue.outputs.issue-url }}
     steps:
       - name: Checkout repository (sparse)
         uses: actions/checkout@v4
@@ -250,179 +245,235 @@ jobs:
           sparse-checkout-cone-mode: false

       - name: Install linear-sdk
-        run: npm install @linear/sdk
+        run: npm install @linear/sdk@71.0.0
+
+      - name: Check for existing release tracking issue
+        id: check-existing-issue
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const { LinearClient } = require( '@linear/sdk' );
+            const linearClient = new LinearClient( {
+              apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+            } );
+
+            const releaseMainVersion = '${{ needs.check-upcoming-release-events.outputs.version }}';
+            const expectedTitle = `[${ releaseMainVersion }] Release tracking`;
+
+            console.log( `Checking for existing issue with title: "${ expectedTitle }"` );
+
+            const issues = await linearClient.issues( {
+              filter: {
+                team: { id: { eq: '${{ secrets.LINEAR_TEAM_ID }}' } },
+                title: { eq: expectedTitle }
+              }
+            } );
+
+            if ( issues.nodes.length > 0 ) {
+              console.log( 'Found existing release tracking issue' );
+              core.setOutput( 'exists', 'true' );
+              core.setOutput( 'issue-url', issues.nodes[ 0 ].url );
+            } else {
+              console.log( 'No existing release tracking issue found' );
+              core.setOutput( 'exists', 'false' );
+            }

       - name: Get Linear data
+        if: ${{ steps.check-existing-issue.outputs.exists != 'true' }}
         id: get-linear-data
-        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
+        uses: actions/github-script@v7
         with:
           script: |
-            const { LinearClient } = require("@linear/sdk");
-            const linearClient = new LinearClient({
-              apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}',
-            });
+            const { LinearClient } = require( '@linear/sdk' );
+            const linearClient = new LinearClient( {
+              apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+            } );

             const organization = await linearClient.organization;

-            const labels = await linearClient.issueLabels({
+            const labels = await linearClient.issueLabels( {
               filter: {
                 name: { eq: 'Release' }
               }
-            });
+            } );

-            const states = await linearClient.workflowStates({
+            const states = await linearClient.workflowStates( {
               filter: {
                 team: { id: { eq: '${{ secrets.LINEAR_TEAM_ID }}' } },
                 name: { eq: 'In Progress' }
               }
-            });
+            } );

-            const releaseLabelId = labels.nodes.length > 0 ? labels.nodes[0].id : null;
-            const inProgressStateId = states.nodes.length > 0 ? states.nodes[0].id : null;
+            const releaseLabelId = labels.nodes.length > 0 ? labels.nodes[ 0 ].id : null;
+            const inProgressStateId = states.nodes.length > 0 ? states.nodes[ 0 ].id : null;

-            core.setOutput('releaseLabelId', releaseLabelId);
-            core.setOutput('inProgressStateId', inProgressStateId);
-            core.setOutput('organizationUrlKey', organization.urlKey);
+            core.setOutput( 'releaseLabelId', releaseLabelId );
+            core.setOutput( 'inProgressStateId', inProgressStateId );
+            core.setOutput( 'organizationUrlKey', organization.urlKey );

-      - name: Process release kickoff templates
-        id: process-kickoff-templates
-        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
+      - name: Get Linear user from email
+        if: ${{ steps.check-existing-issue.outputs.exists != 'true' }}
+        id: get-linear-user
+        uses: actions/github-script@v7
         with:
           script: |
-            const { LinearClient } = require("@linear/sdk");
-            const fs = require('fs');
-            const path = require('path');
-
-            const templates = {
-              'parent': '.linear/release-kickoff.md',
-              'beta-1': '.linear/release-kickoff-beta.md',
-              'beta-2': '.linear/release-kickoff-beta.md',
-              'rc-1': '.linear/release-kickoff-rc.md',
-              'final': '.linear/release-kickoff-patch.md'
-            };
+            const { LinearClient } = require( '@linear/sdk' );
+            const linearClient = new LinearClient( {
+              apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+            } );

-            let templateContents = {};
-            for (const [key, templatePath] of Object.entries(templates)) {
-              let templateContent = '';
-
-              try {
-                templateContent = fs.readFileSync(templatePath, 'utf8');
-              } catch (error) {
-                core.setFailed(`Failed to read template file: ${error.message}`);
-                return;
-              }
-
-              templateContents[key] = templateContent;
-            }
-
-            const linearClient = new LinearClient({
-              apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}',
-            });
-
-            const email = process.env.RELEASE_LEAD_GOOGLE_LOGIN || '';
-            const users = await linearClient.users({
+            const email = '${{ needs.trigger-upcoming-code-freeze-events.outputs.release-lead-google-login }}';
+            const users = await linearClient.users( {
               filter: {
                 email: { eq: email }
               }
-            });
+            } );

-            const userId = users.nodes.length ? users.nodes[0].id : null;
-            const displayName = users.nodes.length ? users.nodes[0].displayName : null;
-            const mention = displayName ? `https://linear.app/${{ steps.get-linear-data.outputs.organizationUrlKey }}/profiles/${displayName}` : null;
+            const userId = users.nodes.length ? users.nodes[ 0 ].id : null;
+            const displayName = users.nodes.length ? users.nodes[ 0 ].displayName : null;
+            const username = users.nodes.length ? users.nodes[ 0 ].username : null;

-            const replacements = {
-              '{RELEASE_VERSION}': process.env.RELEASE_VERSION || '',
-              '{MILESTONE_NUMBER}': process.env.MILESTONE_NUMBER || '',
-              '{RELEASE_LEAD}': mention || process.env.RELEASE_LEAD || '',
-              '{RELEASE_TEAM}': process.env.RELEASE_TEAM || '',
-              '{BETA_1_RELEASE_DATE}': process.env.BETA_1_RELEASE_DATE || '',
-              '{BETA_2_RELEASE_DATE}': process.env.BETA_2_RELEASE_DATE || '',
-              '{RC1_RELEASE_DATE}': process.env.RC1_RELEASE_DATE || '',
-              '{RELEASE_DATE}': process.env.RELEASE_DATE || '',
-              '{FEATURE_FREEZE_DATE}': process.env.FEATURE_FREEZE_DATE || '',
-              '{PATCH_VERSION}': 0,
-            };
+            console.log( userId ? 'Found Linear user for assignee' : 'No Linear user found for assignee' );

-            let processedTemplates = {};
-            for (const [key, content] of Object.entries(templateContents)) {
-              const lines = content.split('\n');
-              const title = lines[0].slice(2).trim();
-              const body = lines.slice(1).join('\n').trim();
+            core.setOutput( 'assignee-id', userId || '' );
+            core.setOutput( 'assignee-display-name', displayName || '' );
+            core.setOutput( 'assignee-username', username || '' );

-              let processedTitle = title;
-              let processedBody = body;
+      - name: Process template and create release tracking issue
+        if: ${{ steps.check-existing-issue.outputs.exists != 'true' }}
+        id: create-tracking-issue
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const { LinearClient } = require( '@linear/sdk' );
+            const fs = require( 'fs' );

-              if (key.includes('beta')) {
-                replacements['{BETA_PATCH_VERSION}'] = key.split('-')[1] || '';
-              } else if (key.includes('rc')) {
-                replacements['{RC_PATCH_VERSION}'] = key.split('-')[1] || '';
-              }
+            const linearClient = new LinearClient( {
+              apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+            } );

-              for (const [placeholder, value] of Object.entries(replacements)) {
-                processedTitle = processedTitle.replaceAll(placeholder, value);
-                processedBody = processedBody.replaceAll(placeholder, value);
-              }
+            // Read template file.
+            const templatePath = '.linear/release-kickoff.md';
+            let templateContent;

-              processedTemplates[key] = {
-                title: processedTitle,
-                body: processedBody
-              };
+            try {
+              templateContent = fs.readFileSync( templatePath, 'utf8' );
+            } catch ( error ) {
+              core.setFailed( `Failed to read template file: ${ error.message }` );
+              return;
             }

-            core.setOutput('processed-templates', JSON.stringify(processedTemplates));
-            core.setOutput('userId', userId || '');
+            // Parse template (first line is title with # prefix).
+            const lines = templateContent.split( '\n' );
+            const titleTemplate = lines[ 0 ].slice( 2 ).trim();
+            const bodyTemplate = lines.slice( 1 ).join( '\n' ).trim();

-      - name: Create Release Kickoff Linear Issue
-        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
-        with:
-          script: |
-            const { LinearClient } = require("@linear/sdk");
+            // Prepare placeholder values.
+            const username = '${{ steps.get-linear-user.outputs.assignee-username }}';
+            const organizationUrlKey = '${{ steps.get-linear-data.outputs.organizationUrlKey }}';
+            const mention = username ? `https://linear.app/${ organizationUrlKey }/profiles/${ username }` : null;

-            const linearClient = new LinearClient({
-              apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}',
-            });
+            const releaseMainVersion = '${{ needs.check-upcoming-release-events.outputs.version }}';
+            const releaseMilestone = `${ releaseMainVersion }.0`;
+            const releaseBranch = `release/${ releaseMainVersion }`;

-            const { parent, ...subIssues } = ${{ steps.process-kickoff-templates.outputs.processed-templates }};
+            const featureFreezeDate = '${{ needs.check-upcoming-release-events.outputs.feature-freeze-date }}';

-            let parentIssue;
+            const repositoryUrl = `https://github.com/${ context.repo.owner }/${ context.repo.repo }`;
+            const replacements = {
+              '{repository_url}': repositoryUrl,
+              '{release_main_version}': releaseMainVersion,
+              '{release_milestone}': releaseMilestone,
+              '{release_branch}': releaseBranch,
+              '{release_version}': releaseMilestone,
+              '{release_date}': '',
+              '{release_type}': '',
+              '{release_monitoring_time}': '',
+              '{lead_user}': mention || '${{ needs.trigger-upcoming-code-freeze-events.outputs.release-lead }}',
+              '{lead_team}': '${{ needs.trigger-upcoming-code-freeze-events.outputs.release-team }}',
+              '{date_feature_freeze}': featureFreezeDate ? featureFreezeDate.split( 'T' )[ 0 ] : '',
+              '{date_beta1}': '${{ needs.check-upcoming-release-events.outputs.beta-1-release-date }}',
+              '{date_beta2}': '${{ needs.check-upcoming-release-events.outputs.beta-2-release-date }}',
+              '{date_rc1}': '${{ needs.check-upcoming-release-events.outputs.rc-1-release-date }}',
+              '{date_stable}': '${{ needs.check-upcoming-release-events.outputs.final-release-date }}'
+            };
+
+            // Apply replacements.
+            let title = titleTemplate;
+            let body = bodyTemplate;
+
+            for ( const [ placeholder, value ] of Object.entries( replacements ) ) {
+              title = title.replaceAll( placeholder, value );
+              body = body.replaceAll( placeholder, value );
+            }
+
+            // Create the Linear issue.
             const labelId = '${{ steps.get-linear-data.outputs.releaseLabelId }}' || null;
-            const assigneeId = '${{ steps.process-kickoff-templates.outputs.userId }}' || null;
+            const stateId = '${{ steps.get-linear-data.outputs.inProgressStateId }}' || null;
+            const assigneeId = '${{ steps.get-linear-user.outputs.assignee-id }}' || null;

             try {
-              const stateId = '${{ steps.get-linear-data.outputs.inProgressStateId }}' || null;
-
-              const parentResult = await linearClient.createIssue({
+              const parentResult = await linearClient.createIssue( {
                 teamId: '${{ secrets.LINEAR_TEAM_ID }}',
-                title: `${parent.title}`,
-                description: `${parent.body}`,
+                title: title,
+                description: body,
                 priority: 2,
-                ...(labelId && { labelIds: [labelId] }),
-                ...(stateId && { stateId }),
-                ...(assigneeId && { assigneeId })
-              });
-
-              parentIssue = await parentResult.issue;
-            } catch (error) {
-              core.setFailed(`Failed to create Linear issue: ${error.message}`);
-              return;
+                ...( labelId && { labelIds: [ labelId ] } ),
+                ...( stateId && { stateId } ),
+                ...( assigneeId && { assigneeId } )
+              } );
+
+              const parentIssue = await parentResult.issue;
+              console.log( 'Parent tracking issue created successfully' );
+              core.setOutput( 'issue-url', parentIssue.url );
+            } catch ( error ) {
+              core.setFailed( `Failed to create parent Linear issue: ${ error.message }` );
             }

-            if (parentIssue) {
-              for (const subIssue of Object.values(subIssues)) {
-                try {
-                  const subIssueResult = await linearClient.createIssue({
-                    teamId: `${{ secrets.LINEAR_TEAM_ID }}`,
-                    title: subIssue.title,
-                    description: subIssue.body,
-                    parentId: parentIssue.id,
-                    priority: 2,
-                    ...(labelId && { labelIds: [labelId] }),
-                    ...(assigneeId && { assigneeId })
-                  });
-                } catch (error) {
-                  console.log(`Failed to create sub-issue in Linear: ${error.message}`);
-                }
-              }
-            }
+      - name: Build sub-issues matrix
+        id: build-matrix
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const version = '${{ needs.check-upcoming-release-events.outputs.version }}';
+
+            const matrix = {
+              include: [
+                { version: `${ version }.0-beta.1`, releaseDate: '${{ needs.check-upcoming-release-events.outputs.beta-1-release-date }}' },
+                { version: `${ version }.0-beta.2`, releaseDate: '${{ needs.check-upcoming-release-events.outputs.beta-2-release-date }}' },
+                { version: `${ version }.0-rc.1`,   releaseDate: '${{ needs.check-upcoming-release-events.outputs.rc-1-release-date }}' },
+                { version: `${ version }.0`,        releaseDate: '${{ needs.check-upcoming-release-events.outputs.final-release-date }}' }
+              ]
+            };

-            console.log(`Release kickoff issue created: ${parentIssue?.url}`);
+            core.setOutput( 'matrix', JSON.stringify( matrix ) );
+
+  create-sub-tracking-issues:
+    name: Create sub-issue for tracking ${{ matrix.version }} release
+    needs: [create-parent-tracking-issue]
+    strategy:
+      matrix: ${{ fromJSON(needs.create-parent-tracking-issue.outputs.sub-issues-matrix) }}
+    uses: ./.github/workflows/release-create-tracking-issue.yml
+    with:
+      version: ${{ matrix.version }}
+      release-date: ${{ matrix.releaseDate }}
+      skip-branch-validation: true
+    secrets:
+      LINEAR_OAUTH_TOKEN: ${{ secrets.LINEAR_OAUTH_TOKEN }}
+      LINEAR_TEAM_ID: ${{ secrets.LINEAR_TEAM_ID }}
+
+  notify-on-failure:
+    name: Notify on failure
+    needs: [check-upcoming-release-events, trigger-upcoming-code-freeze-events, send-slack-notification, create-parent-tracking-issue, create-sub-tracking-issues]
+    if: ${{ failure() }}
+    runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
+    steps:
+      - name: Send failure notification to Slack
+        uses: archive/github-actions-slack@a62d71a4ea93e68cbdc37581166b0298bea512e9 # v2.10.0
+        with:
+          slack-bot-user-oauth-access-token: ${{ secrets.CODE_FREEZE_BOT_TOKEN }}
+          slack-channel: ${{ secrets.WOO_RELEASE_SLACK_CHANNEL }}
+          slack-optional-unfurl_links: false
+          slack-text: |
+            :x: Some steps in the Release Assignment workflow failed. Please check the logs for details and re-run if needed.
+            <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View workflow run>
diff --git a/.github/workflows/release-create-tracking-issue.yml b/.github/workflows/release-create-tracking-issue.yml
new file mode 100644
index 0000000000..c8c3f2b527
--- /dev/null
+++ b/.github/workflows/release-create-tracking-issue.yml
@@ -0,0 +1,311 @@
+name: 'Release: Create Tracking Issue'
+
+on:
+  workflow_dispatch:
+    inputs:
+      version:
+        description: 'Version (e.g., 9.5.0-beta.1, 9.5.0-rc.1, 9.5.0)'
+        required: true
+        type: string
+      release-date:
+        description: 'Release date (YYYY-MM-DD), defaults to today'
+        required: false
+        type: string
+
+  workflow_call:
+    inputs:
+      version:
+        description: 'Version (e.g., 9.5.0-beta.1, 9.5.0-rc.1, 9.5.0)'
+        required: true
+        type: string
+      release-date:
+        description: 'Release date (YYYY-MM-DD), defaults to today'
+        required: false
+        type: string
+      skip-branch-validation:
+        description: 'Skip release branch validation'
+        required: false
+        type: boolean
+        default: false
+    secrets:
+      LINEAR_OAUTH_TOKEN:
+        required: true
+      LINEAR_TEAM_ID:
+        required: true
+
+permissions:
+  contents: read
+  issues: read
+
+jobs:
+  create-tracking-issue:
+    name: Create Release Tracking Issue
+    runs-on: ${{ ( github.repository == 'woocommerce/woocommerce' && 'blacksmith-2vcpu-ubuntu-2404' ) || 'ubuntu-latest' }}
+    steps:
+      - name: Parse version and determine template
+        id: parse-version
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const version = '${{ inputs.version }}';
+
+            // Match X.Y.Z with optional pre-release suffix (beta.N or rc.N).
+            const match = version.match( /^(\d+\.\d+)\.(\d+)(?:-(beta|rc)\.(\d+))?$/ );
+
+            // Pre-release versions must have patch version 0.
+            if ( ! match || ( match[ 3 ] && match[ 2 ] !== '0' ) ) {
+              core.setFailed( `Invalid version format: ${ version }. Expected: X.Y.Z (stable) or X.Y.0-beta.N / X.Y.0-rc.N (pre-release)` );
+              return;
+            }
+
+            const mainVersion = match[ 1 ];
+            const releaseType = match[ 3 ] || 'patch';
+            const templateFile = `.linear/release-kickoff-${ releaseType }.md`;
+            const monitoringTime = releaseType === 'rc' ? '4' : ( releaseType === 'patch' ? '2' : '' );
+
+            const milestone = `${ mainVersion }.0`;
+            const branch = `release/${ mainVersion }`;
+
+            console.log( `Version: ${ version }` );
+            console.log( `Main version: ${ mainVersion }` );
+            console.log( `Release type: ${ releaseType }` );
+            console.log( `Milestone: ${ milestone }` );
+            console.log( `Branch: ${ branch }` );
+            console.log( `Template: ${ templateFile }` );
+
+            core.setOutput( 'main-version', mainVersion );
+            core.setOutput( 'release-type', releaseType );
+            core.setOutput( 'template-file', templateFile );
+            core.setOutput( 'milestone', milestone );
+            core.setOutput( 'branch', branch );
+            core.setOutput( 'monitoring-time', monitoringTime );
+
+      - name: Validate release branch exists
+        if: ${{ !inputs.skip-branch-validation }}
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const branchName = '${{ steps.parse-version.outputs.branch }}';
+
+            try {
+              await github.rest.repos.getBranch( {
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                branch: branchName
+              } );
+              console.log( `Branch '${ branchName }' exists` );
+            } catch ( error ) {
+              if ( error.status === 404 ) {
+                core.setFailed( `Release branch '${ branchName }' does not exist` );
+              } else {
+                throw error;
+              }
+            }
+
+      - name: Install linear-sdk
+        run: npm install @linear/sdk@71.0.0
+
+      - name: Check if tracking issue already exists
+        id: check-existing
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const { LinearClient } = require( '@linear/sdk' );
+            const linearClient = new LinearClient( {
+              apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+            } );
+
+            const mainVersion = '${{ steps.parse-version.outputs.main-version }}';
+            const version = '${{ inputs.version }}';
+            const specificTitle = `[${ mainVersion }] Release \`${ version }\``;
+
+            console.log( `Checking for existing issue: "${ specificTitle }"` );
+
+            const existingIssues = await linearClient.issues( {
+              filter: {
+                team: { id: { eq: '${{ secrets.LINEAR_TEAM_ID }}' } },
+                title: { eq: specificTitle }
+              }
+            } );
+
+            if ( existingIssues.nodes.length > 0 ) {
+              const existingUrl = existingIssues.nodes[ 0 ].url;
+              console.log( 'Tracking issue already exists' );
+              core.setOutput( 'issue-exists', 'true' );
+              core.setOutput( 'existing-url', existingUrl );
+
+              // Write to GitHub job summary.
+              await core.summary
+                .addRaw( `Linear tracking issue already exists: [${ specificTitle }](${ existingUrl })` )
+                .write();
+              return;
+            }
+
+            console.log( 'No existing tracking issue found, proceeding with creation' );
+            core.setOutput( 'issue-exists', 'false' );
+
+      - name: Checkout repository (sparse)
+        if: ${{ steps.check-existing.outputs.issue-exists != 'true' }}
+        uses: actions/checkout@v4
+        with:
+          path: repo
+          sparse-checkout: |
+            .linear
+          sparse-checkout-cone-mode: false
+
+      - name: Get Linear data
+        if: ${{ steps.check-existing.outputs.issue-exists != 'true' }}
+        id: get-linear-data
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const { LinearClient } = require( '@linear/sdk' );
+            const linearClient = new LinearClient( {
+              apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+            } );
+
+            const labels = await linearClient.issueLabels( {
+              filter: {
+                name: { eq: 'Release' }
+              }
+            } );
+
+            const releaseLabelId = labels.nodes.length > 0 ? labels.nodes[ 0 ].id : null;
+
+            console.log( releaseLabelId ? 'Found Release label' : 'Release label not found' );
+
+            core.setOutput( 'release-label-id', releaseLabelId || '' );
+
+      - name: Get parent tracking issue and extract assignee
+        if: ${{ steps.check-existing.outputs.issue-exists != 'true' }}
+        id: get-parent-issue
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const { LinearClient } = require( '@linear/sdk' );
+            const linearClient = new LinearClient( {
+              apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+            } );
+
+            const mainVersion = '${{ steps.parse-version.outputs.main-version }}';
+            const parentTitle = `[${ mainVersion }] Release tracking`;
+
+            console.log( `Looking for parent issue: "${ parentTitle }"` );
+
+            const parentIssues = await linearClient.issues( {
+              filter: {
+                team: { id: { eq: '${{ secrets.LINEAR_TEAM_ID }}' } },
+                title: { eq: parentTitle }
+              }
+            } );
+
+            if ( parentIssues.nodes.length === 0 ) {
+              core.setFailed( `Parent tracking issue '${ parentTitle }' does not exist in Linear` );
+              return;
+            }
+
+            const parentIssue = parentIssues.nodes[ 0 ];
+            console.log( 'Found parent issue' );
+
+            core.setOutput( 'parent-id', parentIssue.id );
+
+            const assignee = await parentIssue.assignee;
+            if ( assignee ) {
+              console.log( 'Parent issue has an assignee' );
+              core.setOutput( 'assignee-id', assignee.id );
+            } else {
+              console.log( 'Parent issue has no assignee' );
+              core.setOutput( 'assignee-id', '' );
+            }
+
+      - name: Process template and create Linear issue
+        if: ${{ steps.check-existing.outputs.issue-exists != 'true' }}
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const { LinearClient } = require( '@linear/sdk' );
+            const fs = require( 'fs' );
+
+            const linearClient = new LinearClient( {
+              apiKey: '${{ secrets.LINEAR_OAUTH_TOKEN }}'
+            } );
+
+            // Read template file.
+            const templateFile = 'repo/${{ steps.parse-version.outputs.template-file }}';
+            let templateContent;
+            try {
+              templateContent = fs.readFileSync( templateFile, 'utf8' );
+            } catch ( error ) {
+              core.setFailed( `Failed to read template file: ${ error.message }` );
+              return;
+            }
+
+            // Parse template (first line is title with # prefix).
+            const lines = templateContent.split( '\n' );
+            const title = lines[ 0 ].slice( 2 ).trim();
+            const body = lines.slice( 1 ).join( '\n' ).trim();
+
+            // Prepare placeholder values.
+            const version = '${{ inputs.version }}';
+            const mainVersion = '${{ steps.parse-version.outputs.main-version }}';
+            const milestone = '${{ steps.parse-version.outputs.milestone }}';
+            const branch = '${{ steps.parse-version.outputs.branch }}';
+            const releaseType = '${{ steps.parse-version.outputs.release-type }}';
+            const monitoringTime = '${{ steps.parse-version.outputs.monitoring-time }}';
+            const repositoryUrl = `https://github.com/${ context.repo.owner }/${ context.repo.repo }`;
+
+            // Release date: use input or default to today.
+            let releaseDate = '${{ inputs.release-date }}';
+            if ( ! releaseDate ) {
+              releaseDate = new Date().toISOString().split( 'T' )[ 0 ];
+            }
+
+            const replacements = {
+              '{repository_url}': repositoryUrl,
+              '{release_main_version}': mainVersion,
+              '{release_milestone}': milestone,
+              '{release_branch}': branch,
+              '{release_version}': version,
+              '{release_date}': releaseDate,
+              '{release_type}': releaseType,
+              '{release_monitoring_time}': monitoringTime
+            };
+
+            // Apply replacements.
+            let processedTitle = title;
+            let processedBody = body;
+
+            for ( const [ placeholder, value ] of Object.entries( replacements ) ) {
+              processedTitle = processedTitle.replaceAll( placeholder, value );
+              processedBody = processedBody.replaceAll( placeholder, value );
+            }
+
+            console.log( `Creating issue with title: "${ processedTitle }"` );
+
+            // Create the Linear issue.
+            const parentId = '${{ steps.get-parent-issue.outputs.parent-id }}' || '';
+            const labelId = '${{ steps.get-linear-data.outputs.release-label-id }}';
+            const assigneeId = '${{ steps.get-parent-issue.outputs.assignee-id }}' || '';
+
+            try {
+              const result = await linearClient.createIssue( {
+                teamId: '${{ secrets.LINEAR_TEAM_ID }}',
+                title: processedTitle,
+                description: processedBody,
+                priority: 2,
+                ...( parentId && { parentId } ),
+                ...( labelId && { labelIds: [ labelId ] } ),
+                ...( assigneeId && { assigneeId } )
+              } );
+
+              const issue = await result.issue;
+              console.log( 'Successfully created tracking issue' );
+
+              // Write to GitHub job summary.
+              await core.summary
+                .addRaw( `Linear tracking issue created: [${ processedTitle }](${ issue.url })` )
+                .write();
+            } catch ( error ) {
+              core.setFailed( `Failed to create Linear issue: ${ error.message }` );
+              return;
+            }
diff --git a/.linear/release-kickoff-beta.md b/.linear/release-kickoff-beta.md
index 6fe51fb027..6bb62112f5 100644
--- a/.linear/release-kickoff-beta.md
+++ b/.linear/release-kickoff-beta.md
@@ -1,63 +1,41 @@
-# WooCommerce {RELEASE_VERSION}-beta.{BETA_PATCH_VERSION}
+# [{release_main_version}] Release `{release_version}`

-This issue tracks the progress of a single WooCommerce core plugin release iteration that covers the publishing of a single patch version. Patch version in this context refers to `z` in a version following `x.y.z` where `z` can be any of `0-99`, `0-rc.[0-99]`, or `0-beta.[0-99]`.
+These are the instructions for releasing `{release_version}`, scheduled for `{release_date}`.

-## Version being released {RELEASE_VERSION}-beta.{BETA_PATCH_VERSION}
+----

-The following details are copied from the official [Building and Publishing guide](https://developer.woocommerce.com/docs/contribution/releases/building-and-publishing/). Please check it to make sure these instructions haven't become out of date.
-Check the [Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/) page for any issues you may encounter.
+Perform all the steps below in order. When running _any_ GitHub workflow, ensure you do it from the `trunk` branch (the default) and input the release version or branch as indicated.

-## Pre-Checks
+Keep the _[Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/)_ guide handy, in case you encounter any issues.

-- [ ] **Verify no open [Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Aopen+is%3Apr) or [Issues](https://github.com/woocommerce/woocommerce/issues)** for the milestone matching the release being published.
-    - All pull requests tied to the release milestone must be closed, including [backported pull requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Apr+label%3A%22type%3A+cherry-pick%22) that may need to be merged into other release branches or trunk.
-- [ ] **Check for unresolved ["cherry pick failed" Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is:pr+label:%22cherry+pick+failed%22).**
-    - Ensure any such PRs are either expected or manually resolved via another PR.
-- [ ] **Confirm the Stable tag in** `readme.txt` **matches [trunk on WordPress.org](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).**
-    - The value should match the current stable version, not the version being built.
-- [ ] **Ensure [GitHub services](https://www.githubstatus.com/) are fully operational**
+----

-## Build WooCommerce
+### 1. Pre-build checks

-1. **Run the ["Release: Bump version number" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-bump-version.yml).**
-   - Run from `trunk`.
-   - Choose the type of version you're releasing (`beta`, `rc`, or `stable`).
-   - Enter as *Release branch* the branch you are about to release from (e.g. `release/10.0`).
-   - Review and merge the PR created.
+- [ ] Confirm [GitHub services](https://www.githubstatus.com/) are operational.
+- [ ] Verify no open issues or pull requests exist against the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}). Ping authors as needed to merge or close.
+- [ ] Ensure that there aren't any pull requests [with label "cherry pick failed"]({repository_url}/pulls?q=is:pr+label:%22cherry+pick+failed%22) that apply to this release that haven't been actioned.
+- [ ] Confirm the `Stable tag` value [in the readme.txt on the release branch]({repository_url}/blob/{release_branch}/plugins/woocommerce/readme.txt#L7) matches the one [on WordPress.org's `trunk`](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).

-2. **Run the ["Release: Compile changelog" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-compile-changelog.yml).**
-   - Run from `trunk` and enter the major version number and the intended release date.
-   - **Review and merge the two PRs created** (one for trunk, one for the release branch).
-   - **Ensure the changelog date is correct.**

-3. **Build the release ZIP file.**
-   - Build the release ZIP file using the ["Release: Build ZIP file" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-build-zip-file.yml).
-   - Run from `trunk` and enter the release branch as argument.
-   - Set "Create GitHub release" to `true`.
-   - The workflow will create a [draft release tag](https://github.com/woocommerce/woocommerce/releases) with an attached `woocommerce.zip` file.
+### 2. Build the release package

-## Publish the Release
+- [ ] Run workflow **[Release: Bump version number]({repository_url}/actions/workflows/release-bump-version.yml)**: enter `{release_branch}` as _Release branch_ and `{release_type}` as _Type of version bump to perform_.
+- [ ] Review and merge the PR that was generated against the release branch. Check the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Compile changelog]({repository_url}/actions/workflows/release-compile-changelog.yml)**: enter `{release_main_version}` as _Version_ and leave _Release date_ empty, except when building the package ahead of schedule.
+- [ ] Review and merge the PRs that were generated: one against `trunk` and another one against the release branch. Both should be under the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Build ZIP file]({repository_url}/actions/workflows/release-build-zip-file.yml)** to build the asset and create the GitHub release: enter `{release_branch}` as _Release branch_ and check _Create GitHub release_.
+- [ ] Confirm that a draft `{release_version}` release [was created in the repository]({repository_url}/releases) with an attached `woocommerce.zip` asset.

-### Step 1: Upload Release to WordPress.org

-- [ ] **Run the ["Release: Upload release to WordPress.org" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-upload-to-wporg.yml)** from `trunk` using the release tag.
-- [ ] **This creates a new [SVN tag](https://plugins.svn.wordpress.org/woocommerce/tags/) and, if the release is newer than trunk, overwrites trunk.**
+### 3. Upload the release to WordPress.org

-### Step 2: Approve the Release
+- [ ] Run workflow **[Release: Upload release to WordPress.org]({repository_url}/actions/workflows/release-upload-to-wporg.yml)**: enter `{release_version}` as _Release tag to upload_ and make sure to check off the confirmation box. This can take up to 40 mins.
+- [ ] Confirm that SVN tag `{release_version}` [exists on WordPress.org SVN](https://plugins.svn.wordpress.org/woocommerce/tags/{release_version}).
+- [ ] [Log into WordPress.org](https://wordpress.org/plugins/developers/releases/) using the credentials from the `WordPress.org "WooCommerce" user account` secret in the secret store and approve the release.
+- [ ] After a few minutes, confirm that [`{release_version}` is available for download](https://downloads.wordpress.org/plugin/woocommerce.{release_version}.zip).

-- [ ] **Visit [WordPress.org plugin releases](https://wordpress.org/plugins/developers/releases/) and approve the release.**
-- [ ] **Wait a few minutes for WordPress.org to build the new version.**

-### Step 3: Verify Release Availability
+### 4. Publish the release

-- [ ] **Confirm the new release appears at:**
-    - <https://plugins.svn.wordpress.org/woocommerce/tags/>
-    - The "Previous versions" dropdown on the [Advanced Options screen](https://wordpress.org/plugins/woocommerce/advanced/).
-
-## Update the Release Tags
-
-### Step 1: Publish GitHub Release Tag
-
-- [ ] **Action:** [Publish the previously created GitHub draft release tag](https://github.com/woocommerce/woocommerce/releases).
-- [ ] **When setting release status:**
-    - check "Set as a pre-release."
+- [ ] Publish the `{release_version}` [release draft]({repository_url}/releases) that was previously created.
diff --git a/.linear/release-kickoff-patch.md b/.linear/release-kickoff-patch.md
index 171e995954..4172e4e57b 100644
--- a/.linear/release-kickoff-patch.md
+++ b/.linear/release-kickoff-patch.md
@@ -1,88 +1,58 @@
-# WooCommerce {RELEASE_VERSION}.{PATCH_VERSION}
+# [{release_main_version}] Release `{release_version}`

-This issue tracks the progress of a single WooCommerce core plugin release iteration that covers the publishing of a single patch version. "Patch version" in this context refers to `z` in a version following `x.y.z` where `z` can be any of `0-99`, `0-rc.[0-99]`, or `0-beta.[0-99]`.
+These are the instructions for releasing `{release_version}`, scheduled for `{release_date}`.

-## Version being released {RELEASE_VERSION}.{PATCH_VERSION}
+----

-The following details are copied from the official [Building and Publishing guide](https://developer.woocommerce.com/docs/contribution/releases/building-and-publishing/). Please check it to make sure these instructions haven't become out of date.
-Check the [Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/) page for any issues you may encounter.
+Perform all the steps below in order. When running _any_ GitHub workflow, ensure you do it from the `trunk` branch (the default) and input the release version or branch as indicated.

-## Pre-Checks
+Keep the _[Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/)_ guide handy, in case you encounter any issues.

-- [ ] **Verify no open [Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Aopen+is%3Apr) or [Issues](https://github.com/woocommerce/woocommerce/issues)** for the milestone matching the release being published.
-    - All pull requests tied to the release milestone must be closed, including [backported pull requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Apr+label%3A%22type%3A+cherry-pick%22) that may need to be merged into other release branches or trunk.
-- [ ] **Check for unresolved ["cherry pick failed" Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is:pr+label:%22cherry+pick+failed%22).**
-    - Ensure any such PRs are either expected or manually resolved via another PR.
-- [ ] **Confirm the Stable tag in** `readme.txt` **matches [trunk on WordPress.org](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).**
-    - The value should match the current stable version, not the version being built.
-- [ ] **Ensure [GitHub services](https://www.githubstatus.com/) are fully operational**
+----

-## Build WooCommerce
+### 1. Pre-build checks

-1. **Run the ["Release: Bump version number" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-bump-version.yml).**
-   - Run from `trunk`.
-   - Choose the type of version you're releasing (`beta`, `rc`, or `stable`).
-   - Enter as *Release branch* the branch you are about to release from (e.g. `release/10.0`).
-   - Review and merge the PR created.
+- [ ] Confirm [GitHub services](https://www.githubstatus.com/) are operational.
+- [ ] Verify no open issues or pull requests exist against the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}). Ping authors as needed to merge or close.
+- [ ] Ensure that there aren't any pull requests [with label "cherry pick failed"]({repository_url}/pulls?q=is:pr+label:%22cherry+pick+failed%22) that apply to this release that haven't been actioned.
+- [ ] Confirm the `Stable tag` value [in the readme.txt on the release branch]({repository_url}/blob/{release_branch}/plugins/woocommerce/readme.txt#L7) matches the one [on WordPress.org's `trunk`](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).

-2. **Run the ["Release: Compile changelog" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-compile-changelog.yml).**
-   - Run from `trunk` and enter the major version number and the intended release date.
-   - **Review and merge the two PRs created** (one for trunk, one for the release branch).
-   - **Ensure the changelog date is correct.**

-3. **Build the release ZIP file.**
-   - Build the release ZIP file using the ["Release: Build ZIP file" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-build-zip-file.yml).
-   - Run from `trunk` and enter the release branch as argument.
-   - The workflow will create a [draft release tag](https://github.com/woocommerce/woocommerce/releases) with an attached `woocommerce.zip` file.
+### 2. Build the release package

-## Publish the Release
+- [ ] Run workflow **[Release: Bump version number]({repository_url}/actions/workflows/release-bump-version.yml)**: enter `{release_branch}` as _Release branch_ and `{release_type}` as _Type of version bump to perform_.
+- [ ] Review and merge the PR that was generated against the release branch. Check the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Compile changelog]({repository_url}/actions/workflows/release-compile-changelog.yml)**: enter `{release_main_version}` as _Version_ and leave _Release date_ empty, except when building the package ahead of schedule.
+- [ ] Review and merge the PRs that were generated: one against `trunk` and another one against the release branch. Both should be under the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Build ZIP file]({repository_url}/actions/workflows/release-build-zip-file.yml)** to build the asset and create the GitHub release: enter `{release_branch}` as _Release branch_ and check _Create GitHub release_.
+- [ ] Confirm that a draft `{release_version}` release [was created in the repository]({repository_url}/releases) with an attached `woocommerce.zip` asset.

-### Step 1: Upload Release to WordPress.org

-- [ ] **Run the ["Release: Upload release to WordPress.org" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-upload-to-wporg.yml)** from `trunk` using the release tag.
-- [ ] **This creates a new [SVN tag](https://plugins.svn.wordpress.org/woocommerce/tags/) and, if the release is newer than trunk, overwrites trunk.**
+### 3. Upload the release to WordPress.org

-### Step 2: Approve the Release
+- [ ] Run workflow **[Release: Upload release to WordPress.org]({repository_url}/actions/workflows/release-upload-to-wporg.yml)**: enter `{release_version}` as _Release tag to upload_ and make sure to check off the confirmation box.
+- [ ] Confirm that SVN tag `{release_version}` [exists on WordPress.org SVN](https://plugins.svn.wordpress.org/woocommerce/tags/{release_version}).
+- [ ] [Log into WordPress.org](https://wordpress.org/plugins/developers/releases/) using the credentials from the `WordPress.org "WooCommerce" user account` secret in the secret store and approve the release.
+- [ ] After a few minutes, confirm that [`{release_version}` is available for download](https://downloads.wordpress.org/plugin/woocommerce.{release_version}.zip).

-- [ ] **Visit [WordPress.org plugin releases](https://wordpress.org/plugins/developers/releases/) and approve the release.**
-- [ ] **Wait a few minutes for WordPress.org to build the new version.**

-### Step 3: Verify Release Availability
+### 4. Deploy to the staging environment

-- [ ] **Confirm the new release appears at:**
-    - <https://plugins.svn.wordpress.org/woocommerce/tags/>
-    - The "Previous versions" dropdown on the [Advanced Options screen](https://wordpress.org/plugins/woocommerce/advanced/).
+- [ ] Follow the [guide to deploy to the staging environment](https://wp.me/PCYsg-18BQ) and monitor for at least {release_monitoring_time} hours after deploy.

-## Release to the Staging Environment (Stable and RC releases)
+**If a critical issue was detected while monitoring...**

-- [ ] **Condition:** Only perform this step for stable and RC releases (`-rc.x` or `.x`).
-- [ ] **Action:** Follow the [guide to deploy to the staging environment](https://developer.woocommerce.com/docs/contribution/releases/building-and-publishing/).
+- [ ] Request a revert in the staging environment.
+- [ ] Pause the release process and **do not continue with any steps on this issue**. Follow the procedure in the [troubleshooting guide](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/#deploy-serious-bug) instead.

-## Update the Release Tags

-### Step 1: Update Stable Tag
+### 5. Publish the release

-- [ ] **Condition:** Only perform this step if:
-    - The release is a stable release, and
-    - No major issues were found during the release to staging.
-    - You have monitored the release following the ["Monitoring Deploys to WPCOM Atomic"](https://fieldguide.automattic.com/woocommerce-core-releases/woocommerce-core-releases-deploying-to-atomic-staging/#monitoring-deploys-atomic) guide for at least 4 hours for `.0` releases and at least 2 hours for other patch releases.
-- [ ] **Action:** Run the ["Release: Update stable tag" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-update-stable-tag.yml) from `trunk`, set the version, and select the option to update the stable tag as part of the workflow input.
-    - Review and merge the pull requests created (one for the release branch and trunk).
+- [ ] Run workflow **[Release: Update stable tag]({repository_url}/actions/workflows/release-update-stable-tag.yml)**: enter `{release_version}` as _Version_ and make sure to check off the confirmation box.
+- [ ] Publish the `{release_version}` [release draft]({repository_url}/releases) that was previously created, as well as any other `{release_main_version}` drafts that might exist from previous attempts. **Ensure** that "Set as the latest release" is checked for `{release_version}`.

-### Step 2: Publish GitHub Release Tag

-- [ ] **Action:** Publish the [previously created GitHub draft release tag](https://github.com/woocommerce/woocommerce/releases).
-- [ ] **When setting release status:**
-    - If releasing a dev, beta, or RC, check "Set as a pre-release."
-    - If the version was marked as stable in Step 1 above, check "Set as the latest release."
-    - If the version was not marked as stable in Step 1 above, do not set as the latest release.
+### 6. Post-release tasks

-### Step 3: Merge follow-up PRs
-
-- [ ] **Action:** Merge the "Update changelog.txt after {RELEASE_VERSION}.{PATCH_VERSION}" PR that is automatically created once the release tag is published on GitHub.
-
-## Post Release Monitoring
-
-For all RC and stable releases, the release lead should continue to monitor for any bugs directly related to the latest version. Monitoring should continue for 3 days after a major release and 1 day for a point release.
-
-See the [WooCommerce Release Monitoring Guide](https://developer.woocommerce.com/docs/contribution/releases/monitoring/) for more details.
+- [ ] Wait at least 1 hour for all automations to complete and make sure to merge any follow-up PRs under the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Continue monitoring for bugs related to the release for at least 3 days. See the [release monitoring guide](https://developer.woocommerce.com/docs/contribution/releases/monitoring/) for more details.
diff --git a/.linear/release-kickoff-rc.md b/.linear/release-kickoff-rc.md
index e2728360a4..b01afd6ef0 100644
--- a/.linear/release-kickoff-rc.md
+++ b/.linear/release-kickoff-rc.md
@@ -1,73 +1,51 @@
-# WooCommerce {RELEASE_VERSION}-rc.{RC_PATCH_VERSION}
+# [{release_main_version}] Release `{release_version}`

-This issue tracks the progress of a single WooCommerce core plugin release iteration that covers the publishing of a single patch version. Patch version in this context refers to `z` in a version following `x.y.z` where `z` can be any of `0-99`, `0-rc.[0-99]`, or `0-beta.[0-99]`.
+These are the instructions for releasing `{release_version}`, scheduled for `{release_date}`.

-## Version being released {RELEASE_VERSION}-RC.{RC_PATCH_VERSION}
+----

-The following details are copied from the official [Building and Publishing guide](https://developer.woocommerce.com/docs/contribution/releases/building-and-publishing/). Please check it to make sure these instructions haven't become out of date.
-Check the [Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/) page for any issues you may encounter.
+Perform all the steps below in order. When running _any_ GitHub workflow, ensure you do it from the `trunk` branch (the default) and input the release version or branch as indicated.

-## Pre-Checks
+Keep the _[Release Troubleshooting & Recovery](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/)_ guide handy, in case you encounter any issues.

-- [ ] **Verify no open [Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Aopen+is%3Apr) or [Issues](https://github.com/woocommerce/woocommerce/issues)** for the milestone matching the release being published.
-    - All pull requests tied to the release milestone must be closed, including [backported pull requests](https://github.com/woocommerce/woocommerce/pulls?q=is%3Apr+label%3A%22type%3A+cherry-pick%22) that may need to be merged into other release branches or trunk.
-- [ ] **Check for unresolved ["cherry pick failed" Pull Requests](https://github.com/woocommerce/woocommerce/pulls?q=is:pr+label:%22cherry+pick+failed%22).**
-    - Ensure any such PRs are either expected or manually resolved via another PR.
-- [ ] **Confirm the Stable tag in** `readme.txt` **matches [trunk on WordPress.org](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).**
-    - The value should match the current stable version, not the version being built.
-- [ ] **Ensure [GitHub services](https://www.githubstatus.com/) are fully operational**
+----

-## Build WooCommerce
+### 1. Pre-build checks

-1. **Run the ["Release: Bump version number" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-bump-version.yml).**
-   - Run from `trunk`.
-   - Choose the type of version you're releasing (`beta`, `rc`, or `stable`).
-   - Enter as *Release branch* the branch you are about to release from (e.g. `release/10.0`).
-   - Review and merge the PR created.
+- [ ] Confirm [GitHub services](https://www.githubstatus.com/) are operational.
+- [ ] Verify no open issues or pull requests exist against the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}). Ping authors as needed to merge or close.
+- [ ] Ensure that there aren't any pull requests [with label "cherry pick failed"]({repository_url}/pulls?q=is:pr+label:%22cherry+pick+failed%22) that apply to this release that haven't been actioned.
+- [ ] Confirm the `Stable tag` value [in the readme.txt on the release branch]({repository_url}/blob/{release_branch}/plugins/woocommerce/readme.txt#L7) matches the one [on WordPress.org's `trunk`](https://plugins.trac.wordpress.org/browser/woocommerce/trunk/readme.txt#L7).

-2. **Run the ["Release: Compile changelog" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-compile-changelog.yml).**
-   - Run from `trunk` and enter the major version number and the intended release date.
-   - **Review and merge the two PRs created** (one for trunk, one for the release branch).
-   - **Ensure the changelog date is correct.**

-3. **Build the release ZIP file.**
-   - Build the release ZIP file using the ["Release: Build ZIP file" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-build-zip-file.yml).
-   - Run from `trunk` and enter the release branch as argument.
-   - The workflow will create a [draft release tag](https://github.com/woocommerce/woocommerce/releases) with an attached `woocommerce.zip` file.
+### 2. Build the release package

-## Publish the Release
+- [ ] Run workflow **[Release: Bump version number]({repository_url}/actions/workflows/release-bump-version.yml)**: enter `{release_branch}` as _Release branch_ and `{release_type}` as _Type of version bump to perform_.
+- [ ] Review and merge the PR that was generated against the release branch. Check the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Compile changelog]({repository_url}/actions/workflows/release-compile-changelog.yml)**: enter `{release_main_version}` as _Version_ and leave _Release date_ empty, except when building the package ahead of schedule.
+- [ ] Review and merge the PRs that were generated: one against `trunk` and another one against the release branch. Both should be under the [{release_milestone} milestone]({repository_url}/issues?q=is:open+milestone:{release_milestone}).
+- [ ] Run workflow **[Release: Build ZIP file]({repository_url}/actions/workflows/release-build-zip-file.yml)** to build the asset and create the GitHub release: enter `{release_branch}` as _Release branch_ and check _Create GitHub release_.
+- [ ] Confirm that a draft `{release_version}` release [was created in the repository]({repository_url}/releases) with an attached `woocommerce.zip` asset.

-### Step 1: Upload Release to WordPress.org

-- [ ] **Run the ["Release: Upload release to WordPress.org" workflow](https://github.com/woocommerce/woocommerce/actions/workflows/release-upload-to-wporg.yml)** from `trunk` using the release tag.
-- [ ] **This creates a new [SVN tag](https://plugins.svn.wordpress.org/woocommerce/tags/) and, if the release is newer than trunk, overwrites trunk.**
+### 3. Upload the release to WordPress.org

-### Step 2: Approve the Release
+- [ ] Run workflow **[Release: Upload release to WordPress.org]({repository_url}/actions/workflows/release-upload-to-wporg.yml)**: enter `{release_version}` as _Release tag to upload_ and make sure to check off the confirmation box.
+- [ ] Confirm that SVN tag `{release_version}` [exists on WordPress.org SVN](https://plugins.svn.wordpress.org/woocommerce/tags/{release_version}).
+- [ ] [Log into WordPress.org](https://wordpress.org/plugins/developers/releases/) using the credentials from the `WordPress.org "WooCommerce" user account` secret in the secret store and approve the release.
+- [ ] After a few minutes, confirm that [`{release_version}` is available for download](https://downloads.wordpress.org/plugin/woocommerce.{release_version}.zip).

-- [ ] **Visit [WordPress.org plugin releases](https://wordpress.org/plugins/developers/releases/) and approve the release.**
-- [ ] **Wait a few minutes for WordPress.org to build the new version.**

-### Step 3: Verify Release Availability
+### 4. Deploy to the staging environment

-- [ ] **Confirm the new release appears at:**
-    - <https://plugins.svn.wordpress.org/woocommerce/tags/>
-    - The "Previous versions" dropdown on the [Advanced Options screen](https://wordpress.org/plugins/woocommerce/advanced/).
+- [ ] Follow the [guide to deploy to the staging environment](https://wp.me/PCYsg-18BQ) and monitor for at least {release_monitoring_time} hours after deploy.

-## Release to the Staging Environment (Stable and RC releases)
+**If a critical issue was detected while monitoring...**

-- [ ] **Condition:** Only perform this step for stable and RC releases (`-rc.x` or `.x`).
-- [ ] **Action:** Follow the [guide to deploy to the staging environment](https://wp.me/PCYsg-18BQ).
+- [ ] Request a revert in the staging environment.
+- [ ] Pause the release process and **do not continue with any steps on this issue**. Follow the procedure in the [troubleshooting guide](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/#deploy-serious-bug) instead.

-## Update the Release Tags

-### Step 1: Publish GitHub Release Tag
+### 5. Publish the release

-- [ ] **Action:** [Publish the previously created GitHub draft release tag](https://github.com/woocommerce/woocommerce/releases).
-- [ ] **When setting release status:**
-    - check "Set as a pre-release."
-
-## Post Release Monitoring
-
-For all RC and stable releases, the release lead should continue to monitor for any bugs directly related to the latest version. Monitoring should continue for 3 days after a major release and 1 day for a point release.
-
-See the [WooCommerce Release Monitoring Guide](https://developer.woocommerce.com/docs/contribution/releases/monitoring/) for more details.
+- [ ] Publish the `{release_version}` [release draft]({repository_url}/releases) that was previously created, as well as any other `{release_main_version}` drafts that might exist from previous attempts. **Do not** check "Set as the latest release".
diff --git a/.linear/release-kickoff.md b/.linear/release-kickoff.md
index 2b04b4df5c..2b01fdc301 100644
--- a/.linear/release-kickoff.md
+++ b/.linear/release-kickoff.md
@@ -1,18 +1,31 @@
-# WooCommerce Release {RELEASE_VERSION}: Tracking
+# [{release_main_version}] Release tracking

-This issue provides visibility on the progress of the release process of WooCommerce core **{RELEASE_VERSION}**, and it centralizes all related discussions and documentation. The ultimate goal of this issue is to keep a reference to the steps, resources, work, and conversations associated with this release so it can be helpful for future contributors handling the release of a new WooCommerce core version.
+This issue provides visibility on the progress of the release process of WooCommerce core **{release_main_version}**.

-• **WooCommerce core version to release:** {RELEASE_VERSION} ([milestone](https://github.com/woocommerce/woocommerce/milestones/{MILESTONE_NUMBER}))
-• **Release lead:** {RELEASE_LEAD} ({RELEASE_TEAM})
-• **Beta 1 Release Date:** {BETA_1_RELEASE_DATE}
-• **Beta 2 Release Date:** {BETA_2_RELEASE_DATE}
-• **RC 1 Release Date:** {RC1_RELEASE_DATE}
-• **Final Release Date:** {RELEASE_DATE} ([release calendar](https://developer.woocommerce.com/release-calendar/))
+- **Main version being released:** `{release_main_version}`
+- **Milestone:** [{release_milestone}]({repository_url}/issues?q=is:open+milestone:{release_milestone})
+- **Release branch:** [`{release_branch}`]({repository_url}/tree/{release_branch})
+- **Release lead:** {lead_user} ({lead_team})
+- **Relevant dates:** ([release calendar](https://developer.woocommerce.com/release-calendar/))
+  - **Feature Freeze:** {date_feature_freeze}
+  - **Beta 1:** {date_beta1}
+  - **Beta 2:** {date_beta2}
+  - **RC 1:** {date_rc1}
+  - **Stable:** {date_stable}

-## Resources
+---

-• **[WooCommerce core changelog](https://github.com/woocommerce/woocommerce/blob/trunk/changelog.txt)**
-• **[Previous releases](https://github.com/woocommerce/woocommerce/releases)**
-• **[Release process documentation](https://developer.woocommerce.com/docs/contribution/releases/)**
+⚠ Dear release lead:
+
+- Please read this issue carefully and familiarize yourself with the [release process documentation](https://developer.woocommerce.com/docs/contribution/releases/).
+- Join the release channels in Slack (`#woo-core-releases`, `#woo-core-releases-notifications`), where discussions happen and notifications are sent.
+- For every release in the cycle, there's a corresponding sub-issue. On the date of each release (see schedule above), open the relevant issue and follow the instructions in it.
+- Any additional point/patch releases after the first stable must be tracked as well. Run the **[Release: Create Tracking Issue]({repository_url}/actions/workflows/release-create-tracking-issue.yml)** workflow with the version (e.g., `{release_main_version}.1`) to create the sub-issue.
+
+##### Resources
+
+- [Release process documentation](https://developer.woocommerce.com/docs/contribution/releases/).
+- [Troubleshooting Guide](https://developer.woocommerce.com/docs/contribution/releases/troubleshooting/).
+- [Previous releases]({repository_url}/releases).
+- [WooCommerce core changelog]({repository_url}/blob/trunk/changelog.txt).

-Add any new Sub-issues as needed for any point releases (patch) needed after the `x.y.0` stable release using the [`.linear/release-kickoff-patch.md`](https://github.com/woocommerce/woocommerce/blob/trunk/.linear/release-kickoff-patch.md) template.