Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions .github/scripts/conflict_detector.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,34 @@ const IGNORED_CONFLICT_FILES = [
'residual-trend-history.ndjson',
];

// Comments from automation often mention "conflict" but should not block execution.
const IGNORED_COMMENT_AUTHORS = new Set([
'github-actions[bot]',
'github-merge-queue[bot]',
'dependabot[bot]',
'github',
]);

const IGNORED_COMMENT_MARKERS = [
'<!-- keepalive-state',
'keepalive-loop-summary',
'auto-status-summary',
];

function isIgnoredComment(comment) {
if (!comment) {
return false;
}

const author = comment.user?.login || '';
if (comment.user?.type === 'Bot' || IGNORED_COMMENT_AUTHORS.has(author)) {
return true;
}

const body = comment.body || '';
return IGNORED_COMMENT_MARKERS.some((marker) => body.includes(marker));
}

/**
* Check if a file should be excluded from conflict detection.
* @param {string} filename - File path to check
Expand Down Expand Up @@ -223,8 +251,10 @@ async function checkCommentsForConflicts(github, context, prNumber) {
per_page: 20,
});

// Check recent comments (last 10)
const recentComments = comments.slice(-10);
// Check recent comments (last 10) and ignore bot/system noise
const recentComments = comments
.filter((comment) => !isIgnoredComment(comment))
.slice(-10);

for (const comment of recentComments) {
for (const pattern of CONFLICT_PATTERNS) {
Expand Down
3 changes: 3 additions & 0 deletions .github/scripts/error_classifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ function classifyByMessage(message) {

function classifyError(error) {
const message = normaliseMessage(error);
const preview = message ? message.slice(0, 50) : 'unknown';
// eslint-disable-next-line no-console
console.log(`[error_classifier] Classifying error: ${preview}`);
const status = getStatusCode(error);

const statusCategory = status ? classifyByStatus(status, message) : null;
Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/parse_chatgpt_topics.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ def _parse_sections(
"tasks": {"tasks"},
"acceptance_criteria": {"acceptance criteria", "acceptance criteria."},
"implementation_notes": {
"admin access",
"admin requirement",
"admin requirements",
"dependencies",
"dependency",
"implementation notes",
"implementation note",
"notes",
Expand Down
44 changes: 25 additions & 19 deletions .github/workflows/agents-auto-label.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,16 @@ jobs:
!contains(github.event.issue.labels.*.name, 'automated')

steps:
- name: Checkout Workflows repo
uses: actions/checkout@v6
with:
repository: stranske/Workflows
path: workflows-repo
- name: Checkout repository
uses: actions/checkout@v4
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checkout action was downgraded from v6 to v4. This is an unusual change as typically actions are upgraded to newer versions. If this is intentional for compatibility, it should be documented. Otherwise, consider keeping v6 which may have security or performance improvements.

Suggested change
uses: actions/checkout@v4
uses: actions/checkout@v6

Copilot uses AI. Check for mistakes.

- name: Set up Python
uses: actions/setup-python@v5
with:
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Python version was downgraded from 3.12 to 3.11. If this change is intentional for compatibility reasons, it should be documented why. If the codebase or dependencies require Python 3.12 features, this could cause runtime errors.

Suggested change
with:
with:
# Use Python 3.11 intentionally: some dependencies in this workflow (including optional extras)
# are not yet fully compatible with Python 3.12 in our CI environment.

Copilot uses AI. Check for mistakes.
python-version: "3.12"
python-version: "3.11"

- name: Install dependencies
run: |
cd workflows-repo
pip install -e ".[langchain]" --quiet

- name: Get repo labels
Expand Down Expand Up @@ -74,8 +70,8 @@ jobs:
LABELS_JSON: ${{ steps.get-labels.outputs.labels_json }}
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
PYTHONPATH: ${{ github.workspace }}
run: |
cd workflows-repo
python3 << 'PYTHON_SCRIPT'
import json
import os
Expand Down Expand Up @@ -135,6 +131,16 @@ jobs:
auto_apply = [m for m in matches if m.score >= auto_threshold]
suggestions = [m for m in matches if suggest_threshold <= m.score < auto_threshold]

# IMPORTANT: Only auto-apply the BEST matching label, not all above threshold
# This prevents over-labeling issues with multiple labels like bug+enhancement
if auto_apply:
best_match = auto_apply[0] # matches are already sorted by score descending
auto_apply = [best_match]
# Move other high-confidence matches to suggestions
for m in matches[1:]:
if m.score >= auto_threshold and m not in suggestions:
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition 'm not in suggestions' on line 141 is redundant. The 'suggestions' list (line 132) only contains matches where score < auto_threshold, so any match with score >= auto_threshold will never be in 'suggestions'. The check 'm not in suggestions' will always evaluate to True when 'm.score >= auto_threshold' is True, making the second condition unnecessary.

Suggested change
if m.score >= auto_threshold and m not in suggestions:
if m.score >= auto_threshold:

Copilot uses AI. Check for mistakes.
suggestions.insert(0, m)

print(f"Auto-apply labels ({auto_threshold}+ confidence):")
for m in auto_apply:
print(f" - {m.label.name}: {m.score:.2%}")
Expand All @@ -144,15 +150,15 @@ jobs:
print(f" - {m.label.name}: {m.score:.2%}")

# Output results
auto_labels = json.dumps([m.label.name for m in auto_apply])
suggest_json = json.dumps([
{'name': m.label.name, 'score': f'{m.score:.0%}'}
for m in suggestions
])
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write('has_suggestions=true\n')
f.write(f'auto_apply_labels={auto_labels}\n')
f.write(f'suggested_labels={suggest_json}\n')
auto_json = json.dumps([m.label.name for m in auto_apply])
f.write(f'auto_apply_labels={auto_json}\n')
sugg_data = [
{"name": m.label.name, "score": f"{m.score:.0%}"}
for m in suggestions
]
f.write(f'suggested_labels={json.dumps(sugg_data)}\n')

PYTHON_SCRIPT

Expand Down Expand Up @@ -220,14 +226,14 @@ jobs:
body += `${suggestions}\n\n`;

if (autoApplied.length > 0) {
const appliedStr = autoApplied.map(l => `\`${l}\``).join(', ');
body += `**Auto-applied:** ${appliedStr}\n\n`;
const applied = autoApplied.map(l => `\`${l}\``).join(', ');
body += `**Auto-applied:** ${applied}\n\n`;
}

body += `<details>\n<summary>How to use these suggestions</summary>\n\n`;
body += `- Click the label name in the sidebar to add it\n`;
const ghCmd = `gh issue edit ${context.issue.number} --add-label "label-name"`;
body += `- Or use the GitHub CLI: \`${ghCmd}\`\n`;
const editCmd = `gh issue edit ${context.issue.number} --add-label "label-name"`;
body += `- Or use the GitHub CLI: \`${editCmd}\`\n`;
body += `</details>\n\n`;
body += `---\n*Auto-generated by label matcher*`;

Expand Down
Loading
Loading