Skip to content

Commit 06a4488

Browse files
EMaherCopilot
andcommitted
refactor: rename label workflows and add generic uniqueness enforcement
- Rename sync-squad-labels.yml → issue-labels-sync.yml - Delete squad-label-enforce.yml (hardcoded per-category logic) - Add issue-labels-enforce-unique.yml: generic enforcement for any category:value labels, reads sync file for priority ordering, exempts squad:* labels - Update doc references in 3 files Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d8a2ed1 commit 06a4488

6 files changed

Lines changed: 102 additions & 185 deletions

File tree

.copilot/skills/init-mode/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ No team exists yet. Propose one — but **DO NOT create any files until the user
5858

5959
**Seeding:** Each agent's `history.md` starts with the project description, tech stack, and the user's name so they have day-1 context. Agent folder names are the cast name in lowercase (e.g., `.squad/agents/ripley/`). The Scribe's charter includes maintaining `decisions.md` and cross-agent context sharing.
6060

61-
**Team.md structure:** `team.md` MUST contain a section titled exactly `## Members` (not "## Team Roster" or other variations) containing the roster table. This header is hard-coded in GitHub workflows (`squad-heartbeat.yml`, `squad-issue-assign.yml`, `squad-triage.yml`, `sync-squad-labels.yml`) for label automation. If the header is missing or titled differently, label routing breaks.
61+
**Team.md structure:** `team.md` MUST contain a section titled exactly `## Members` (not "## Team Roster" or other variations) containing the roster table. This header is hard-coded in GitHub workflows (`squad-heartbeat.yml`, `squad-issue-assign.yml`, `squad-triage.yml`, `issue-labels-sync.yml`) for label automation. If the header is missing or titled differently, label routing breaks.
6262

6363
**Merge driver for append-only files:** Create or update `.gitattributes` at the repo root to enable conflict-free merging of `.squad/` state across branches:
6464
```
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
name: Enforce Unique Category Labels
2+
3+
on:
4+
issues:
5+
types: [labeled]
6+
7+
permissions:
8+
issues: write
9+
contents: read
10+
11+
jobs:
12+
enforce:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Enforce one label per category
18+
uses: actions/github-script@v7
19+
with:
20+
script: |
21+
const fs = require('fs');
22+
const issue = context.payload.issue;
23+
const appliedLabel = context.payload.label.name;
24+
25+
// Only handle "category:value" labels
26+
const match = appliedLabel.match(/^([^:]+):/);
27+
if (!match) {
28+
core.info(`Label "${appliedLabel}" has no category prefix — skipping`);
29+
return;
30+
}
31+
32+
const category = match[1];
33+
34+
// squad: labels are exempt from uniqueness enforcement
35+
if (category === 'squad') {
36+
core.info(`squad: labels are exempt — skipping`);
37+
return;
38+
}
39+
40+
// Collect all labels in the same category currently on the issue
41+
const allLabels = issue.labels.map(l => l.name);
42+
const sameCategory = allLabels.filter(l => l.startsWith(category + ':'));
43+
44+
if (sameCategory.length <= 1) {
45+
core.info(`Only one "${category}:" label present — nothing to enforce`);
46+
return;
47+
}
48+
49+
// Read the sync workflow to determine canonical label ordering.
50+
// Labels listed earlier in issue-labels-sync.yml have higher priority.
51+
const syncPath = '.github/workflows/issue-labels-sync.yml';
52+
let labelOrder = [];
53+
if (fs.existsSync(syncPath)) {
54+
const syncContent = fs.readFileSync(syncPath, 'utf8');
55+
const nameRegex = /name:\s*'([^']+)'/g;
56+
let m;
57+
while ((m = nameRegex.exec(syncContent)) !== null) {
58+
if (m[1].startsWith(category + ':')) {
59+
labelOrder.push(m[1]);
60+
}
61+
}
62+
}
63+
64+
// Determine winner: first label (by sync-file order) that is on the issue.
65+
// If the category isn't in the sync file, fall back to keeping whichever
66+
// label appeared first in the issue's current label list.
67+
let winner = null;
68+
if (labelOrder.length > 0) {
69+
for (const ordered of labelOrder) {
70+
if (sameCategory.includes(ordered)) {
71+
winner = ordered;
72+
break;
73+
}
74+
}
75+
}
76+
if (!winner) {
77+
winner = sameCategory[0];
78+
}
79+
80+
const toRemove = sameCategory.filter(l => l !== winner);
81+
core.info(`Conflict in "${category}:" — keeping "${winner}", removing ${toRemove.join(', ')}`);
82+
83+
for (const label of toRemove) {
84+
await github.rest.issues.removeLabel({
85+
owner: context.repo.owner,
86+
repo: context.repo.repo,
87+
issue_number: issue.number,
88+
name: label
89+
});
90+
core.info(`Removed: ${label}`);
91+
}
92+
93+
await github.rest.issues.createComment({
94+
owner: context.repo.owner,
95+
repo: context.repo.repo,
96+
issue_number: issue.number,
97+
body: `🏷️ Label conflict resolved in \`${category}:\` — kept \`${winner}\`, removed ${toRemove.map(l => '`' + l + '`').join(', ')}.`
98+
});

