diff --git a/.github/workflows/campaign-manager.lock.yml b/.github/workflows/campaign-manager.lock.yml index fde9369cfa8..e9af039e9ed 100644 --- a/.github/workflows/campaign-manager.lock.yml +++ b/.github/workflows/campaign-manager.lock.yml @@ -490,9 +490,20 @@ jobs: "type": "string", "enum": [ "issue", - "pull_request" + "pull_request", + "draft_issue" ] }, + "draft_body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "draft_title": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, "fields": { "type": "object" }, @@ -510,7 +521,8 @@ jobs: "pull_request": { "optionalPositiveInteger": true } - } + }, + "customValidation": "updateProjectValidTarget" } } EOF diff --git a/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml b/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml index 272e3a69af9..4a75c022759 100644 --- a/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml +++ b/.github/workflows/docs-quality-maintenance-project67.campaign.lock.yml @@ -365,9 +365,20 @@ jobs: "type": "string", "enum": [ "issue", - "pull_request" + "pull_request", + "draft_issue" ] }, + "draft_body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "draft_title": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, "fields": { "type": "object" }, @@ -385,7 +396,8 @@ jobs: "pull_request": { "optionalPositiveInteger": true } - } + }, + "customValidation": "updateProjectValidTarget" } } EOF @@ -515,7 +527,7 @@ jobs: This workflow orchestrates the 'Documentation Quality & Maintenance Campaign (Project 67)' campaign. - - Tracker label: `campaign:docs-quality-maintenance-project67` + - Tracker label (optional ingestion): `campaign:docs-quality-maintenance-project67` - Objective: Maintain high-quality, accessible, and consistent documentation following the Diátaxis framework while ensuring all docs are accurate, complete, and user-friendly - KPIs: - Documentation coverage of features (primary): baseline 85 → target 95 over 90 days percent @@ -534,7 +546,7 @@ jobs: ## Campaign Orchestrator Rules - This orchestrator follows system-agnostic rules that enforce clean separation between workers and campaign coordination. It also maintains the campaign dashboard by ensuring the GitHub Project stays in sync with the campaign's tracker label. + This orchestrator follows system-agnostic rules that enforce clean separation between workers and campaign coordination. The GitHub Project is the single source of truth for campaign membership and state. ### Traffic and rate limits (required) @@ -592,7 +604,7 @@ jobs: - Use an ISO date (UTC) filename, for example: `metrics/2025-12-22.json`. - Keep snapshots append-only: write a new file per run; do not rewrite historical snapshots. - If a KPI is present, record its computed value and trend (Improving/Flat/Regressing) in the kpi_trends array. - - Count tasks from all sources: tracker-labeled issues, worker-created issues, and project board items. + - Count tasks from all sources: project board items (canonical), plus any newly discovered tracker-labeled or worker-created items that will be added. - Set tasks_total to the total number of unique tasks discovered in this run. - Set tasks_completed to the count of tasks with state "Done" or closed status. @@ -617,7 +629,7 @@ jobs: 10. **Predefined fields only** - Only update explicitly defined project board fields 11. **Explicit outcomes** - Record actual outcomes, never infer status 12. **Idempotent operations** - Re-execution produces the same result without corruption - 13. **Dashboard synchronization** - Keep Project items in sync with tracker-labeled issues/PRs + 13. **Dashboard synchronization** - Keep the Project board in sync with discovered work (Project items are canonical; tracker labels are optional ingestion) ### Objective and KPIs (first-class) @@ -656,13 +668,18 @@ jobs: #### Phase 1: Read State (Discovery) - 1. **Query tracker-labeled items** - Search for issues and PRs matching the campaign's tracker label + 1. **Query current project state (canonical)** - Read the GitHub Project board + - Retrieve all items currently on the project board + - For each item, record: content type, URL (if present), draft title (if present), status field value, and other predefined field values + - Create a snapshot of current board state + + 2. **Query tracker-labeled items (optional ingestion)** - If a tracker label is configured, search for issues and PRs matching it - Search: `repo:OWNER/REPO label:TRACKER_LABEL` for all open and closed items - If governance opt-out labels are configured, exclude items with those labels - Collect all matching issue/PR URLs - Record metadata: number, title, state (open/closed), created date, updated date - 2. **Query worker-created content** (if workers are configured) - Search for issues, PRs, and discussions containing worker tracker-ids + 3. **Query worker-created content** (if workers are configured) - Search for issues, PRs, and discussions containing worker tracker-ids - Worker workflows: daily-doc-updater, docs-noob-tester, daily-multi-device-docs-tester, unbloat-docs, developer-docs-consolidator, technical-doc-writer - **IMPORTANT**: You MUST perform SEPARATE searches for EACH worker workflow listed above - **IMPORTANT**: Workers may create different types of content (issues, PRs, discussions, comments). Search ALL content types to discover all worker outputs. @@ -702,18 +719,29 @@ jobs: - Combine results from all worker searches into a single list of discovered items - Note: Comments are discovered via their parent issue/PR - the issue/PR is what gets added to the board - 3. **Query current project state** - Read the GitHub Project board - - Retrieve all items currently on the project board - - For each item, record: issue URL, status field value, other predefined field values - - Create a snapshot of current board state - - 4. **Compare and identify gaps** - Analyze current state (for reporting only - do NOT use this to filter items in Phase 3) - - Items from step 1 or 2 not on board = **new work discovered** (report count) - - Items on board with state mismatch = **status updates needed** (report count) + 4. **Merge and identify gaps** - Analyze current state (for reporting only - do NOT use this to filter items in Phase 3) + - Items on the board are **in scope** by definition (canonical membership) + - Items from steps 2-3 not on board = **new work discovered** (report count) + - Items on board with state mismatch vs issue/PR state = **status updates needed** (report count) - Items on board with missing custom fields (e.g., worker_workflow) = **fields to populate** (report count) - - Items on board but no longer found = **check if archived/deleted** (report count) + - Items on board but no longer accessible = **check if archived/deleted** (report count) - **CRITICAL**: This comparison is for reporting and planning only. In Phase 3, you MUST send ALL discovered items to update-project regardless of whether they appear to be on the board. The update-project tool handles duplicate detection automatically. + 4.8 **Locate the campaign hub issue (optional but recommended)** + + If you have permission and comment writes are allowed, attempt to locate the campaign hub (“epic”) issue for posting per-run summaries. + + Deterministic matching rules: + - Prefer an issue that has BOTH labels: `campaign-tracker` AND `campaign:docs-quality-maintenance-project67`. + - If none found, do NOT guess. Proceed without an epic issue comment. + - If multiple matches, treat as ambiguous and proceed without commenting (report ambiguity). + + If a single hub issue is found, treat it as an in-scope item for synchronization: + - Include it in the Phase 3 `update-project` operations so it is present on the Project board (idempotent). + - When updating the hub issue item, set (when the fields exist): + - `worker_workflow = "orchestrator"` + - `human_oversight_required = "Yes"` + #### Phase 2: Make Decisions (Planning) 4.5 **Deterministic planner step (required when objective/KPIs are present)** @@ -736,12 +764,13 @@ jobs: } ``` - 5. **Decide processing order (with pacing)** - For items discovered in steps 1-2: - - **CRITICAL**: ALL discovered items (both tracker-labeled from step 1 AND worker-created from step 2) MUST be sent to update-project in Phase 3, regardless of whether they appear to already be on the board. The update-project tool handles idempotency automatically. + 5. **Decide processing order (with pacing)** - For items discovered in steps 1-3: + - **CRITICAL**: ALL discovered items (project items from step 1, tracker-labeled from step 2, and worker-created from step 3) MUST be sent to update-project in Phase 3, regardless of whether they appear to already be on the board. The update-project tool handles idempotency automatically. - If `governance.max-new-items-per-run` is set, process at most that many items in this single run (remaining items will be processed in subsequent runs) - When applying the governance limit, prioritize in this order: - 1. Tracker-labeled items (campaign tasks) - process oldest first - 2. Worker-created items (worker outputs) - process oldest first + 1. Project board items (canonical scope) - process oldest first + 2. Tracker-labeled items (optional ingestion) - process oldest first + 3. Worker-created items (worker outputs) - process oldest first - Determine appropriate status field value based on item state: - Open issue/PR/discussion → "Todo" status - Closed issue/discussion → "Done" status @@ -770,12 +799,18 @@ jobs: #### Phase 3: Write State (Execution) - **CRITICAL RULE**: In this phase, you MUST send update-project requests for ALL discovered items from steps 1-2, regardless of whether they appear to already be on the board. The update-project tool handles duplicate detection and idempotency automatically. Do NOT pre-filter items based on board state. + **CRITICAL RULE**: In this phase, you MUST send update-project requests for ALL discovered items from steps 1-3, regardless of whether they appear to already be on the board. The update-project tool handles duplicate detection and idempotency automatically. Do NOT pre-filter items based on board state. 8. **Execute project updates** - Send update-project for ALL discovered items - - Process ALL items from steps 1-2 (both tracker-labeled and worker-created), up to the governance limit if set + - Process ALL items from steps 1-3 (project items, tracker-labeled, and worker-created), up to the governance limit if set - Use `update-project` safe-output for EVERY discovered item - - Include fields from steps 5-6.5: `status`, `worker_workflow`, `priority`, `size`, etc. + PROMPT_EOF + - name: Append prompt (part 2) + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: | + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + - Include fields from steps 5-6.5: `status`, `worker_workflow`, `human_oversight_required`, `priority`, `size`, etc. - **The update-project tool will automatically**: - Skip adding items that are already on the board (idempotent add) - Update fields for items already on the board @@ -793,27 +828,36 @@ jobs: #### Phase 4: Report (Output) 10. **Generate status report** - Summarize execution results: - - Total items discovered via tracker label (by type: issues, PRs) - - Total items discovered via worker tracker-ids (by type: issues, PRs, discussions) - PROMPT_EOF - - name: Append prompt (part 2) - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: | - cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" - - Items processed with update-project this run (count and URLs, broken down by: tracker-labeled vs worker-created) + - Total items currently on the project board (canonical) + - Total items discovered via tracker label (optional ingestion, by type: issues, PRs) + - Total items discovered via worker tracker-ids (by type: issues, PRs, discussions) + - Items processed with update-project this run (count and URLs, broken down by: project-board vs tracker-labeled vs worker-created) - Items skipped due to governance limits (count, type, and why - noting they will be processed in next run) - Current campaign metrics: open vs closed, progress percentage - Any failures encountered during update-project operations - Campaign completion status + 11. **Post a hub issue comment (if hub issue was found and add-comment is allowed)** + + Post a short comment to the campaign hub issue that includes: + - Link to the Project board + - What was processed this run (counts + a few representative URLs) + - The current “Needs human” queue size (items with `human_oversight_required = Yes` if that field is used) + - If repo-memory metrics are enabled, include the path to the latest metrics snapshot written this run + + Do not paste large JSON blobs. Keep the comment concise and human-scannable. + ### Predefined Project Fields Only these fields may be updated on the project board: - - `status` (required) - Values: "Todo", "In Progress", "Done" + - `status` (required) - Values: "Todo", "In Progress", "Blocked", "Done" + - `worker_workflow` (optional) - String (recommended: the worker workflow ID/name) + - `human_oversight_required` (optional) - Values: "Yes", "No" (powers a dedicated human review queue) - `priority` (optional) - Values: "High", "Medium", "Low" - `size` (optional) - Values: "Small", "Medium", "Large" + - `start_date` (optional) - ISO date YYYY-MM-DD (if the project has a matching field) + - `end_date` (optional) - ISO date YYYY-MM-DD (if the project has a matching field) - `campaign_status` (metadata) - Values: "active", "completed" Do NOT update any other fields or create custom fields. @@ -843,9 +887,7 @@ jobs: Execute state writes using the `update-project` safe-output. All writes must target this exact project URL: **Project URL**: https://github.com/orgs/githubnext/projects/67 - - **Campaign ID**: Extract from tracker label `campaign:docs-quality-maintenance-project67` (format: `campaign:CAMPAIGN_ID`) - + **Campaign ID**: `docs-quality-maintenance-project67` (recommended to tag items via the optional `campaign_id` field) #### Adding New Issues/PRs @@ -855,7 +897,7 @@ jobs: project: "https://github.com/orgs/githubnext/projects/67" content_type: "issue" # or "pull_request" content_number: 123 # Extract number from URL like https://github.com/owner/repo/issues/123 - campaign_id: "CAMPAIGN_ID" # Required: extract from tracker label campaign:docs-quality-maintenance-project67 + campaign_id: "docs-quality-maintenance-project67" # Optional: tags items for this campaign fields: status: "Todo" # or "Done" if issue/PR is already closed/merged ``` @@ -879,8 +921,9 @@ jobs: content_number: 123 # Extract from URL fields: status: "Todo" # or "In Progress", "Blocked", "Done" - campaign_id: "CAMPAIGN_ID" # Extract from tracker label campaign:docs-quality-maintenance-project67 + campaign_id: "docs-quality-maintenance-project67" # Optional: tags items for this campaign worker_workflow: "WORKFLOW_ID" # Enables swimlane grouping and filtering + human_oversight_required: "No" # or "Yes" - powers a dedicated human review queue priority: "High" # or "Medium", "Low" - enables priority-based views effort: "Medium" # or "Small", "Large" - enables capacity planning team: "TEAM_NAME" # Optional: for team-based grouping @@ -889,6 +932,7 @@ jobs: **Custom Field Benefits**: - `worker_workflow`: Groups items by workflow in Roadmap swimlanes; enables "Slice by" filtering in Table views (orchestrator populates this by discovering which worker created the item via tracker-id) + - `human_oversight_required`: Creates an explicit human review queue; use a "Needs human" view filtered to "Yes" - `priority`: Enables priority-based filtering and sorting - `effort`: Supports capacity planning and workload distribution - `team`: Enables team-based grouping for multi-team campaigns @@ -906,7 +950,7 @@ jobs: project: "https://github.com/orgs/githubnext/projects/67" content_type: "issue" # or "pull_request" content_number: 123 # Extract from URL - campaign_id: "CAMPAIGN_ID" # Required: extract from tracker label campaign:docs-quality-maintenance-project67 + campaign_id: "docs-quality-maintenance-project67" # Optional: tags items for this campaign fields: status: "Done" # or "In Progress", "Todo" ``` diff --git a/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml b/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml index c8ee37d9148..3d014fa7828 100644 --- a/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml +++ b/.github/workflows/go-file-size-reduction-project64.campaign.lock.yml @@ -365,9 +365,20 @@ jobs: "type": "string", "enum": [ "issue", - "pull_request" + "pull_request", + "draft_issue" ] }, + "draft_body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "draft_title": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, "fields": { "type": "object" }, @@ -385,7 +396,8 @@ jobs: "pull_request": { "optionalPositiveInteger": true } - } + }, + "customValidation": "updateProjectValidTarget" } } EOF @@ -515,7 +527,7 @@ jobs: This workflow orchestrates the 'Go File Size Reduction Campaign (Project 64)' campaign. - - Tracker label: `campaign:go-file-size-reduction-project64` + - Tracker label (optional ingestion): `campaign:go-file-size-reduction-project64` - Objective: Reduce all Go files to ≤800 lines of code while maintaining test coverage and preventing regressions - KPIs: - Files reduced to target size (primary): baseline 0 → target 100 over 90 days percent @@ -533,7 +545,7 @@ jobs: ## Campaign Orchestrator Rules - This orchestrator follows system-agnostic rules that enforce clean separation between workers and campaign coordination. It also maintains the campaign dashboard by ensuring the GitHub Project stays in sync with the campaign's tracker label. + This orchestrator follows system-agnostic rules that enforce clean separation between workers and campaign coordination. The GitHub Project is the single source of truth for campaign membership and state. ### Traffic and rate limits (required) @@ -591,7 +603,7 @@ jobs: - Use an ISO date (UTC) filename, for example: `metrics/2025-12-22.json`. - Keep snapshots append-only: write a new file per run; do not rewrite historical snapshots. - If a KPI is present, record its computed value and trend (Improving/Flat/Regressing) in the kpi_trends array. - - Count tasks from all sources: tracker-labeled issues, worker-created issues, and project board items. + - Count tasks from all sources: project board items (canonical), plus any newly discovered tracker-labeled or worker-created items that will be added. - Set tasks_total to the total number of unique tasks discovered in this run. - Set tasks_completed to the count of tasks with state "Done" or closed status. @@ -616,7 +628,7 @@ jobs: 10. **Predefined fields only** - Only update explicitly defined project board fields 11. **Explicit outcomes** - Record actual outcomes, never infer status 12. **Idempotent operations** - Re-execution produces the same result without corruption - 13. **Dashboard synchronization** - Keep Project items in sync with tracker-labeled issues/PRs + 13. **Dashboard synchronization** - Keep the Project board in sync with discovered work (Project items are canonical; tracker labels are optional ingestion) ### Objective and KPIs (first-class) @@ -653,13 +665,18 @@ jobs: #### Phase 1: Read State (Discovery) - 1. **Query tracker-labeled items** - Search for issues and PRs matching the campaign's tracker label + 1. **Query current project state (canonical)** - Read the GitHub Project board + - Retrieve all items currently on the project board + - For each item, record: content type, URL (if present), draft title (if present), status field value, and other predefined field values + - Create a snapshot of current board state + + 2. **Query tracker-labeled items (optional ingestion)** - If a tracker label is configured, search for issues and PRs matching it - Search: `repo:OWNER/REPO label:TRACKER_LABEL` for all open and closed items - If governance opt-out labels are configured, exclude items with those labels - Collect all matching issue/PR URLs - Record metadata: number, title, state (open/closed), created date, updated date - 2. **Query worker-created content** (if workers are configured) - Search for issues, PRs, and discussions containing worker tracker-ids + 3. **Query worker-created content** (if workers are configured) - Search for issues, PRs, and discussions containing worker tracker-ids - Worker workflows: daily-file-diet - **IMPORTANT**: You MUST perform SEPARATE searches for EACH worker workflow listed above - **IMPORTANT**: Workers may create different types of content (issues, PRs, discussions, comments). Search ALL content types to discover all worker outputs. @@ -674,16 +691,28 @@ jobs: - Combine results from all worker searches into a single list of discovered items - Note: Comments are discovered via their parent issue/PR - the issue/PR is what gets added to the board - 3. **Query current project state** - Read the GitHub Project board - - Retrieve all items currently on the project board - - For each item, record: issue URL, status field value, other predefined field values - - Create a snapshot of current board state + 4. **Merge and identify gaps** - Analyze current state (for reporting only - do NOT use this to filter items in Phase 3) + - Items on the board are **in scope** by definition (canonical membership) + - Items from steps 2-3 not on board = **new work discovered** (report count) + - Items on board with state mismatch vs issue/PR state = **status updates needed** (report count) + - Items on board with missing custom fields (e.g., worker_workflow) = **fields to populate** (report count) + - Items on board but no longer accessible = **check if archived/deleted** (report count) + - **CRITICAL**: This comparison is for reporting and planning only. In Phase 3, you MUST send ALL discovered items to update-project regardless of whether they appear to be on the board. The update-project tool handles duplicate detection automatically. + + 4.8 **Locate the campaign hub issue (optional but recommended)** - 4. **Compare and identify gaps** - Determine what needs updating - - Items from step 1 or 2 not on board = **new work to add** - - Items on board with state mismatch = **status to update** - - Items on board with missing custom fields (e.g., worker_workflow) = **fields to populate** - - Items on board but no longer found = **check if archived/deleted** + If you have permission and comment writes are allowed, attempt to locate the campaign hub (“epic”) issue for posting per-run summaries. + + Deterministic matching rules: + - Prefer an issue that has BOTH labels: `campaign-tracker` AND `campaign:go-file-size-reduction-project64`. + - If none found, do NOT guess. Proceed without an epic issue comment. + - If multiple matches, treat as ambiguous and proceed without commenting (report ambiguity). + + If a single hub issue is found, treat it as an in-scope item for synchronization: + - Include it in the Phase 3 `update-project` operations so it is present on the Project board (idempotent). + - When updating the hub issue item, set (when the fields exist): + - `worker_workflow = "orchestrator"` + - `human_oversight_required = "Yes"` #### Phase 2: Make Decisions (Planning) @@ -707,32 +736,34 @@ jobs: } ``` - 5. **Decide additions (with pacing)** - For each new item discovered: - - **CRITICAL**: All discovered items (both tracker-labeled from step 1 AND worker-created from step 2) MUST be added to the board to maintain synchronization between the campaign state and the project board. - - Decision: Add to board? (Default: **YES** for all items with tracker label or worker tracker-id) - - If `governance.max-new-items-per-run` is set, add at most that many new items in this single run (remaining items will be added in subsequent runs) + 5. **Decide processing order (with pacing)** - For items discovered in steps 1-3: + - **CRITICAL**: ALL discovered items (project items from step 1, tracker-labeled from step 2, and worker-created from step 3) MUST be sent to update-project in Phase 3, regardless of whether they appear to already be on the board. The update-project tool handles idempotency automatically. + - If `governance.max-new-items-per-run` is set, process at most that many items in this single run (remaining items will be processed in subsequent runs) - When applying the governance limit, prioritize in this order: - 1. Tracker-labeled items (campaign tasks) - add oldest first - 2. Worker-created items (worker outputs) - add oldest first - - Determine initial status field value based on item state: + 1. Project board items (canonical scope) - process oldest first + 2. Tracker-labeled items (optional ingestion) - process oldest first + 3. Worker-created items (worker outputs) - process oldest first + - Determine appropriate status field value based on item state: - Open issue/PR/discussion → "Todo" status - Closed issue/discussion → "Done" status - Merged PR → "Done" status + - **IMPORTANT**: Do NOT skip items that appear to be on the board already. Step 4 comparison is for reporting only. In Phase 3, send ALL items to update-project. - 6. **Decide updates (no downgrade)** - For each existing board item with mismatched state: - - Decision: Update status field? (Default: yes if item state changed) - - If `governance.do-not-downgrade-done-items` is true, do not move items from Done back to active status - - Determine new status field value: + 6. **Decide updates (no downgrade)** - For status field value determination: + - Determine appropriate status based on item state (open/closed/merged) + - If `governance.do-not-downgrade-done-items` is true, preserve "Done" status for items that are already marked as done on the board + - Status field mapping: - Open issue/PR/discussion → "In Progress" or "Todo" - Closed issue/discussion → "Done" - Merged PR → "Done" + - **IMPORTANT**: This is for determining what status value to send to update-project, not for deciding whether to send the request. Send ALL discovered items to update-project in Phase 3. - 6.5 **Decide field updates** - For each existing board item, check for missing custom fields: - - If item is missing `worker_workflow` field: - - Search item body (issue/PR/discussion) for tracker-id (e.g., ``) - - If tracker-id matches a worker in `workflows`, populate `worker_workflow` field with that worker ID - - Only update fields that exist on the project board - - Skip items that already have all required fields populated + 6.5 **Decide field values** - For custom field population: + - Determine which custom fields should be populated based on item metadata + - If item has a worker tracker-id in its body (e.g., ``): + - Extract the worker ID and prepare to populate `worker_workflow` field + - Prepare other custom field values based on item properties + - **IMPORTANT**: This is for determining field values to send, not for filtering items. Send ALL discovered items to update-project in Phase 3. 7. **Decide completion** - Check campaign completion criteria: - If all discovered items (issues/PRs/discussions) are closed/merged AND all board items are "Done" → Campaign complete @@ -740,49 +771,65 @@ jobs: #### Phase 3: Write State (Execution) - 8. **Execute additions** - Add new items to project board - - Add ALL items identified in step 5 (both tracker-labeled and worker-created), up to the governance limit if set - - Use `update-project` safe-output for each new item - - Set predefined fields: `status` (required), optionally `priority`, `size` - - If worker tracker-id is found in item body (issue/PR/discussion), populate `worker_workflow` field - - Record outcome: success or failure with error details - - If governance limit is reached, log remaining items and note they will be processed in the next run + **CRITICAL RULE**: In this phase, you MUST send update-project requests for ALL discovered items from steps 1-3, regardless of whether they appear to already be on the board. The update-project tool handles duplicate detection and idempotency automatically. Do NOT pre-filter items based on board state. - 9. **Execute status updates** - Update existing board items with status changes - - Use `update-project` safe-output for each status change - - Update only predefined fields: `status` and related metadata - - Record outcome: success or failure with error details - - 9.5 **Execute field updates** - Update existing board items with missing custom fields - - Use `update-project` safe-output for each item with missing fields - - Populate missing fields identified in step 6.5 (e.g., `worker_workflow`) + 8. **Execute project updates** - Send update-project for ALL discovered items + - Process ALL items from steps 1-3 (project items, tracker-labeled, and worker-created), up to the governance limit if set + - Use `update-project` safe-output for EVERY discovered item + - Include fields from steps 5-6.5: `status`, `worker_workflow`, `human_oversight_required`, `priority`, `size`, etc. + - **The update-project tool will automatically**: + - Skip adding items that are already on the board (idempotent add) + - Update fields for items already on the board + - Add new items that are not yet on the board - Record outcome: success or failure with error details + - If governance limit is reached, log remaining items and note they will be processed in the next run + - **DO NOT**: Check if items are already on the board before sending requests - this causes synchronization bugs + - **DO NOT**: Skip items that appear to be on the board - send them all and let the tool handle idempotency - 10. **Record completion state** - If campaign is complete: + 9. **Record completion state** - If campaign is complete: - Mark project metadata field `campaign_status` as "completed" - Do NOT create new work or modify existing items - This is a terminal state #### Phase 4: Report (Output) - 11. **Generate status report** - Summarize execution results: - - Total items discovered via tracker label (by type: issues, PRs) - - Total items discovered via worker tracker-ids (by type: issues, PRs, discussions) - - Items added to board this run (count and URLs, broken down by: tracker-labeled vs worker-created) - - Items updated on board this run (count and status changes) - - Items with fields populated this run (count and which fields, e.g., worker_workflow) - - Items skipped due to governance limits (count, type, and why - noting they will be added in next run) + 10. **Generate status report** - Summarize execution results: + - Total items currently on the project board (canonical) + - Total items discovered via tracker label (optional ingestion, by type: issues, PRs) + - Total items discovered via worker tracker-ids (by type: issues, PRs, discussions) + - Items processed with update-project this run (count and URLs, broken down by: project-board vs tracker-labeled vs worker-created) + - Items skipped due to governance limits (count, type, and why - noting they will be processed in next run) - Current campaign metrics: open vs closed, progress percentage - - Any failures encountered during writes + - Any failures encountered during update-project operations - Campaign completion status + 11. **Post a hub issue comment (if hub issue was found and add-comment is allowed)** + + Post a short comment to the campaign hub issue that includes: + - Link to the Project board + - What was processed this run (counts + a few representative URLs) + - The current “Needs human” queue size (items with `human_oversight_required = Yes` if that field is used) + - If repo-memory metrics are enabled, include the path to the latest metrics snapshot written this run + + Do not paste large JSON blobs. Keep the comment concise and human-scannable. + ### Predefined Project Fields Only these fields may be updated on the project board: - - `status` (required) - Values: "Todo", "In Progress", "Done" + - `status` (required) - Values: "Todo", "In Progress", "Blocked", "Done" + - `worker_workflow` (optional) - String (recommended: the worker workflow ID/name) + - `human_oversight_required` (optional) - Values: "Yes", "No" (powers a dedicated human review queue) - `priority` (optional) - Values: "High", "Medium", "Low" - `size` (optional) - Values: "Small", "Medium", "Large" + - `start_date` (optional) - ISO date YYYY-MM-DD (if the project has a matching field) + - `end_date` (optional) - ISO date YYYY-MM-DD (if the project has a matching field) + PROMPT_EOF + - name: Append prompt (part 2) + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: | + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" - `campaign_status` (metadata) - Values: "active", "completed" Do NOT update any other fields or create custom fields. @@ -798,21 +845,21 @@ jobs: ### Idempotency Guarantee - All operations must be idempotent: - - Adding an item (issue/PR/discussion) already on the board → No-op (do not duplicate) - - Updating a status that matches current value → No-op (no change recorded) + **The update-project tool handles idempotency automatically.** You MUST send update-project requests for ALL discovered items. The tool will: + - Adding an item already on the board → Skips the add operation, but still updates fields (handled by tool) + - Updating a status that matches current value → No-op (handled by tool) - Marking a completed campaign as completed → No-op (terminal state preserved) - Re-running the orchestrator produces consistent results regardless of how many times it executes. + **CRITICAL**: Do NOT try to implement idempotency in your orchestrator logic by checking if items are already on the board before sending requests. This causes synchronization bugs where items are discovered but not processed. Always send ALL discovered items to update-project and let the tool handle duplicate detection. + + Re-running the orchestrator produces consistent results regardless of how many times it executes, because the update-project tool is idempotent. ### Project Board Integration Execute state writes using the `update-project` safe-output. All writes must target this exact project URL: **Project URL**: https://github.com/orgs/githubnext/projects/64 - - **Campaign ID**: Extract from tracker label `campaign:go-file-size-reduction-project64` (format: `campaign:CAMPAIGN_ID`) - + **Campaign ID**: `go-file-size-reduction-project64` (recommended to tag items via the optional `campaign_id` field) #### Adding New Issues/PRs @@ -822,7 +869,7 @@ jobs: project: "https://github.com/orgs/githubnext/projects/64" content_type: "issue" # or "pull_request" content_number: 123 # Extract number from URL like https://github.com/owner/repo/issues/123 - campaign_id: "CAMPAIGN_ID" # Required: extract from tracker label campaign:go-file-size-reduction-project64 + campaign_id: "go-file-size-reduction-project64" # Optional: tags items for this campaign fields: status: "Todo" # or "Done" if issue/PR is already closed/merged ``` @@ -842,18 +889,13 @@ jobs: ``` update-project: project: "https://github.com/orgs/githubnext/projects/64" - PROMPT_EOF - - name: Append prompt (part 2) - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: | - cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" content_type: "issue" # or "pull_request" content_number: 123 # Extract from URL fields: status: "Todo" # or "In Progress", "Blocked", "Done" - campaign_id: "CAMPAIGN_ID" # Extract from tracker label campaign:go-file-size-reduction-project64 + campaign_id: "go-file-size-reduction-project64" # Optional: tags items for this campaign worker_workflow: "WORKFLOW_ID" # Enables swimlane grouping and filtering + human_oversight_required: "No" # or "Yes" - powers a dedicated human review queue priority: "High" # or "Medium", "Low" - enables priority-based views effort: "Medium" # or "Small", "Large" - enables capacity planning team: "TEAM_NAME" # Optional: for team-based grouping @@ -862,6 +904,7 @@ jobs: **Custom Field Benefits**: - `worker_workflow`: Groups items by workflow in Roadmap swimlanes; enables "Slice by" filtering in Table views (orchestrator populates this by discovering which worker created the item via tracker-id) + - `human_oversight_required`: Creates an explicit human review queue; use a "Needs human" view filtered to "Yes" - `priority`: Enables priority-based filtering and sorting - `effort`: Supports capacity planning and workload distribution - `team`: Enables team-based grouping for multi-team campaigns @@ -879,7 +922,7 @@ jobs: project: "https://github.com/orgs/githubnext/projects/64" content_type: "issue" # or "pull_request" content_number: 123 # Extract from URL - campaign_id: "CAMPAIGN_ID" # Required: extract from tracker label campaign:go-file-size-reduction-project64 + campaign_id: "go-file-size-reduction-project64" # Optional: tags items for this campaign fields: status: "Done" # or "In Progress", "Todo" ``` diff --git a/.github/workflows/playground-org-project-update-issue.lock.yml b/.github/workflows/playground-org-project-update-issue.lock.yml index 0d454e87830..1026bd2224e 100644 --- a/.github/workflows/playground-org-project-update-issue.lock.yml +++ b/.github/workflows/playground-org-project-update-issue.lock.yml @@ -313,9 +313,20 @@ jobs: "type": "string", "enum": [ "issue", - "pull_request" + "pull_request", + "draft_issue" ] }, + "draft_body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "draft_title": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, "fields": { "type": "object" }, @@ -333,7 +344,8 @@ jobs: "pull_request": { "optionalPositiveInteger": true } - } + }, + "customValidation": "updateProjectValidTarget" } } EOF diff --git a/pkg/cli/mcp_server_error_codes_test.go b/pkg/cli/mcp_server_error_codes_test.go index 76bb35937e2..4b14e3df86b 100644 --- a/pkg/cli/mcp_server_error_codes_test.go +++ b/pkg/cli/mcp_server_error_codes_test.go @@ -194,7 +194,7 @@ func TestMCPServer_ErrorCodes_InternalError(t *testing.T) { params := &mcp.CallToolParams{ Name: "audit", Arguments: map[string]any{ - "run_id": int64(1), // Invalid run ID + "run_id_or_url": "1", // Invalid run ID (as string) }, } diff --git a/pkg/cli/mcp_server_json_integration_test.go b/pkg/cli/mcp_server_json_integration_test.go index 98797204947..003f83c3dab 100644 --- a/pkg/cli/mcp_server_json_integration_test.go +++ b/pkg/cli/mcp_server_json_integration_test.go @@ -453,7 +453,7 @@ func TestMCPServer_AllToolsReturnContent(t *testing.T) { name: "audit", toolName: "audit", args: map[string]any{ - "run_id": int64(1), + "run_id_or_url": "1", }, expectJSON: false, // May return error message mayFailInTest: true, // Expected to fail with invalid run ID diff --git a/pkg/cli/run_command_test.go b/pkg/cli/run_command_test.go index ceaaec4290d..55c23ee8bfa 100644 --- a/pkg/cli/run_command_test.go +++ b/pkg/cli/run_command_test.go @@ -3,6 +3,7 @@ package cli import ( + "context" "fmt" "strings" "testing" @@ -135,10 +136,10 @@ func TestProgressFlagSignature(t *testing.T) { // This is a compile-time check more than a runtime check // RunWorkflowOnGitHub should NOT accept progress parameter anymore - _ = RunWorkflowOnGitHub("test", false, "", "", "", false, false, false, []string{}, false) + _ = RunWorkflowOnGitHub(context.Background(), "test", false, "", "", "", false, false, false, []string{}, false) // RunWorkflowsOnGitHub should NOT accept progress parameter anymore - _ = RunWorkflowsOnGitHub([]string{"test"}, 0, false, "", "", "", false, false, []string{}, false) + _ = RunWorkflowsOnGitHub(context.Background(), []string{"test"}, 0, false, "", "", "", false, false, []string{}, false) // getLatestWorkflowRunWithRetry should NOT accept progress parameter anymore _, _ = getLatestWorkflowRunWithRetry("test.lock.yml", "", false) @@ -148,22 +149,22 @@ func TestProgressFlagSignature(t *testing.T) { func TestRefFlagSignature(t *testing.T) { // Test that RunWorkflowOnGitHub accepts refOverride parameter // This is a compile-time check that ensures the refOverride parameter exists - _ = RunWorkflowOnGitHub("test", false, "", "", "main", false, false, false, []string{}, false) + _ = RunWorkflowOnGitHub(context.Background(), "test", false, "", "", "main", false, false, false, []string{}, false) // Test that RunWorkflowsOnGitHub accepts refOverride parameter - _ = RunWorkflowsOnGitHub([]string{"test"}, 0, false, "", "", "main", false, false, []string{}, false) + _ = RunWorkflowsOnGitHub(context.Background(), []string{"test"}, 0, false, "", "", "main", false, false, []string{}, false) } // TestRunWorkflowOnGitHubWithRef tests that the ref parameter is handled correctly func TestRunWorkflowOnGitHubWithRef(t *testing.T) { // Test with explicit ref override (should still fail for non-existent workflow, but syntax is valid) - err := RunWorkflowOnGitHub("nonexistent-workflow", false, "", "", "main", false, false, false, []string{}, false) + err := RunWorkflowOnGitHub(context.Background(), "nonexistent-workflow", false, "", "", "main", false, false, false, []string{}, false) if err == nil { t.Error("RunWorkflowOnGitHub should return error for non-existent workflow even with ref flag") } // Test with ref override and repo override - err = RunWorkflowOnGitHub("nonexistent-workflow", false, "", "owner/repo", "feature-branch", false, false, false, []string{}, false) + err = RunWorkflowOnGitHub(context.Background(), "nonexistent-workflow", false, "", "owner/repo", "feature-branch", false, false, false, []string{}, false) if err == nil { t.Error("RunWorkflowOnGitHub should return error for non-existent workflow with both ref and repo") } @@ -173,10 +174,10 @@ func TestRunWorkflowOnGitHubWithRef(t *testing.T) { func TestInputFlagSignature(t *testing.T) { // Test that RunWorkflowOnGitHub accepts inputs parameter // This is a compile-time check that ensures the inputs parameter exists - _ = RunWorkflowOnGitHub("test", false, "", "", "", false, false, false, []string{"key=value"}, false) + _ = RunWorkflowOnGitHub(context.Background(), "test", false, "", "", "", false, false, false, []string{"key=value"}, false) // Test that RunWorkflowsOnGitHub accepts inputs parameter - _ = RunWorkflowsOnGitHub([]string{"test"}, 0, false, "", "", "", false, false, []string{"key=value"}, false) + _ = RunWorkflowsOnGitHub(context.Background(), []string{"test"}, 0, false, "", "", "", false, false, []string{"key=value"}, false) } // TestInputValidation tests that input validation works correctly @@ -231,7 +232,7 @@ func TestInputValidation(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Since we can't actually run workflows in tests, we'll just test the validation // by checking if the function would error before attempting to run - err := RunWorkflowOnGitHub("nonexistent-workflow", false, "", "owner/repo", "", false, false, false, tt.inputs, false) + err := RunWorkflowOnGitHub(context.Background(), "nonexistent-workflow", false, "", "owner/repo", "", false, false, false, tt.inputs, false) if tt.shouldError { if err == nil { diff --git a/pkg/cli/templates/github-agentic-workflows.md b/pkg/cli/templates/github-agentic-workflows.md index 19bcb46b02c..f8052224b8e 100644 --- a/pkg/cli/templates/github-agentic-workflows.md +++ b/pkg/cli/templates/github-agentic-workflows.md @@ -115,6 +115,9 @@ The YAML frontmatter supports these fields: - **`roles:`** - Repository access roles that can trigger workflow (array or "all") - Default: `[admin, maintainer, write]` - Available roles: `admin`, `maintainer`, `write`, `read`, `all` +- **`bots:`** - Bot identifiers allowed to trigger workflow regardless of role permissions (array) + - Example: `bots: [dependabot[bot], renovate[bot], github-actions[bot]]` + - Bot must be active (installed) on repository to trigger workflow - **`strict:`** - Enable enhanced validation for production workflows (boolean, defaults to `true`) - When omitted, workflows enforce strict mode security constraints - Set to `false` to explicitly disable strict mode for development/testing @@ -251,6 +254,43 @@ The YAML frontmatter supports these fields: args: ["--custom-arg", "value"] # Optional: additional AWF arguments ``` +- **`sandbox:`** - Sandbox configuration for AI engines (string or object) + - String format: `"default"` (no sandbox), `"awf"` (Agent Workflow Firewall), `"srt"` or `"sandbox-runtime"` (Anthropic Sandbox Runtime) + - Object format for full configuration: + ```yaml + sandbox: + agent: awf # or "srt", or false to disable + mcp: # MCP Gateway configuration (requires mcp-gateway feature flag) + container: ghcr.io/githubnext/mcp-gateway + port: 8080 + api-key: ${{ secrets.MCP_GATEWAY_API_KEY }} + ``` + - **Agent sandbox options**: + - `awf`: Agent Workflow Firewall for domain-based access control + - `srt`: Anthropic Sandbox Runtime for filesystem and command sandboxing + - `false`: Disable agent firewall + - **AWF configuration**: + ```yaml + sandbox: + agent: + id: awf + mounts: + - "/host/data:/data:ro" + - "/host/bin/tool:/usr/local/bin/tool:ro" + ``` + - **SRT configuration**: + ```yaml + sandbox: + agent: + id: srt + config: + filesystem: + allowWrite: [".", "/tmp"] + denyRead: ["/etc/secrets"] + enableWeakerNestedSandbox: true + ``` + - **MCP Gateway**: Routes MCP server calls through unified HTTP gateway (experimental) + - **`tools:`** - Tool configuration for coding agent - `github:` - GitHub API tools - `allowed:` - Array of allowed GitHub API functions @@ -578,6 +618,43 @@ The YAML frontmatter supports these fields: ``` Useful when you need additional permissions or want to perform actions across repositories. +- **`safe-inputs:`** - Define custom lightweight MCP tools as JavaScript, shell, or Python scripts (object) + - Tools mounted in MCP server with access to specified secrets + - Each tool requires `description` and one of: `script` (JavaScript), `run` (shell), or `py` (Python) + - Tool configuration properties: + - `description:` - Tool description (required) + - `inputs:` - Input parameters with type and description (object) + - `script:` - JavaScript implementation (CommonJS format) + - `run:` - Shell script implementation + - `py:` - Python script implementation + - `env:` - Environment variables for secrets (supports `${{ secrets.* }}`) + - `timeout:` - Execution timeout in seconds (default: 60) + - Example: + ```yaml + safe-inputs: + search-issues: + description: "Search GitHub issues using API" + inputs: + query: + type: string + description: "Search query" + required: true + limit: + type: number + description: "Max results" + default: 10 + script: | + const { Octokit } = require('@octokit/rest'); + const octokit = new Octokit({ auth: process.env.GH_TOKEN }); + const result = await octokit.search.issuesAndPullRequests({ + q: inputs.query, + per_page: inputs.limit + }); + return result.data.items; + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ``` + - **`slash_command:`** - Command trigger configuration for /mention workflows (replaces deprecated `command:`) - **`cache:`** - Cache configuration for workflow dependencies (object or array) - **`cache-memory:`** - Memory MCP server with persistent cache storage (boolean or object) diff --git a/pkg/workflow/safe_outputs_integration_test.go b/pkg/workflow/safe_outputs_integration_test.go index 0dd313a1882..414d96b6a7c 100644 --- a/pkg/workflow/safe_outputs_integration_test.go +++ b/pkg/workflow/safe_outputs_integration_test.go @@ -517,9 +517,8 @@ func TestConsolidatedSafeOutputsJobIntegration(t *testing.T) { "SHARED_VAR", }, expectedStepNames: []string{ - "create_issue", - "create_pull_request", - "add_comment", + "process_safe_outputs", // Handler manager consolidates create_issue and add_comment + "create_pull_request", // Create PR is handled separately // Note: "noop" is not included in consolidated job }, },