Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1900a78
feat: Add agents-keepalive-loop.yml for consumer repos
stranske Dec 26, 2025
5f5ea2a
chore(codex-autofix): apply updates (PR #183)
github-actions[bot] Dec 26, 2025
f6cc567
Add required sections to agent task issue form
Dec 26, 2025
f0e2c5a
Align agent task template with required sections
Dec 26, 2025
c7f2086
chore(codex-keepalive): apply updates (PR #183)
github-actions[bot] Dec 26, 2025
73f27fa
test: cover agent issue templates for required sections
codex Dec 26, 2025
4f35c44
chore(codex-keepalive): apply updates (PR #183)
github-actions[bot] Dec 26, 2025
75b113b
Add scope/tasks/acceptance to Codex issue forms
codex Dec 26, 2025
5065251
chore(codex-keepalive): apply updates (PR #183)
github-actions[bot] Dec 26, 2025
48c10f8
chore(autofix): formatting/lint
github-actions[bot] Dec 26, 2025
9b86d35
Add keepalive placeholders for missing sections
Dec 26, 2025
9419900
chore(codex-keepalive): apply updates (PR #183)
github-actions[bot] Dec 26, 2025
7cb316c
test(keepalive): prefer real sections over placeholders
Dec 26, 2025
b5215ae
chore(codex-keepalive): apply updates (PR #183)
github-actions[bot] Dec 26, 2025
6ac5340
fix(issue-scope): treat placeholders as missing sections
Dec 26, 2025
e084cf9
chore(codex-keepalive): apply updates (PR #183)
github-actions[bot] Dec 26, 2025
a65d518
fix: correctly map evaluateKeepaliveLoop result to outputs
stranske Dec 26, 2025
b859f4a
fix: restore workflow_dispatch trigger and SHA fallback for consumer …
stranske Dec 26, 2025
326dd51
docs: update SETUP_CHECKLIST with agents-keepalive-loop requirements
stranske Dec 26, 2025
ab87e2b
fix: add fetch/rebase before push in reusable-codex-run.yml
stranske Dec 26, 2025
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
12 changes: 10 additions & 2 deletions .github/ISSUE_TEMPLATE/agent-task.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,22 @@ assignees: ''
section header options, and examples of valid issue structures.
-->

## Goal
## Why

Copilot AI Dec 26, 2025

Copy link

Choose a reason for hiding this comment

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

The section header changed from "Goal" to "Why" but this is inconsistent with the YAML template changes where "Goal" was kept (though renamed to "Acceptance criteria"). Consider whether "Why" is the intended header name or if it should match other templates. Also, the "Success criteria" section is renamed to "Acceptance criteria" which is good for consistency, but ensure this aligns with the documentation and any parsing logic that looks for these section headers.

Suggested change
## Why
## Goal

Copilot uses AI. Check for mistakes.
<!-- Describe the primary objective Codex should accomplish. Include links to relevant issues, documents, or workflows. -->

## Scope
<!-- Define what is IN scope for this task. Be specific about files, components, or features to be modified. -->

## Constraints
<!-- List guardrails Codex must respect (files to avoid, technologies to use, time limits, dependencies, etc.). -->

## Tasks
<!-- Actionable checklist of work items. Use [ ] checkbox format. -->
- [ ] Task 1
- [ ] Task 2

## Expected outputs
<!-- Enumerate the artifacts Codex should produce (code changes, tests, docs, dashboards, reports, etc.). -->

## Success criteria
## Acceptance criteria
<!-- State how you will evaluate success. Reference acceptance tests, validation commands, or qualitative outcomes that must be met. -->
24 changes: 20 additions & 4 deletions .github/ISSUE_TEMPLATE/agent_task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,32 @@ body:
placeholder: Provide the context and link supporting material.
validations:
required: true
- type: textarea
id: scope
attributes:
label: Scope
description: What is in scope for this work? Call out files, systems, or workflows to touch.
placeholder: Describe the intended scope.
validations:
required: true
- type: textarea
id: tasks
attributes:
label: Tasks
description: List the concrete tasks Codex should complete.
placeholder: "- [ ] Task 1\n- [ ] Task 2"
validations:
required: true
- type: textarea
id: goals
attributes:
label: Goals
description: List the concrete outcomes this task should deliver.
placeholder: Bullet the acceptance criteria or deliverables.
label: Acceptance criteria
description: Describe what must be true for this work to be considered complete.
placeholder: "- [ ] Criterion 1\n- [ ] Criterion 2"
validations:
required: true
- type: textarea
id: scope
id: guardrails

Copilot AI Dec 26, 2025

Copy link

Choose a reason for hiding this comment

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

The field ID has changed from "goals" to "guardrails" but the field position and some context suggests this might be unintentional. The old "Goals" field (line 42-47) has been renamed to "Acceptance criteria" which makes sense, but the old "scope" field (id: scope) has been replaced with "guardrails" while a new "Scope" field was added above. This creates a naming inconsistency where the "Out of scope / guardrails" label is associated with the "guardrails" ID, but it used to be "scope". This could break any automation that references these field IDs.

Copilot uses AI. Check for mistakes.
attributes:
label: Out of scope / guardrails
description: Clarify any boundaries Codex must respect (files to avoid, limits, etc.).
Expand Down
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report_codex.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,32 @@ body:
1.
2.
3.
validations:
required: true
Comment on lines +34 to +35

Copilot AI Dec 26, 2025

Copy link

Choose a reason for hiding this comment

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

The "Steps to reproduce" field is now marked as required, but this is a bug report template. There are edge cases where bugs might not have clear reproduction steps (e.g., intermittent issues, race conditions, or bugs found through code review). Consider whether this should remain optional or if the description should clarify that users should explain what they've observed even if they can't provide exact reproduction steps.

Copilot uses AI. Check for mistakes.
- type: textarea
id: scope
attributes:
label: Scope
description: What is in scope for the fix? Call out files, systems, or workflows to touch.
placeholder: Describe the intended scope.
validations:
required: true
- type: textarea
id: tasks
attributes:
label: Tasks
description: Checklist of concrete work items for Codex to complete.
placeholder: "- [ ] Task 1\n- [ ] Task 2"
validations:
required: true
- type: textarea
id: acceptance
attributes:
label: Acceptance criteria
description: Bullet list of verifiable outcomes for the fix.
value: |
- [ ] A
- [ ] B
- [ ] C
validations:
required: true
16 changes: 16 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request_codex.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ body:
placeholder: e.g., "Add preview of score frame before selection"
validations:
required: true
- type: textarea
id: scope
attributes:
label: Scope
description: What is in scope for this change? Mention files, systems, or workflows to touch.
placeholder: Describe the intended scope.
validations:
required: true
- type: textarea
id: tasks
attributes:
label: Tasks
description: Checklist of concrete work items for Codex to complete.
placeholder: "- [ ] Task 1\n- [ ] Task 2"
validations:
required: true
- type: textarea
id: acceptance
attributes:
Expand Down
20 changes: 20 additions & 0 deletions .github/scripts/__tests__/issue_context_utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ const TASKS_ONLY = `
- [ ] missing acceptance criteria
`;

const PLACEHOLDER_SECTIONS = `
## Scope
_No scope information provided_

## Tasks
- [ ] _No tasks defined_

## Acceptance Criteria
- [ ] _No acceptance criteria defined_
`;

const WHY_INSTEAD_OF_SCOPE = `
## Why
Explains the motivation.
Expand Down Expand Up @@ -66,6 +77,15 @@ test('buildIssueContext flags warnings when Acceptance is missing', () => {
assert.ok(result.missingSections.includes('Acceptance Criteria'));
});

test('buildIssueContext treats placeholder-only sections as missing', () => {
const result = buildIssueContext(PLACEHOLDER_SECTIONS);
assert.equal(result.summaryNeedsWarning, true);
assert.equal(result.hasActionableContent, false);
assert.ok(result.missingSections.includes('Tasks'));
assert.ok(result.missingSections.includes('Acceptance Criteria'));
assert.ok(result.warningLines.some((line) => line.includes('Template Warning')));
});

test('buildIssueContext accepts "Why" as Scope alias', () => {
const result = buildIssueContext(WHY_INSTEAD_OF_SCOPE);
assert.equal(result.summaryNeedsWarning, false);
Expand Down
23 changes: 23 additions & 0 deletions .github/scripts/__tests__/issue_scope_parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,29 @@ test('analyzeSectionPresence recognises canonical template', () => {
assert.deepEqual(status.missing, []);
});

test('analyzeSectionPresence treats placeholder-only sections as missing', () => {
const issue = [
'## Scope',
'_No scope information provided_',
'',
'## Tasks',
'- [ ] _No tasks defined_',
'',
'## Acceptance Criteria',
'- [ ] _No acceptance criteria defined_',
].join('\n');

const status = analyzeSectionPresence(issue);
assert.deepEqual(status.entries, [
{ key: 'scope', label: 'Scope', present: false, optional: true },
{ key: 'tasks', label: 'Tasks', present: false, optional: false },
{ key: 'acceptance', label: 'Acceptance Criteria', present: false, optional: false },
]);
assert.deepEqual(status.missing, ['Tasks', 'Acceptance Criteria']);
assert.equal(status.hasAllRequired, false);
assert.equal(status.hasActionableContent, false);
});

test('extracts "Why" section as Scope alias', () => {
const issue = [
'## Why',
Expand Down
28 changes: 28 additions & 0 deletions .github/scripts/__tests__/issue_template_sections.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');

const repoRoot = path.resolve(__dirname, '../../..');
const issueFormPath = path.join(repoRoot, '.github/ISSUE_TEMPLATE/agent_task.yml');
const issueTemplatePath = path.join(repoRoot, '.github/ISSUE_TEMPLATE/agent-task.md');

const readFile = (filePath) => fs.readFileSync(filePath, 'utf8');

test('agent task issue form includes Scope/Tasks/Acceptance sections', () => {
const content = readFile(issueFormPath);

assert.match(content, /label:\s*Scope\b/i);
assert.match(content, /label:\s*Tasks\b/i);
assert.match(content, /label:\s*Acceptance criteria\b/i);
});

test('agent task markdown template includes Scope/Tasks/Acceptance sections', () => {
const content = readFile(issueTemplatePath);

assert.match(content, /^##\s+Scope\b/m);
assert.match(content, /^##\s+Tasks\b/m);
assert.match(content, /^##\s+Acceptance criteria\b/m);
});
42 changes: 41 additions & 1 deletion .github/scripts/issue_scope_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,30 @@ const PLACEHOLDERS = {

const CHECKBOX_SECTIONS = new Set(['tasks', 'acceptance']);

function normaliseSectionContent(sectionKey, content) {
const trimmed = String(content || '').trim();
if (!trimmed) {
return '';
}
if (CHECKBOX_SECTIONS.has(sectionKey)) {
return normaliseChecklist(trimmed).trim();
}
return trimmed;
}

function isPlaceholderContent(sectionKey, content) {
const placeholder = PLACEHOLDERS[sectionKey];
if (!placeholder) {
return false;
}
const normalized = normaliseSectionContent(sectionKey, content);
if (!normalized) {
return false;
}
const placeholderNormalized = normaliseSectionContent(sectionKey, placeholder);
return normalized === placeholderNormalized;
}

function normaliseChecklist(content) {
const raw = String(content || '');
if (!raw.trim()) {
Expand Down Expand Up @@ -215,14 +239,29 @@ const parseScopeTasksAcceptanceSections = (source) => {
return sections;
};

const hasNonPlaceholderScopeTasksAcceptanceContent = (source) => {
const { sections } = collectSections(source);
if (!sections || typeof sections !== 'object') {
return false;
}
return Object.entries(sections).some(([key, value]) => {
const content = String(value || '').trim();
if (!content) {
return false;
}
return !isPlaceholderContent(key, content);
});
};

const analyzeSectionPresence = (source) => {
const { sections } = collectSections(source);
const entries = SECTION_DEFS.map((section) => {
const content = (sections[section.key] || '').trim();
const isPlaceholder = isPlaceholderContent(section.key, content);
return {
key: section.key,
label: section.label,
present: Boolean(content),
present: Boolean(content) && !isPlaceholder,
optional: Boolean(section.optional),
};
});
Expand All @@ -245,5 +284,6 @@ const analyzeSectionPresence = (source) => {
module.exports = {
extractScopeTasksAcceptanceSections,
parseScopeTasksAcceptanceSections,
hasNonPlaceholderScopeTasksAcceptanceContent,
analyzeSectionPresence,
};
6 changes: 5 additions & 1 deletion .github/workflows/maint-68-sync-consumer-repos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ jobs:
"agents-orchestrator.yml:agents-orchestrator.yml"
"agents-orchestrator.yml:agents-70-orchestrator.yml"
"agents-pr-meta.yml:agents-pr-meta.yml"
"agents-keepalive-loop.yml:agents-keepalive-loop.yml"
"autofix.yml:autofix.yml"
"pr-00-gate.yml:pr-00-gate.yml"
)
Expand Down Expand Up @@ -295,6 +296,7 @@ jobs:
SYNC_TEMPLATES=(
"agents-orchestrator.yml"
"agents-pr-meta.yml"
"agents-keepalive-loop.yml"
"autofix.yml"
"pr-00-gate.yml"
)
Expand All @@ -308,7 +310,9 @@ jobs:
elif [ -f ".github/workflows/agents-70-orchestrator.yml" ] && [ "$file" = "agents-orchestrator.yml" ]; then
target=".github/workflows/agents-70-orchestrator.yml"
else
continue
# Create new file if it doesn't exist (for new workflows like keepalive-loop)
mkdir -p .github/workflows
target=".github/workflows/$file"
Comment on lines +313 to +315

Copilot AI Dec 26, 2025

Copy link

Choose a reason for hiding this comment

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

The logic change here creates new workflow files without checking if that's the intended behavior. The previous code would skip files that didn't exist in either the default or alternative location. Now, if a workflow doesn't exist, it will be created in .github/workflows/$file. This could unexpectedly add workflows to consumer repos that hadn't opted in to them. Consider adding a check or flag to control whether new workflows should be auto-created versus requiring manual setup.

Suggested change
# Create new file if it doesn't exist (for new workflows like keepalive-loop)
mkdir -p .github/workflows
target=".github/workflows/$file"
# Skip workflows that don't already exist in the consumer repo
echo "Skipping missing workflow: $file"
continue

Copilot uses AI. Check for mistakes.
fi

if [ -f "$template" ]; then
Expand Down
19 changes: 18 additions & 1 deletion .github/workflows/reusable-codex-run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,24 @@ jobs:
COMMIT_SHA=$(git rev-parse HEAD)
echo "commit-sha=${COMMIT_SHA}" >> "$GITHUB_OUTPUT"

git push "https://x-access-token:${PUSH_TOKEN}@github.com/${{ github.repository }}" "HEAD:${TARGET_BRANCH}"
# Pull and rebase before push to handle concurrent changes (e.g., Autofix)
# This prevents push failures when the branch has been updated while Codex ran
REMOTE_URL="https://x-access-token:${PUSH_TOKEN}@github.com/${{ github.repository }}"
echo "::group::Sync with remote before push"
git fetch "${REMOTE_URL}" "${TARGET_BRANCH}" 2>/dev/null || true
if git rev-parse "FETCH_HEAD" >/dev/null 2>&1; then
if ! git rebase FETCH_HEAD; then
echo "::warning::Rebase failed, attempting merge strategy"
git rebase --abort 2>/dev/null || true
git pull --no-rebase "${REMOTE_URL}" "${TARGET_BRANCH}" --allow-unrelated-histories || true
fi
# Update commit SHA after rebase/merge
COMMIT_SHA=$(git rev-parse HEAD)
echo "commit-sha=${COMMIT_SHA}" >> "$GITHUB_OUTPUT"
fi
echo "::endgroup::"

git push "${REMOTE_URL}" "HEAD:${TARGET_BRANCH}"

echo "::notice::Pushed commit ${COMMIT_SHA} with ${CHANGED_FILES} file(s) changed"

Expand Down
1 change: 1 addition & 0 deletions autofix_report_enriched.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"changed": true, "classification": {"total": 0, "new": 0, "allowed": 0}, "timestamp": "2025-12-26T16:43:58Z", "files": ["tests/workflows/test_workflow_agents_consolidation.py"]}
18 changes: 12 additions & 6 deletions codex-output.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
Added an automatic default metrics log path for keepalive iterations running under GitHub Actions so records are appended without extra inputs, and expanded keepalive-loop tests to verify the default log behavior and clean up the workspace file. Updated the acceptance checkbox in `codex-prompt.md` after verifying the new logging behavior. Changes are in `.github/scripts/keepalive_loop.js`, `.github/scripts/__tests__/keepalive-loop.test.js`, and `codex-prompt.md`.
Improved placeholder handling so placeholder-only Scope/Tasks/Acceptance sections no longer count as real content, and added coverage to lock in missing-section detection. This keeps missing sections visible to the system while avoiding false “present” signals in `.github/scripts/issue_scope_parser.js` and ensures `buildIssueContext` surfaces warnings when placeholders are used.

Tests: `node --test .github/scripts/__tests__/keepalive-loop.test.js`
Details
- Updated `.github/scripts/issue_scope_parser.js` to treat placeholder-only content as not present for `analyzeSectionPresence`.
- Added placeholder-specific tests in `.github/scripts/__tests__/issue_scope_parser.test.js`.
- Added placeholder-missing coverage in `.github/scripts/__tests__/issue_context_utils.test.js`.

Workflow update is still blocked by policy: I can’t edit `.github/workflows/agents-orchestrator.yml` in this run. Please add a `needs-human` label and a PR comment instructing the workflow update to call `scripts/keepalive_metrics_collector.py` after keepalive completes (or set `KEEPALIVE_METRICS_PATH` for the loop).
Tests
- `node --test .github/scripts/__tests__/issue_scope_parser.test.js .github/scripts/__tests__/issue_context_utils.test.js`

Next steps:
1) Have a human update `.github/workflows/agents-orchestrator.yml` to invoke the metrics collector or set `KEEPALIVE_METRICS_PATH`.
2) Run the full selftest CI to satisfy the remaining acceptance criterion.
Notes
- `codex-prompt.md` has existing uncommitted changes (progress/checkbox resets). I didn’t modify it this round; let me know if you want me to update/commit it.

Next steps
1) Update the source issue to add `Scope`, `Tasks`, and `Acceptance Criteria` sections, then re-run the PR body sync/keepalive.
Loading