.github/workflows/squad-label-enforce.yml

Lines changed: 0 additions & 181 deletions
This file was deleted.

.squad/templates/skills/init-mode/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ No team exists yet. Propose one — but **DO NOT create any files until the user
5858

5959
**Seeding:** Each agent's `history.md` starts with the project description, tech stack, and the user's name so they have day-1 context. Agent folder names are the cast name in lowercase (e.g., `.squad/agents/ripley/`). The Scribe's charter includes maintaining `decisions.md` and cross-agent context sharing.
6060

61-
**Team.md structure:** `team.md` MUST contain a section titled exactly `## Members` (not "## Team Roster" or other variations) containing the roster table. This header is hard-coded in GitHub workflows (`squad-heartbeat.yml`, `squad-issue-assign.yml`, `squad-triage.yml`, `sync-squad-labels.yml`) for label automation. If the header is missing or titled differently, label routing breaks.
61+
**Team.md structure:** `team.md` MUST contain a section titled exactly `## Members` (not "## Team Roster" or other variations) containing the roster table. This header is hard-coded in GitHub workflows (`squad-heartbeat.yml`, `squad-issue-assign.yml`, `squad-triage.yml`, `issue-labels-sync.yml`) for label automation. If the header is missing or titled differently, label routing breaks.
6262

6363
**Merge driver for append-only files:** Create or update `.gitattributes` at the repo root to enable conflict-free merging of `.squad/` state across branches:
6464
```

.squad/templates/squad.agent.md.template

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ No team exists yet. Propose one — but **DO NOT create any files until the user
9292

9393
**Seeding:** Each agent's `history.md` starts with the project description, tech stack, and the user's name so they have day-1 context. Agent folder names are the cast name in lowercase (e.g., `.squad/agents/ripley/`). The Scribe's charter includes maintaining `decisions.md` and cross-agent context sharing. Rai's charter is seeded from the `Rai-charter.md` template, and `.squad/rai/policy.md` is seeded from `rai-policy.md`.
9494

95-
**Team.md structure:** `team.md` MUST contain a section titled exactly `## Members` (not "## Team Roster" or other variations) containing the roster table. This header is hard-coded in GitHub workflows (`squad-heartbeat.yml`, `squad-issue-assign.yml`, `squad-triage.yml`, `sync-squad-labels.yml`) for label automation. If the header is missing or titled differently, label routing breaks.
95+
**Team.md structure:** `team.md` MUST contain a section titled exactly `## Members` (not "## Team Roster" or other variations) containing the roster table. This header is hard-coded in GitHub workflows (`squad-heartbeat.yml`, `squad-issue-assign.yml`, `squad-triage.yml`, `issue-labels-sync.yml`) for label automation. If the header is missing or titled differently, label routing breaks.
9696

9797
**Merge driver for append-only files:** Create or update `.gitattributes` at the repo root to enable conflict-free merging of `.squad/` state across branches:
9898
```
@@ -186,7 +186,7 @@ For each squad member with assigned issues, note them in the session context. Wh
186186

187187
**Proactive issue pickup:** If a user starts a session and there are open `squad:{member}` issues, mention them: *"Hey {user}, {AgentName} has an open issue — #42: Fix auth endpoint timeout. Want them to pick it up?"*
188188

189-
**Issue triage routing:** When a new issue gets the `squad` label (via the sync-squad-labels workflow), the Lead triages it — reading the issue, analyzing it, assigning the correct `squad:{member}` label(s), and commenting with triage notes. The Lead can also reassign by swapping labels.
189+
**Issue triage routing:** When a new issue gets the `squad` label (via the issue-labels-sync workflow), the Lead triages it — reading the issue, analyzing it, assigning the correct `squad:{member}` label(s), and commenting with triage notes. The Lead can also reassign by swapping labels.
190190

191191
**⚡ Read `.squad/team.md` (roster), `.squad/routing.md` (routing), and `.squad/casting/registry.json` (persistent names) as parallel tool calls in a single turn. Do NOT read these sequentially.**
192192

0 commit comments

Comments
 (0)