diff --git a/.agents/skills/curate-whats-new/SKILL.md b/.agents/skills/curate-whats-new/SKILL.md index 446416c9b0b..fb9bd323906 100644 --- a/.agents/skills/curate-whats-new/SKILL.md +++ b/.agents/skills/curate-whats-new/SKILL.md @@ -53,9 +53,13 @@ status only when the relative importance of the candidate set changes. 1. Read `data/whats-new.json`. 2. Determine the review mode from the request: + - If the request does not specify a publication window, end it on the + previous UTC day and start it 29 days earlier. - For an incremental review, list PRs merged from the day after the supplied checkpoint through the end of the publication window. Retain existing items inside the publication window without re-reviewing their source PRs. + If the request does not supply a checkpoint, use the existing + `period_end` from `data/whats-new.json`. - For a full review, inspect every PR merged in the supplied publication window. 3. List PRs in the range that applies to the review mode: @@ -74,8 +78,19 @@ status only when the relative importance of the candidate set changes. merely because they were not part of the incremental candidate range. 7. Replace `period_start`, `period_end`, and `items` in `data/whats-new.json`. Sort items by `published` date, newest first. -8. Write `.pr-body.md` with the publication period, selected highlights and - source PRs, plus concise reasons for plausible exclusions. +8. Report the curation outcome in the response: + - Enumerate every newly selected entry by product, title, and source PRs. + - Summarize retained and expired entries by count. Identify any retained + entry whose content or sources changed. + - Explain only the most relevant plausible exclusions. Do not enumerate + routine maintenance PRs that were never credible candidates. + +If the request includes creating a pull request, use the same report in the PR +body with `## Summary`, `## Selected entries`, and `## Curation notes` +sections. Put each selected entry on one Markdown bullet and reference PRs as +`#NNNN` rather than full same-repository links. Do not hard-wrap bullets. Do +not create `.pr-body.md` or another handoff file unless the request explicitly +asks for one. Each item must contain `product`, `title`, `description`, `url`, `published`, `source_prs`, and `featured`. Use the canonical product name, a published diff --git a/.github/agents/whats-new.yaml b/.github/agents/whats-new.yaml deleted file mode 100644 index 73f6894f274..00000000000 --- a/.github/agents/whats-new.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/docker/docker-agent/refs/heads/main/agent-schema.json -models: - claude-sonnet-5: - provider: anthropic - model: claude-sonnet-5 - max_tokens: 4096 - -agents: - root: - model: claude-sonnet-5 - description: Curates user-facing Docker launches from recently published documentation - instruction: | - Use the curate-whats-new skill for every request. - skills: - - curate-whats-new - - toolsets: - - type: filesystem - tools: - - read_file - - read_multiple_files - - write_file - - list_directory - - directory_tree - - type: shell - -permissions: - allow: - - shell:cmd=gh pr list --* - - shell:cmd=gh pr view --* - - shell:cmd=gh pr diff --* - - shell:cmd=git diff --* - - shell:cmd=git log --* diff --git a/.github/workflows/update-whats-new.yml b/.github/workflows/update-whats-new.yml deleted file mode 100644 index 112ce7066f9..00000000000 --- a/.github/workflows/update-whats-new.yml +++ /dev/null @@ -1,210 +0,0 @@ -name: Update What's New - -on: - schedule: - # Review new merges and expire old highlights every day at 06:00 UTC. - - cron: "0 6 * * *" - workflow_dispatch: - inputs: - dry-run: - description: "Propose highlights without opening a pull request" - type: boolean - default: false - full-rescan: - description: "Re-evaluate every pull request in the 30-day window" - type: boolean - default: false - -permissions: - contents: write - pull-requests: write - id-token: write - -concurrency: - group: update-whats-new - cancel-in-progress: false - -env: - BRANCH_NAME: bot/update-whats-new - -jobs: - update: - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout repository - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - with: - fetch-depth: 0 - - - name: Set publication period - id: period - run: | - PERIOD_END=$(date -u -d 'yesterday' +%F) - echo "start=$(date -u -d "$PERIOD_END - 29 days" +%F)" >> "$GITHUB_OUTPUT" - echo "end=$PERIOD_END" >> "$GITHUB_OUTPUT" - - - name: Restore curator state - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: ${{ runner.temp }}/whats-new-state.json - key: whats-new-state-${{ github.repository }}-${{ github.run_id }} - restore-keys: | - whats-new-state-${{ github.repository }}- - - - name: Configure AWS credentials - id: aws-credentials - continue-on-error: true - uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6.1.1 - with: - role-to-assume: arn:aws:iam::710015040892:role/docker-agent-action-20260409141318957000000001 - aws-region: us-east-1 - - - name: Fetch bot token - if: steps.aws-credentials.outcome == 'success' - run: | - PAT=$(aws secretsmanager get-secret-value \ - --secret-id docker-agent-action/github-app \ - --query SecretString \ - --output text | jq -r '.pat') - echo "::add-mask::$PAT" - echo "GITHUB_APP_TOKEN=$PAT" >> "$GITHUB_ENV" - - - name: Restore open proposal - env: - GH_TOKEN: ${{ env.GITHUB_APP_TOKEN || github.token }} - run: | - EXISTING_PR=$(gh pr list --state open --head "$BRANCH_NAME" --json number --jq '.[0].number // empty') - if [ -n "$EXISTING_PR" ]; then - git fetch origin "$BRANCH_NAME:refs/remotes/origin/$BRANCH_NAME" - git show "origin/$BRANCH_NAME:data/whats-new.json" > data/whats-new.json - fi - - - name: Set review mode and checkpoint - id: review - env: - FULL_RESCAN: ${{ inputs.full-rescan == true }} - PERIOD_END: ${{ steps.period.outputs.end }} - STATE_FILE: ${{ runner.temp }}/whats-new-state.json - run: | - REVIEWED_THROUGH=$(jq -r '.reviewed_through // empty' "$STATE_FILE" 2>/dev/null || true) - if ! [[ "$REVIEWED_THROUGH" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then - REVIEWED_THROUGH=$(jq -r '.period_end' data/whats-new.json) - fi - DATA_PERIOD_END=$(jq -r '.period_end' data/whats-new.json) - if [[ "$DATA_PERIOD_END" > "$REVIEWED_THROUGH" ]]; then - REVIEWED_THROUGH=$DATA_PERIOD_END - fi - - if [ "$FULL_RESCAN" = "true" ]; then - MODE=full - else - MODE=incremental - fi - - jq -n --arg reviewed_through "$PERIOD_END" \ - '{reviewed_through: $reviewed_through}' > "$STATE_FILE" - - { - echo "mode=$MODE" - echo "reviewed-through=$REVIEWED_THROUGH" - } >> "$GITHUB_OUTPUT" - - - name: Curate highlights - uses: docker/docker-agent-action@e96a4bb40cac114f64358621e1d08346c8eadc8c # v2.0.1 - env: - GH_TOKEN: ${{ env.GITHUB_APP_TOKEN || github.token }} - with: - agent: ${{ github.workspace }}/.github/agents/whats-new.yaml - prompt: >- - Use the curate-whats-new skill in ${{ steps.review.outputs.mode }} mode. - The publication window is ${{ steps.period.outputs.start }} through - ${{ steps.period.outputs.end }}, inclusive. The last successful incremental - review covered through ${{ steps.review.outputs.reviewed-through }}. - anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} - github-token: ${{ env.GITHUB_APP_TOKEN || github.token }} - timeout: 1200 - - - name: Verify agent changes - env: - PERIOD_START: ${{ steps.period.outputs.start }} - PERIOD_END: ${{ steps.period.outputs.end }} - run: | - CHANGED=$(git status --short | awk '$2 != "data/whats-new.json" && $2 != ".pr-body.md" { print }') - if [ -n "$CHANGED" ]; then - echo "::error::Agent changed files outside its allowed scope" - echo "$CHANGED" - exit 1 - fi - test -f .pr-body.md - npx prettier --check data/whats-new.json - node hack/validate-whats-new.mjs data/whats-new.json "$PERIOD_START" "$PERIOD_END" - - - name: Show proposal - if: inputs.dry-run == true - run: | - git diff -- data/whats-new.json - cat .pr-body.md - - - name: Detect changes - id: changes - if: inputs.dry-run != true - run: | - if cmp -s \ - <(git show HEAD:data/whats-new.json | jq -S '.items') \ - <(jq -S '.items' data/whats-new.json); then - echo "changed=false" >> "$GITHUB_OUTPUT" - else - echo "changed=true" >> "$GITHUB_OUTPUT" - fi - - - name: Close obsolete pull request - if: steps.changes.outputs.changed == 'false' - env: - GH_TOKEN: ${{ env.GITHUB_APP_TOKEN || github.token }} - run: | - EXISTING_PR=$(gh pr list --state open --head "$BRANCH_NAME" --json url --jq '.[0].url // empty') - if [ -n "$EXISTING_PR" ]; then - gh pr close "$EXISTING_PR" \ - --comment "The latest sliding-window review no longer proposes changes to the published highlights." - fi - - - name: Commit changes - if: steps.changes.outputs.changed == 'true' - env: - PERIOD_END: ${{ steps.period.outputs.end }} - run: | - git checkout -B "$BRANCH_NAME" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add data/whats-new.json - git commit -m "docs: update what's new for $PERIOD_END" - - - name: Create or update pull request - if: steps.changes.outputs.changed == 'true' - env: - GH_TOKEN: ${{ env.GITHUB_APP_TOKEN || github.token }} - run: | - gh auth setup-git - EXISTING_PR=$(gh pr list --state open --head "$BRANCH_NAME" --json url --jq '.[0].url // empty') - - if [ -n "$EXISTING_PR" ]; then - git push --force --set-upstream origin "$BRANCH_NAME" - gh pr edit "$EXISTING_PR" \ - --title "docs: update what's new" \ - --body-file .pr-body.md - else - git push --force --set-upstream origin "$BRANCH_NAME" - gh pr create \ - --title "docs: update what's new" \ - --body-file .pr-body.md \ - --base main \ - --head "$BRANCH_NAME" - fi - - - name: Save curator state - if: success() && inputs.dry-run != true && steps.changes.outputs.changed != 'true' - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: ${{ runner.temp }}/whats-new-state.json - key: whats-new-state-${{ github.repository }}-${{ github.run_id }}