diff --git a/.github/workflows/issue-implementer.lock.yml b/.github/workflows/issue-implementer.lock.yml index 77248e6..7f5dc84 100644 --- a/.github/workflows/issue-implementer.lock.yml +++ b/.github/workflows/issue-implementer.lock.yml @@ -22,7 +22,7 @@ # For more information: https://github.github.com/gh-aw/introduction/overview/ # # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"4fc53e677916c7a07bd3ab866fd52f1d8f45d833aec9d14a70f8645c5c84117a","compiler_version":"v0.60.0","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"4bf6e6fac4f12e27d69748e222619fbbbb024a06d91ad63b336dff1612286086","compiler_version":"v0.60.0","strict":true} name: "Issue Implementer" "on": @@ -52,7 +52,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw-actions/setup@v0.60.0 + uses: github/gh-aw-actions/setup@998487a673ace02b3d9586e7511268089af88971 # v0.60.0 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -263,7 +263,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw-actions/setup@v0.60.0 + uses: github/gh-aw-actions/setup@998487a673ace02b3d9586e7511268089af88971 # v0.60.0 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -329,7 +329,7 @@ jobs: cat > /opt/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF' { "description_suffixes": { - "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created." + "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Labels [\"aw\"] will be automatically added." }, "repo_params": {}, "dynamic_tools": [] @@ -880,7 +880,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw-actions/setup@v0.60.0 + uses: github/gh-aw-actions/setup@998487a673ace02b3d9586e7511268089af88971 # v0.60.0 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1009,7 +1009,7 @@ jobs: push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} steps: - name: Setup Scripts - uses: github/gh-aw-actions/setup@v0.60.0 + uses: github/gh-aw-actions/setup@998487a673ace02b3d9586e7511268089af88971 # v0.60.0 with: destination: /opt/gh-aw/actions safe-output-custom-tokens: 'true' @@ -1062,7 +1062,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,conda.anaconda.org,conda.binstar.org,crates.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,github.com,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,static.crates.io,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"auto_merge\":true,\"draft\":false,\"github-token\":\"${{ secrets.GH_AW_WRITE_TOKEN }}\",\"max\":1,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"AGENTS.md\"],\"protected_files_policy\":\"fallback-to-issue\",\"protected_path_prefixes\":[\".github/\",\".agents/\"]},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"github-token\":\"${{ secrets.GH_AW_WRITE_TOKEN }}\",\"if_no_changes\":\"warn\",\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"]}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"auto_merge\":true,\"draft\":false,\"github-token\":\"${{ secrets.GH_AW_WRITE_TOKEN }}\",\"labels\":[\"aw\"],\"max\":1,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"AGENTS.md\"],\"protected_files_policy\":\"fallback-to-issue\",\"protected_path_prefixes\":[\".github/\",\".agents/\"]},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"github-token\":\"${{ secrets.GH_AW_WRITE_TOKEN }}\",\"if_no_changes\":\"warn\",\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"]}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} GITHUB_TOKEN: ${{ secrets.GH_AW_WRITE_TOKEN }} with: diff --git a/.github/workflows/issue-implementer.md b/.github/workflows/issue-implementer.md index f600391..babcd46 100644 --- a/.github/workflows/issue-implementer.md +++ b/.github/workflows/issue-implementer.md @@ -31,6 +31,7 @@ safe-outputs: create-pull-request: github-token: ${{ secrets.GH_AW_WRITE_TOKEN }} protected-files: fallback-to-issue + labels: [aw] auto-merge: true draft: false push-to-pull-request-branch: @@ -54,4 +55,4 @@ uv sync && uv run ruff check --fix . && uv run ruff format . && uv run pyright & Fix any lint or type errors found by ruff/pyright before committing. Iterate until all checks pass cleanly. -Open a pull request with the fix. The PR title should reference the issue number. Include tests as specified in the issue. The PR must NOT be a draft — open it as a regular PR ready for review. Add the `aw` label to the PR. +Open a pull request with the fix. The PR title should reference the issue number. Include tests as specified in the issue. The PR must NOT be a draft — open it as a regular PR ready for review. diff --git a/docs/agentic-workflows.md b/docs/agentic-workflows.md index e5f23a0..ef0e2b6 100644 --- a/docs/agentic-workflows.md +++ b/docs/agentic-workflows.md @@ -452,8 +452,8 @@ Never squash merge — it loses commit history and the user gets angry. Set merg ### 10. Issues are specs Issues describe WHAT to do, not HOW. The implementer agent reads the issue and decides the implementation. -### 11. `labels:` on `create-pull-request` config is broken (gh-aw runtime bug) -The `labels` field compiles into the lock file and the handler reads it, but the post-creation label API call fails non-deterministically with a node ID resolution error. Worse, the tool description tells the agent "Labels will be automatically added" — so the agent stops including labels in its own call. Do NOT use `labels:` config. Instead, instruct the agent to include labels in the `create_pull_request` call. Tracked in #108. +### 11. ~~`labels:` on `create-pull-request` config is broken~~ (resolved) +~~The `labels` field compiles into the lock file and the handler reads it, but the post-creation label API call fails non-deterministically with a node ID resolution error.~~ **Update (2026-03-21)**: Investigation found that `labels` is officially documented and supported in gh-aw. The "node ID resolution error" was never properly investigated and may have been misattributed to the auto-merge step (which does use node IDs). Re-enabled `labels: [aw]` on the implementer. The `labels` field applies labels via REST API after PR creation — use it for reliable labeling instead of depending on agent instructions. Closed #108. ### 12. Review thread IDs are invalidated by pushes Pushing code to a PR branch can invalidate GraphQL thread IDs. If the responder pushes before resolving threads, the resolve calls fail with stale node IDs. Always resolve threads BEFORE pushing. @@ -487,6 +487,20 @@ Not all safe output handlers resolve `target` the same way. `submit-pull-request ### 22. Adding a trigger to `on:` requires updating the job `if:` condition If a job has an `if:` condition that gates on `github.event_name`, adding a new trigger to `on:` is not enough — the `if:` must also include the new event name. The orchestrator had this bug twice: first when switching quality gate to `workflow_dispatch`, then when adding `schedule`. The cron fired correctly but the job was skipped because `'schedule'` wasn't in the `if:` condition. **Always check for `event_name` gates when adding triggers.** +### 23. `tools:` block in shared imports restricts the entire agent's tool allowlist +Adding a `tools:` block (e.g., `tools: bash: [cat, grep, jq]`) to a shared import causes `gh aw compile` to switch from `--allow-all-tools` to a restricted `--allow-tool shell(...)` list in the compiled lock file. This affects the ENTIRE agent, not just the shared import's step. The agent gets "Permission denied" on any command not in the explicit list — including `uv`, `python3`, `pip`, `curl`, and `git fetch`. Since only the importing workflow is affected, the bug manifests as one agent failing while others work fine — it looks non-deterministic but is actually a consistent config issue. **Fix**: never add `tools:` blocks to shared imports. The pre-fetch step runs as a regular workflow step (not agent shell), so it doesn't need tool permissions. + +### 24. `gh run list --status` only accepts a single value +The `--status` flag is type `string`, not array. Passing `--status=in_progress --status=queued` only uses the **last** value. To filter for multiple statuses, skip `--status` entirely and filter client-side with `--json databaseId,status --jq '[.[] | select(.status == "in_progress" or .status == "queued")]'`. + +### 25. `cancel-in-progress: true` is dangerous for workflows with side effects +If a workflow modifies external state (labels, dispatches) and `cancel-in-progress: true` kills it mid-flight, the side effects may be partially applied. Example: orchestrator labels an issue `aw-dispatched` then gets cancelled before dispatching the implementer — the issue is orphaned. Use `cancel-in-progress: false` for workflows with non-atomic side effects. + +### 26. Quality gate approval label desyncs from actual approval state +Branch protection's `dismiss_stale_reviews: true` dismisses the quality gate's APPROVE review when new code is pushed, but the `aw-quality-gate-approved` label persists. The orchestrator sees the label and skips re-dispatching the quality gate, leaving the PR stuck in BLOCKED state. Labels are hints, not source of truth — always verify actual review state. + +### 27. Shared imports use `imports:` + `steps:` pattern +To pre-fetch data before the agent runs, create a shared `.md` file with a `steps:` block in the frontmatter. The importing workflow uses `imports: [shared/filename.md]`. The steps run as regular workflow steps (with full `gh` CLI access and `GITHUB_TOKEN`), writing data to `/tmp/gh-aw/` for the agent to read. This bypasses MCP tool limitations. Based on the pattern from `github/gh-aw`'s own `copilot-pr-data-fetch.md`. @@ -540,9 +554,9 @@ gh run view --log-failed # View failed job logs | `code-health.md` | schedule (daily) / manual | Find refactoring/cleanup opportunities | `create-issue` (max 2), `dispatch-workflow` (implementer) | | `issue-implementer.md` | `workflow_dispatch` (issue number) | Implement fix from issue spec, open PR | `create-pull-request` (draft: false, auto-merge), `push-to-pull-request-branch` | | `ci-fixer.md` | `workflow_dispatch` (PR number) | Fix CI failures on agent PRs | `push-to-pull-request-branch`, `add-labels`, `add-comment` | -| `review-responder.md` | `pull_request_review` (moving to `workflow_dispatch`) | Address review comments | `push-to-pull-request-branch`, `reply-to-pull-request-review-comment`, `add-labels` | +| `review-responder.md` | `workflow_dispatch` (PR number) | Address review comments | `push-to-pull-request-branch`, `reply-to-pull-request-review-comment`, `add-labels` | | `quality-gate.md` | `workflow_dispatch` | Evaluate quality + blast radius, approve or close | `submit-pull-request-review`, `close-pull-request`, `add-comment`, `add-labels` | -| `pipeline-orchestrator.yml` | `workflow_run` / `push` / `pull_request_review` / `workflow_dispatch` | Dispatch implementer/ci-fixer/responder/quality-gate, resolve threads, rebase PRs | N/A (bash, not gh-aw) | +| `pipeline-orchestrator.yml` | `workflow_run` / `pull_request_review` / `workflow_dispatch` / `schedule` | Dispatch implementer/ci-fixer/responder/quality-gate, resolve threads, rebase PRs | N/A (bash, not gh-aw) | ### Loop prevention @@ -699,4 +713,20 @@ The enhanced PR rescue (#116) went through three complete rewrites: - `workflow_dispatch` is the right trigger for the responder — the orchestrator decides when to run it, eliminating trigger loops. - Always verify claims by reading actual data (run logs, thread state) before proceeding. +### 2026-03-20/21 — Pre-fetch pattern, responder fix, duplicate dispatch fix + +- **PR #186**: Fixed responder's inability to read review comments. MCP `pull_request_read` returns `[]` in gh-aw sandbox. Solution: shared import (`shared/fetch-review-comments.md`) runs `gh api graphql` BEFORE the agent starts, writes threads to `/tmp/gh-aw/review-data/unresolved-threads.json`. +- **Critical bug found and fixed**: The shared import initially included a `tools:` block with an allowlist of shell commands. This caused `gh aw compile` to switch from `--allow-all-tools` to `--allow-tool shell(cat) --allow-tool shell(grep) ...` in the lock file. The agent got "Permission denied" on everything not in the list (uv, python3, pip, curl, git fetch). Only the responder was affected because only it imported the shared file. Fix: removed the `tools:` block entirely. Same class of bug as pitfall #13. +- **Pre-fetch pattern tested end-to-end**: Responder found and addressed review comments on PRs #172 and #177. Both subsequently passed quality gate and auto-merged. First successful responder runs with both comment reading AND CI validation. +- **PR #190**: Fixed duplicate implementer dispatches. Orchestrator was dispatching multiple implementers in quick succession because: (1) `push` trigger fired on every merge, (2) `cancel-in-progress: true` killed runs mid-flight after labeling, (3) no check for in-flight implementer. Fix: removed `push` trigger, switched to `cancel-in-progress: false`, added in-flight check via `gh run list` with jq filter. Copilot review caught that `--status` only accepts a single value — fixed to client-side jq filter. Also changed API error fallback from "0" (fail open) to "1" (fail safe). +- **Quality gate label desync discovered**: `dismiss_stale_reviews: true` dismisses the quality gate's approval when new code is pushed, but the `aw-quality-gate-approved` label persists. Orchestrator sees label, skips quality gate, PR stays stuck. Filed issue #187. +- **Issues filed**: #183 (astral.sh blocked — not needed), #184 (audit workflows — not needed), #187 (quality gate label/approval desync). +- **Issues closed**: #180 (MCP empty comments), #164 (duplicate dispatches). +- **Key lessons**: + - `tools:` blocks in shared imports affect the entire compiled agent, not just the import's step. + - `cancel-in-progress: true` + side effects = orphaned state. Use `false` for workflows that label/dispatch. + - `gh run list --status` is single-value — use jq for multi-status filtering. + - Labels are hints, not source of truth — always verify actual state (review approval, run status). + - When a bug looks non-deterministic (one agent fails, others don't), it's almost always a config difference — find it. + diff --git a/docs/changelog.md b/docs/changelog.md index 992fbbb..baff245 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,69 @@ Append-only history of repo-level changes (CI, infra, shared config). Tool-speci --- +## fix: re-add labels config to implementer — 2026-03-21 + +**Problem**: The `labels: [aw]` config on `create-pull-request` was removed weeks ago due to a vague "node ID resolution error" that was never properly investigated. Without it, labeling depends on the agent including labels in its call — which is non-deterministic. Some PRs were created without the `aw` label. + +**Investigation**: Read the gh-aw docs and source. The `labels` field is officially documented and supported — labels are applied via REST API after PR creation. The "node ID error" was likely misattributed (possibly from the `auto-merge` step which uses GraphQL node IDs, not from label application). + +**Fix**: Re-added `labels: [aw]` to `issue-implementer.md` and recompiled lock file. Labels are now applied by infrastructure, not dependent on agent behavior. Note: recompiling also introduced SHA pinning for `gh-aw-actions/setup` (`@v0.60.0` → `@SHA # v0.60.0`) — this is a compiler behavior change expected in future recompilations. + +Closes #108. + +--- + +## fix: prevent duplicate implementer dispatches — 2026-03-21 + +**Problem**: The orchestrator dispatched a new implementer on every trigger without checking if one was already running. When multiple triggers fired in quick succession (e.g., two PRs merging back-to-back via auto-merge), multiple implementers were dispatched for different issues. GitHub's concurrency group (`cancel-in-progress` was `true`) then cancelled intermediate runs, leaving issues labeled `aw-dispatched` but never worked on. Observed: 4 implementer dispatches in 10 minutes for issues #155, #160, #161, #181 — one cancelled, issues orphaned. + +**Root cause**: Three compounding issues: (1) the `push` trigger fired on every merge to main, causing rapid-fire orchestrator runs, (2) `cancel-in-progress: true` killed orchestrator runs mid-flight (potentially after labeling but before the implementer started), (3) no check for in-flight implementer runs. + +**Fix (PR #190)**: +1. Removed `push` trigger — 5-minute cron covers post-merge dispatch without the rapid-fire problem. +2. Changed `cancel-in-progress` to `false` — orchestrator runs queue instead of cancelling, so each run sees state left by the previous one. +3. Added in-flight implementer check before dispatching — queries `gh run list` for `in_progress`/`queued`/`waiting` implementer runs and skips dispatch if any exist. +4. Fail-safe: if the `gh run list` API call errors, defaults to "1" (assume something is running) rather than "0" (dispatch anyway). + +**Review finding**: `gh run list --status` only accepts a single value — passing it multiple times only uses the last one. Fixed by filtering client-side with `--json databaseId,status --jq '[.[] | select(.status == "in_progress" or .status == "queued" or .status == "waiting")] | length'`. Caught by Copilot code reviewer. + +**Lesson**: `cancel-in-progress: true` is dangerous for workflows that modify external state (labels, dispatches) before completing. Use `false` when the workflow has side effects that must complete atomically. + +Fixes #164. + +--- + +## fix: pre-fetch review comments via GraphQL — 2026-03-20/21 + +**Problem**: The MCP `pull_request_read` tool returns empty `[]` for review comments inside the gh-aw agent sandbox. This was confirmed across multiple responder runs — the tool never reliably returns review comment data. The responder couldn't find comments to address. + +**Root cause**: Unknown upstream issue with MCP tool behavior in gh-aw sandbox. The GitHub GraphQL API works fine via `gh api graphql` in workflow steps. + +**Fix (PR #186)**: +1. Created `.github/workflows/shared/fetch-review-comments.md` — a shared import that runs `gh api graphql` BEFORE the agent starts, writing unresolved review threads to `/tmp/gh-aw/review-data/unresolved-threads.json`. +2. Updated `review-responder.md` to import the shared step and read from the pre-fetched file instead of using MCP tools. +3. Added `databaseId` to GraphQL query so the agent can use `reply_to_pull_request_review_comment` with the correct comment ID. +4. Bumped `comments(first: 10)` to `comments(first: 100)` — proper pagination tracked in issue #185. +5. jq error handling: fail loudly on parse errors instead of silently writing `[]`. + +**Critical discovery — `tools:` block in shared imports**: The initial shared import included a `tools: bash:` block listing allowed shell commands. This caused `gh aw compile` to switch from `--allow-all-tools` to a restricted `--allow-tool` list in the lock file. The agent could only run commands explicitly listed (cat, grep, jq, etc.) — everything else got "Permission denied." This broke `uv sync`, `python3 --version`, `pip install`, `curl`, even `git fetch`. Only the responder was affected because only it imported the shared file. **Fix**: removed the `tools:` block entirely from the shared import. This is the same class of bug as pitfall #13 in agentic-workflows.md. + +**Tested**: Responder successfully found and addressed review comments on PRs #172 and #177. Both PRs subsequently passed quality gate and auto-merged — first end-to-end test of the pre-fetch pattern. + +Closes #180. Related: #183 (astral.sh — not needed), #184 (audit — astral.sh not needed). + +--- + +## fix: quality gate label/approval desync — 2026-03-21 + +**Problem**: The orchestrator checks for the `aw-quality-gate-approved` label to decide whether to dispatch the quality gate. But branch protection has `dismiss_stale_reviews: true` — when a new commit is pushed (e.g., by the responder or ci-fixer), GitHub automatically dismisses all existing approvals including the quality gate's APPROVE review. The label persists even though the approval is gone, so the orchestrator sees the label and skips the quality gate dispatch. PR stays BLOCKED with no valid approval. + +**Observed on PR #177**: Quality gate approved → responder pushed fix commit → GitHub dismissed approval → orchestrator saw label → skipped quality gate → PR stuck. + +**Fix**: Manually removed stale labels. Filed issue #187 to fix the orchestrator to check actual review state, not just labels. Proposed solution: agents remove `aw-quality-gate-approved` when they push, AND orchestrator verifies a non-dismissed approval exists. + +--- + ## fix: cron schedule skipped — missing from orchestrator if: condition — 2026-03-20 **Problem**: PR #174 enabled a 5-minute cron on the orchestrator but didn't add `schedule` to the job's `if:` condition. Cron fired correctly but the job was immediately skipped every time.