Skip to content

Commit de0e504

Browse files
committed
feat: Add auto-assignment workflow and issue management strategy
- Created auto-assign-copilot.yml workflow - Automatically assigns issues to Copilot when dependencies complete - Tracks dependency chain status on each PR merge - Created ISSUE_MANAGEMENT_STRATEGY.md documenting approach - Maps all 77 issues with clear dependency chains - Enables parallel content generation (9 issues ready) - Sequential domain/application/API layers (58 issues) - Estimated 18-week timeline for complete application
1 parent aba78fd commit de0e504

2 files changed

Lines changed: 466 additions & 0 deletions

File tree

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
name: Auto-Assign Issues to Copilot
2+
3+
on:
4+
pull_request:
5+
types: [closed]
6+
workflow_dispatch:
7+
inputs:
8+
issue_number:
9+
description: 'Manually trigger assignment for specific issue'
10+
required: false
11+
12+
jobs:
13+
auto-assign-dependencies:
14+
runs-on: ubuntu-latest
15+
if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
16+
17+
permissions:
18+
issues: write
19+
pull-requests: read
20+
contents: read
21+
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v4
25+
26+
- name: Get merged PR details
27+
if: github.event_name == 'pull_request'
28+
id: pr_details
29+
uses: actions/github-script@v7
30+
with:
31+
script: |
32+
const pr = context.payload.pull_request;
33+
34+
// Extract issue numbers from PR body (looks for "Fixes #X" or "Closes #X")
35+
const issueMatches = pr.body?.match(/(?:Fixes|Closes|Resolves)\s+#(\d+)/gi) || [];
36+
const issueNumbers = issueMatches.map(match => {
37+
const num = match.match(/\d+/);
38+
return num ? parseInt(num[0]) : null;
39+
}).filter(n => n !== null);
40+
41+
console.log('Completed issues:', issueNumbers);
42+
core.setOutput('completed_issues', JSON.stringify(issueNumbers));
43+
44+
return issueNumbers;
45+
46+
- name: Check and assign dependent issues
47+
uses: actions/github-script@v7
48+
env:
49+
COMPLETED_ISSUES: ${{ steps.pr_details.outputs.completed_issues || '[]' }}
50+
with:
51+
script: |
52+
const completedIssues = JSON.parse(process.env.COMPLETED_ISSUES || '[]');
53+
54+
// Dependency mapping (issue number -> array of blocker issue numbers)
55+
const dependencies = {
56+
17: [7], // Domain Entities blocked by Solution Structure
57+
18: [17], // Domain Interfaces blocked by Domain Entities
58+
19: [17], // Value Objects blocked by Domain Entities
59+
20: [17], // EF Core Configs blocked by Domain Entities
60+
21: [20], // Migrations blocked by EF Core Configs
61+
22: [20], // Repositories blocked by EF Core Configs
62+
23: [21, 13, 14], // Seed Traditions blocked by Migrations + Stoic + Vedanta quotes
63+
24: [21, 15], // Seed Philosophers blocked by Migrations + Bios
64+
25: [21, 13, 14], // Seed Quotes blocked by Migrations + quote data
65+
26: [21, 16], // Seed Timeline blocked by Migrations + timeline data
66+
27: [17], // DTOs blocked by Domain Entities
67+
28: [27], // AutoMapper blocked by DTOs
68+
29: [22, 27], // Tradition Service blocked by Repositories + DTOs
69+
30: [22, 27], // Philosopher Service blocked by Repositories + DTOs
70+
31: [22, 27], // Quote Service blocked by Repositories + DTOs
71+
32: [22, 27], // Theme Service blocked by Repositories + DTOs
72+
33: [22, 27], // Practice Service blocked by Repositories + DTOs
73+
34: [22, 27], // Timeline Service blocked by Repositories + DTOs
74+
35: [29], // Traditions API blocked by Tradition Service
75+
36: [30], // Philosophers API blocked by Philosopher Service
76+
37: [31], // Quotes API blocked by Quote Service
77+
38: [32], // Themes API blocked by Theme Service
78+
39: [33], // Practices API blocked by Practice Service
79+
40: [34], // Timeline API blocked by Timeline Service
80+
41: [29, 30, 31], // Search API blocked by core services
81+
42: [31], // Daily Quote API blocked by Quote Service
82+
};
83+
84+
// Issues that are ready to assign (have dependencies defined)
85+
const issuesWithDependencies = Object.keys(dependencies).map(Number);
86+
87+
console.log('Checking dependencies for issues:', issuesWithDependencies);
88+
console.log('Recently completed issues:', completedIssues);
89+
90+
for (const issueNum of issuesWithDependencies) {
91+
const blockers = dependencies[issueNum];
92+
93+
// Check if all blockers are in completed list
94+
const allBlockersComplete = blockers.every(blocker =>
95+
completedIssues.includes(blocker)
96+
);
97+
98+
if (allBlockersComplete) {
99+
console.log(`✅ Issue #${issueNum} is ready - all blockers complete`);
100+
101+
// Check if issue exists and is not already assigned to Copilot
102+
try {
103+
const { data: issue } = await github.rest.issues.get({
104+
owner: context.repo.owner,
105+
repo: context.repo.repo,
106+
issue_number: issueNum
107+
});
108+
109+
const isAssignedToCopilot = issue.assignees?.some(a =>
110+
a.login === 'Copilot' || a.type === 'Bot'
111+
);
112+
113+
if (issue.state === 'open' && !isAssignedToCopilot) {
114+
console.log(`🤖 Auto-assigning issue #${issueNum} to GitHub Copilot`);
115+
116+
// Assign to Copilot
117+
await github.rest.issues.addAssignees({
118+
owner: context.repo.owner,
119+
repo: context.repo.repo,
120+
issue_number: issueNum,
121+
assignees: ['Copilot']
122+
});
123+
124+
// Add comment explaining auto-assignment
125+
await github.rest.issues.createComment({
126+
owner: context.repo.owner,
127+
repo: context.repo.repo,
128+
issue_number: issueNum,
129+
body: `🤖 **Auto-assigned to GitHub Copilot**\n\nAll dependency blockers have been completed:\n${blockers.map(b => `- Issue #${b} ✅`).join('\n')}\n\nCopilot will now begin working on this issue.`
130+
});
131+
132+
console.log(`✅ Successfully assigned issue #${issueNum} to Copilot`);
133+
} else if (isAssignedToCopilot) {
134+
console.log(`ℹ️ Issue #${issueNum} already assigned to Copilot`);
135+
} else if (issue.state === 'closed') {
136+
console.log(`ℹ️ Issue #${issueNum} is already closed`);
137+
}
138+
} catch (error) {
139+
console.log(`⚠️ Issue #${issueNum} not found or error: ${error.message}`);
140+
}
141+
} else {
142+
const pendingBlockers = blockers.filter(b => !completedIssues.includes(b));
143+
console.log(`⏳ Issue #${issueNum} still waiting on: ${pendingBlockers.join(', ')}`);
144+
}
145+
}
146+
147+
- name: Update dependency tracking comment
148+
uses: actions/github-script@v7
149+
if: github.event_name == 'pull_request'
150+
with:
151+
script: |
152+
// Find or create a tracking issue/comment for dependency status
153+
// This helps visualize the dependency chain progress
154+
155+
const dependencies = {
156+
17: [7], 18: [17], 19: [17], 20: [17], 21: [20], 22: [20],
157+
23: [21, 13, 14], 24: [21, 15], 25: [21, 13, 14], 26: [21, 16],
158+
27: [17], 28: [27], 29: [22, 27], 30: [22, 27], 31: [22, 27],
159+
32: [22, 27], 33: [22, 27], 34: [22, 27], 35: [29], 36: [30],
160+
37: [31], 38: [32], 39: [33], 40: [34], 41: [29, 30, 31], 42: [31]
161+
};
162+
163+
// Get all issues to check their status
164+
const { data: allIssues } = await github.rest.issues.listForRepo({
165+
owner: context.repo.owner,
166+
repo: context.repo.repo,
167+
state: 'all',
168+
per_page: 100
169+
});
170+
171+
const issueStatusMap = {};
172+
allIssues.forEach(issue => {
173+
issueStatusMap[issue.number] = issue.state;
174+
});
175+
176+
// Calculate readiness for each dependent issue
177+
let statusReport = '## 🤖 Dependency Chain Status\n\n';
178+
statusReport += '_Auto-updated on each PR merge_\n\n';
179+
180+
const phases = {
181+
'Phase 2: Domain Layer': [17, 18, 19],
182+
'Phase 3: Data Layer': [20, 21, 22, 23, 24, 25, 26],
183+
'Phase 4: Application Layer': [27, 28, 29, 30, 31, 32, 33, 34],
184+
'Phase 5: API Layer': [35, 36, 37, 38, 39, 40, 41, 42]
185+
};
186+
187+
for (const [phase, issues] of Object.entries(phases)) {
188+
statusReport += `### ${phase}\n\n`;
189+
for (const issueNum of issues) {
190+
const blockers = dependencies[issueNum] || [];
191+
const allComplete = blockers.every(b => issueStatusMap[b] === 'closed');
192+
const issueState = issueStatusMap[issueNum];
193+
194+
let status = '⏳ Waiting';
195+
if (issueState === 'closed') status = '✅ Complete';
196+
else if (allComplete) status = '🟢 Ready';
197+
198+
statusReport += `- ${status} Issue #${issueNum}`;
199+
if (blockers.length > 0) {
200+
statusReport += ` (depends on: ${blockers.map(b => `#${b}`).join(', ')})`;
201+
}
202+
statusReport += '\n';
203+
}
204+
statusReport += '\n';
205+
}
206+
207+
console.log('Dependency Status Report:\n', statusReport);
208+
209+
// Post summary to PR
210+
await github.rest.issues.createComment({
211+
owner: context.repo.owner,
212+
repo: context.repo.repo,
213+
issue_number: context.payload.pull_request.number,
214+
body: statusReport
215+
});

0 commit comments

Comments
 (0)