Files
openclaw/.github/workflows/dated-todo-sweep.yml
Peter Steinberger 4596fd2dd2 feat(ci): weekly Codex sweep for date-carrying to-dos (#113914)
* feat(ci): weekly Codex sweep for date-carrying to-dos

Mondays 06:23 UTC (plus default-branch-only manual dispatch with
dry_run): a deterministic prefilter collects candidate lines where
to-do markers co-occur with date tokens, plus every deprecated compat
record from the plugin registry; a pinned openai/codex-action step
judges each candidate in context (genuine dated commitment vs
historical date or fixture) and writes an OVERDUE / DUE-30-days /
FUTURE report; a separate privileged job validates the report (tracked
file:line locations, date shape, inert text) and upserts one
marker-tagged tracking issue via the Barnacle app token, commenting
only when items newly become due.

The app token is minted on a fresh runner and checkout — never beside
Codex or its child processes; only the validated report artifact
crosses jobs. No permission-* subsets on token minting (installations
reject explicit subsets; see pr-ci-sweeper).

* chore(ci): localize upsert helpers with no external consumers
2026-07-25 18:18:06 -07:00

157 lines
5.7 KiB
YAML

name: Dated TODO sweep
on:
schedule:
- cron: "23 6 * * 1"
workflow_dispatch:
inputs:
dry_run:
description: Log the fresh report without creating or updating the tracking issue.
required: false
default: false
type: boolean
permissions:
contents: read
concurrency:
group: dated-todo-sweep
cancel-in-progress: false
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
analyze:
# Manual runs may select a ref; secrets are available only when that ref is
# the trusted default branch. Scheduled runs already target that branch.
if: github.event_name != 'workflow_dispatch' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
outputs:
sweep-date: ${{ steps.sweep-date.outputs.date }}
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
node-version: "24.x"
install-bun: "false"
- name: Collect dated TODO candidates
run: node scripts/dated-todo-scan.mjs
- name: Capture sweep date
id: sweep-date
run: echo "date=$(date -u +%F)" >> "$GITHUB_OUTPUT"
- name: Run Codex dated TODO sweep
uses: openai/codex-action@52fe01ec70a42f454c9d2ebd47598f9fd6893d56
env:
DATED_TODO_SWEEP_DATE: ${{ steps.sweep-date.outputs.date }}
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt-file: .github/codex/prompts/dated-todo-sweep.md
model: ${{ vars.OPENCLAW_CI_OPENAI_MODEL_BARE }}
effort: medium
sandbox: workspace-write
safety-strategy: drop-sudo
# Only the report crosses into the privileged job. The app token is minted
# on a fresh runner and checkout, never beside Codex or its child processes.
- name: Upload dated TODO report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: dated-todo-report
path: .artifacts/dated-todo-report.md
if-no-files-found: error
include-hidden-files: true
retention-days: 7
upsert:
if: github.event_name != 'workflow_dispatch' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
needs: analyze
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout trusted workflow code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Download dated TODO report
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: dated-todo-report
path: .artifacts
- name: Validate dated TODO report
env:
DATED_TODO_SWEEP_DATE: ${{ needs.analyze.outputs.sweep-date }}
shell: bash
run: |
set -euo pipefail
node --input-type=module <<'NODE'
import fs from "node:fs";
import { pathToFileURL } from "node:url";
const reportPath = ".artifacts/dated-todo-report.md";
const report = fs.readFileSync(reportPath, "utf8");
const moduleUrl = pathToFileURL(
`${process.env.GITHUB_WORKSPACE}/scripts/github/dated-todo-upsert.mjs`,
);
const { validateDatedTodoReport } = await import(moduleUrl.href);
validateDatedTodoReport(report, {
expectedDate: process.env.DATED_TODO_SWEEP_DATE,
repoRoot: process.env.GITHUB_WORKSPACE,
});
NODE
- name: Log dry-run report
if: github.event_name == 'workflow_dispatch' && inputs.dry_run
run: cat .artifacts/dated-todo-report.md
# No permission-* subset here: requesting a permission the installation
# does not grant fails token minting outright. No inputs uses the app's
# full granted set, matching the proven Barnacle fallback workflow.
- name: Create Barnacle app token
if: github.event_name != 'workflow_dispatch' || !inputs.dry_run
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # zizmor: ignore[github-app] v3
id: app-token
continue-on-error: true
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Create fallback Barnacle app token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # zizmor: ignore[github-app] v3
id: app-token-fallback
if: (github.event_name != 'workflow_dispatch' || !inputs.dry_run) && steps.app-token.outcome == 'failure'
with:
app-id: "2971289"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
- name: Upsert dated TODO tracking issue
if: github.event_name != 'workflow_dispatch' || !inputs.dry_run
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
github-token: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
script: |
const { pathToFileURL } = require("node:url");
const moduleUrl = pathToFileURL(
`${process.env.GITHUB_WORKSPACE}/scripts/github/dated-todo-upsert.mjs`,
);
const { runDatedTodoUpsert } = await import(moduleUrl.href);
await runDatedTodoUpsert({
github,
context,
core,
});