Skip to content

Agent / Project Manager #20

Agent / Project Manager

Agent / Project Manager #20

name: Agent / Project Manager
on:
schedule:
- cron: "17 */6 * * *" # Every 6 hours at minute 17 UTC
workflow_dispatch:
inputs:
enabled:
description: "Set true to run project management. Defaults off."
required: false
default: "false"
dry_run:
description: "Set false to allow label writes."
required: false
default: "true"
apply_labels:
description: "Set false to skip managed priority/effort label application."
required: false
default: "true"
post_summary:
description: "Set true to comment on today's Daily Summary discussion if it exists."
required: false
default: "false"
discussion_category:
description: "Discussion category where Daily Summary discussions are posted."
required: false
default: "General"
limit:
description: "Maximum open issues and PRs for the agent to inspect per kind."
required: false
default: "100"
permissions:
contents: read
discussions: write
issues: write
pull-requests: write
id-token: write # required for GitHub Actions OIDC broker exchange
concurrency:
group: agent-project-manager-${{ github.repository }}
cancel-in-progress: false
jobs:
gate:
if: ${{ vars.AGENT_ENABLED != 'false' && ((github.event_name == 'workflow_dispatch' && inputs.enabled == 'true') || (github.event_name != 'workflow_dispatch' && vars.AGENT_PROJECT_MANAGEMENT_ENABLED == 'true')) }}
runs-on: ${{ fromJson(vars.AGENT_RUNS_ON || '["ubuntu-latest"]') }}
outputs:
skip: ${{ steps.gate.outputs.skip }}
mode: ${{ steps.gate.outputs.mode }}
reason: ${{ steps.gate.outputs.reason }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
persist-credentials: false
ref: ${{ github.event.repository.default_branch }}
token: ${{ github.token }}
- name: Resolve scheduled activity gate
id: gate
uses: ./.github/actions/scheduled-activity-gate
with:
github_token: ${{ github.token }}
schedule_policy: ${{ vars.AGENT_SCHEDULE_POLICY || '' }}
workflow: agent-project-manager.yml
project-management:
needs: gate
if: vars.AGENT_ENABLED != 'false' && needs.gate.outputs.skip != 'true'
runs-on: ${{ fromJson(vars.AGENT_RUNS_ON || '["ubuntu-latest"]') }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
persist-credentials: false
ref: ${{ github.event.repository.default_branch }}
token: ${{ github.token }}
- name: Resolve project management configuration
id: project_config
shell: bash
env:
RAW_DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || vars.AGENT_PROJECT_MANAGEMENT_DRY_RUN || 'true' }}
RAW_APPLY_LABELS: ${{ github.event_name == 'workflow_dispatch' && inputs.apply_labels || vars.AGENT_PROJECT_MANAGEMENT_APPLY_LABELS || 'true' }}
RAW_POST_SUMMARY: ${{ github.event_name == 'workflow_dispatch' && inputs.post_summary || vars.AGENT_PROJECT_MANAGEMENT_POST_SUMMARY || 'false' }}
RAW_DISCUSSION_CATEGORY: ${{ github.event_name == 'workflow_dispatch' && inputs.discussion_category || vars.AGENT_PROJECT_MANAGEMENT_DISCUSSION_CATEGORY || 'General' }}
RAW_LIMIT: ${{ github.event_name == 'workflow_dispatch' && inputs.limit || vars.AGENT_PROJECT_MANAGEMENT_LIMIT || '100' }}
run: |
set -euo pipefail
normalize_bool() {
local value="${1:-}"
local default="${2:-false}"
value="$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
if [ -z "$value" ]; then
echo "$default"
return
fi
case "$value" in
1|true|yes|on) echo true ;;
*) echo false ;;
esac
}
positive_int_or_default() {
local value="${1:-}"
local default="${2:-100}"
if printf '%s' "$value" | grep -Eq '^[1-9][0-9]*$'; then
echo "$value"
else
echo "$default"
fi
}
dry_run="$(normalize_bool "$RAW_DRY_RUN" true)"
apply_labels="$(normalize_bool "$RAW_APPLY_LABELS" true)"
post_summary="$(normalize_bool "$RAW_POST_SUMMARY" false)"
discussion_category="${RAW_DISCUSSION_CATEGORY:-General}"
limit="$(positive_int_or_default "$RAW_LIMIT" 100)"
{
echo "dry_run=${dry_run}"
echo "apply_labels=${apply_labels}"
echo "post_summary=${post_summary}"
echo "discussion_category=${discussion_category}"
echo "limit=${limit}"
} >> "$GITHUB_OUTPUT"
- name: Resolve GitHub auth
id: auth
uses: ./.github/actions/resolve-github-auth
with:
app_id: ${{ secrets.AGENT_APP_ID }}
app_private_key: ${{ secrets.AGENT_APP_PRIVATE_KEY }}
pat: ${{ secrets.AGENT_PAT }}
fallback_token: ${{ github.token }}
- name: Resolve project manager provider
id: provider
uses: ./.github/actions/resolve-agent-provider
with:
route: project-manager
default_provider: ${{ vars.AGENT_DEFAULT_PROVIDER || 'auto' }}
openai_api_key: ${{ secrets.OPENAI_API_KEY }}
claude_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
model_policy: ${{ vars.AGENT_MODEL_POLICY || '' }}
- name: Setup selected provider
uses: ./.github/actions/setup-agent-runtime
with:
install_codex: ${{ steps.provider.outputs.install_codex }}
install_claude: ${{ steps.provider.outputs.install_claude }}
- name: Resolve task timeout
id: task_timeout
env:
AGENT_TASK_TIMEOUT_POLICY: ${{ vars.AGENT_TASK_TIMEOUT_POLICY || '' }}
ROUTE: answer
run: node .agent/dist/cli/resolve-task-timeout.js
- name: Run project manager agent
id: project_manager
timeout-minutes: ${{ fromJson(steps.task_timeout.outputs.minutes || '30') }}
uses: ./.github/actions/run-agent-task
with:
agent: ${{ steps.provider.outputs.provider }}
github_token: ${{ steps.auth.outputs.token }}
secondary_github_token: ${{ secrets.AGENT_SECONDARY_GITHUB_TOKEN }}
openai_api_key: ${{ secrets.OPENAI_API_KEY }}
claude_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
model: ${{ steps.provider.outputs.model }}
display_model: ${{ vars.AGENT_DISPLAY_MODEL || '' }}
permission_mode: approve-all
prompt: project-manager
route: answer
memory_mode_override: disabled
rubrics_mode_override: disabled
session_policy: none
request_text: |
Run the scheduled project-manager pass for ${{ github.repository }}.
Configuration:
- repository: ${{ github.repository }}
- limit per kind: ${{ steps.project_config.outputs.limit }}
- dry run: ${{ steps.project_config.outputs.dry_run }}
- apply labels after agent: ${{ steps.project_config.outputs.apply_labels }}
- post summary after agent: ${{ steps.project_config.outputs.post_summary }}
- Daily Summary discussion category: ${{ steps.project_config.outputs.discussion_category }}
- workflow schedule: every 6 hours at minute 17 UTC
requested_by: ${{ github.actor }}
source_kind: workflow_dispatch
target_kind: repository
target_number: '0'
target_url: ${{ github.server_url }}/${{ github.repository }}
reasoning_effort: ${{ steps.provider.outputs.reasoning_effort || 'medium' }}
workflow: agent-project-manager.yml
- name: Apply managed label plan
id: managed_labels
env:
AGENT_PROJECT_MANAGEMENT_APPLY_LABELS: ${{ steps.project_config.outputs.apply_labels }}
AGENT_PROJECT_MANAGEMENT_DRY_RUN: ${{ steps.project_config.outputs.dry_run }}
BODY_FILE: ${{ steps.project_manager.outputs.response_file }}
GH_TOKEN: ${{ steps.auth.outputs.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_TOKEN: ${{ steps.auth.outputs.token }}
run: node .agent/dist/cli/apply-project-management-labels.js
- name: Publish project management summary
env:
AGENT_PROJECT_MANAGEMENT_DISCUSSION_CATEGORY: ${{ steps.project_config.outputs.discussion_category }}
AGENT_PROJECT_MANAGEMENT_POST_SUMMARY: ${{ steps.project_config.outputs.post_summary }}
BODY: ${{ steps.managed_labels.outputs.summary }}
GH_TOKEN: ${{ steps.auth.outputs.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_TOKEN: ${{ steps.auth.outputs.token }}
run: node .agent/dist/cli/post-project-management-summary.js