You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(aidd-orchestrator): make audit + finalize self-verifying
Action 06-write-audit now pushes the audit JSON via the Contents API
instead of `git checkout` + commit + push. The old flow lost the
untracked audit file when switching branches on the runner and caused
the action to silently abort, leaving the issue stuck on `claude/working`.
Action 04-finalize handles the `no_comments` stop reason (was missing
from the input enum, so the skill exited without posting a summary on
fresh-PR `@claude /review` triggers) and always posts the summary.
Both actions:
- emit a `aidd-orchestrator:run-complete` / `:review-complete`
completion-marker PR comment as the sole exit signal.
- carry a Test section the agent must run before exiting, with one
`OK` / `FAIL` assertion per persisted artifact (audit file, check
run, labels, summary, marker).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Persists the run record, creates the GitHub Check Run, and transitions the lifecycle labels to `claude/awaiting-review` (or `claude/blocked` on failure).
3
+
Persist the run record on the PR branch, finalize the Check Run, transition the issue labels, post the completion marker.
4
4
5
5
## Inputs
6
6
7
-
-`run_record`(required) -- merged data from `03-acquire-lock` and `05-delegate-sdlc`
@@ -22,25 +25,43 @@ Persists the run record, creates the GitHub Check Run, and transitions the lifec
22
25
23
26
## Process
24
27
25
-
**MANDATORY — this action must run and must persist its artifacts.** Skipping any step (especially the audit write or its commit + push to the PR branch) is a contract violation. The audit JSON is the single source of truth for the run; without it on the PR branch, the run is unverifiable.
26
-
27
-
1. Compute `audit_dir = config.audit.log_dir + "/" + YYYY_MM` (UTC). Create it if missing.
28
-
2. Write `<audit_dir>/<run_id>.json` with the full run record: trigger, issue, dependency check, lock timestamps, SDLC outcome (including `delegated_via_skill` boolean and the skill name actually called), errors if any, plugin version. The `delegated_via_skill` field MUST be `true` when action 05 invoked the SDLC skill via the `Skill` tool; `false` means the orchestrator bypassed delegation and the run is flagged as a contract violation.
29
-
3.**Commit and push the audit file to the PR branch** (the SDLC pushed the feature branch in action 05). Run from the runner's working tree:
28
+
1. Compose `audit_path = config.audit.log_dir + "/" + YYYY_MM + "/" + run_id + ".json"`. Build the run record JSON: trigger, issue, lock timestamps, SDLC outcome, `delegated_via_skill`, errors, plugin version.
29
+
2. Push the audit file to the PR branch via the Contents API (no `git checkout`):
30
+
```bash
31
+
SHA=$(gh api "repos/$GITHUB_REPOSITORY/contents/$audit_path?ref=$branch" --jq .sha 2>/dev/null ||echo"")
32
+
PAYLOAD=$(jq -n --arg msg "chore(orchestrator): record async run audit $run_id" \
audit_commit_url=$(gh api -X PUT "repos/$GITHUB_REPOSITORY/contents/$audit_path" --input - <<<"$PAYLOAD" --jq .commit.html_url)
37
+
```
38
+
3. If `config.audit.github_check_run`: create or update Check Run `aidd-async/<run_id>`. Capture `check_run_id` and set a final `conclusion` (`success` / `failure` / `action_required`).
39
+
4. Transition labels: remove `config.labels.working`; on success add `config.labels.awaiting_review`, on failure add `config.labels.blocked` and post the error on the issue. Capture `labels_after` from the API response.
40
+
5. Post the completion marker (single PR comment, idempotent via the HTML token):
30
41
```
31
-
git fetch origin feat/issue-<n>-<slug>
32
-
git checkout feat/issue-<n>-<slug>
33
-
git add <audit_dir>/<run_id>.json
34
-
git commit -m "chore(orchestrator): record async run audit <run_id>" --no-verify
`--no-verify` is used because the audit commit is an orchestrator artefact, not a feature change. If `delegated_via_skill` is `false`, additionally post a PR comment referencing the contract violation so a human can decide whether to keep the PR.
38
-
4. If `config.audit.github_check_run` is true, call `gh api repos/{owner}/{repo}/check-runs` to create or update a Check Run named `aidd-async/<run_id>` with `status`, `conclusion`, and `output.summary` reflecting the run (including `delegated_via_skill`).
39
-
5. Transition labels on the issue:
40
-
- On success (PR was opened): remove `config.labels.working`, add `config.labels.awaiting_review`.
41
-
- On failure: remove `config.labels.working`, add `config.labels.blocked`. Post a comment on the issue with the error details.
42
-
6. Return the audit path and check run id.
45
+
Capture `completion_marker_url`. The agent uses this as its sole exit signal.
46
+
6. Run the Test section. Exit only when every assertion prints `OK`.
43
47
44
48
## Test
45
49
46
-
After a successful run: `gh api 'repos/{owner}/{repo}/contents/aidd_docs/async-runs/<YYYY_MM>/<run_id>.json?ref=feat/issue-<n>-<slug>'` returns the file (proving it was committed and pushed); `jq '.run_id, .pr_number, .delegated_via_skill' <local-copy>` returns the run id, PR number, and `true`; `gh api repos/{owner}/{repo}/check-runs/<id>` returns `conclusion: "success"`; `gh issue view <n> --repo <owner>/<repo> --json labels --jq '.labels[].name'` includes `claude/awaiting-review` and excludes `claude/working`. If `delegated_via_skill` is `false`, the PR carries a comment naming the contract violation.
50
+
```bash
51
+
gh api "repos/$GITHUB_REPOSITORY/contents/$audit_path?ref=$branch" --jq .sha >/dev/null \
52
+
&&echo"OK audit_file"||echo"FAIL audit_file"
53
+
54
+
gh api "repos/$GITHUB_REPOSITORY/check-runs/$check_run_id" --jq .conclusion \
-`02-detect-stop` (only when its`decision == "stop"`)
28
+
-`02-detect-stop` (when `decision == "stop"`)
25
29
26
30
## Process
27
31
28
-
1. Open the existing audit record. Append `{ "loop_closed_at": "<ISO8601>", "stop_reason": "<reason>", "iterations": iteration_log }`.
29
-
2. Update the GitHub Check Run for the run id: `conclusion = "success"` if `stop_reason == "human_reviewer"` and last iteration tests passed, else `"neutral"` for `max_iterations` and `"action_required"` for `blocked_label`.
30
-
3. Render the summary body from the template below; substitute every placeholder. Empty sections must be omitted, never left blank.
31
-
4. Post the summary as a single PR comment via `gh pr comment <pr> --repo <owner>/<repo> --body-file -`.
32
-
5. If `trigger_comment_id` is set, ensure a final reaction is set on the trigger comment: `+1` for success, `confused` for `max_iterations`, `-1` for `blocked_label`. Remove any prior `eyes` reaction first.
33
-
6. Transition labels on the linked issue (`gh pr view <pr> --json closingIssuesReferences`):
34
-
- On `human_reviewer` (success): remove `config.labels.working`, add `config.labels.awaiting_review`.
35
-
- On `max_iterations`: remove `config.labels.working`, add `config.labels.awaiting_review` (PR exists, human must look).
36
-
- On `blocked_label`: remove `config.labels.working`, add `config.labels.blocked`.
32
+
1. Read the existing audit JSON via the Contents API. Append `{ "loop_closed_at": "<ISO8601>", "stop_reason": "<reason>", "iterations": iteration_log }`. Push the update with `gh api -X PUT` (pass the prior `sha`). Capture `audit_commit_url`.
33
+
2. Update Check Run `aidd-async/<run_id>`: `success` for `human_reviewer` (tests passed) or `no_comments`; `neutral` for `max_iterations`; `action_required` for `blocked_label`.
34
+
3. Render the summary body from the template below. When `stop_reason == "no_comments"` and `iteration_log` is empty, show only the header row plus `_no fix iterations on this loop_`.
35
+
4. Post the summary as a single PR comment: `summary_comment_url=$(gh pr comment $pr_number --body-file -)`. Always post, including on `no_comments`.
36
+
5. If `trigger_comment_id` set: remove the prior `eyes` reaction; add the final one (`+1` for `human_reviewer` / `no_comments`, `confused` for `max_iterations`, `-1` for `blocked_label`).
Capture `completion_marker_url`. The agent uses this as its sole exit signal.
53
+
8. Run the Test section. Exit only when every assertion prints `OK`.
37
54
38
55
## Summary template
39
56
40
-
The body is markdown and must follow this exact structure:
41
-
42
57
```markdown
43
58
## Async review summary
44
59
@@ -60,16 +75,43 @@ The body is markdown and must follow this exact structure:
60
75
61
76
### Next step
62
77
63
-
-`human_reviewer` -- A reviewer has commented since the last automated push. The loop is paused. To continue, address the comments and re-trigger with `<retry mention>` or `<retry label>`.
64
-
-`max_iterations` -- The maximum of `<max_iterations>` automatic iterations was reached without convergence. Human review is required before another retry.
65
-
-`blocked_label` -- The `<blocked label>` label is set. Remove it once the blocker is resolved to allow another retry.
78
+
-`human_reviewer` -- A reviewer commented since the last automated push. Re-trigger with `<retry mention>` / `<retry label>` once comments are addressed.
79
+
-`max_iterations` -- Hit `<max_iterations>` without convergence. Human review required before retry.
80
+
-`blocked_label` -- `<blocked label>` is set. Remove it to allow another retry.
81
+
-`no_comments` -- Nothing actionable to address. Re-trigger with a concrete inline review comment.
66
82
67
83
---
68
84
*Audit log: `<audit_path>`.*
69
85
```
70
86
71
-
The summary stays under ~30 lines on a typical PR. Truncate quoted comment bodies to 80 characters with `...`. Iterations table has one row per iteration; if no fix iterations ran, show only the header row plus a `_no fix iterations on this loop_` note.
87
+
Stay under ~30 lines on a typical PR. Truncate quoted bodies to 80 characters with `...`.
72
88
73
89
## Test
74
90
75
-
Given an audit record with two `fix-iteration` entries and `stop_reason = human_reviewer`: after this action runs, the audit JSON contains `loop_closed_at` and `stop_reason`, the Check Run conclusion is `success`, the trigger comment carries a `+1` reaction, and a PR comment exists whose body matches the template (contains the `## Async review summary` heading, an `Iterations` table with two data rows, a `Next step` section with the `human_reviewer` bullet, and an `Audit log` footer).
91
+
```bash
92
+
gh api "repos/$GITHUB_REPOSITORY/contents/$audit_path?ref=$branch" --jq .content \
93
+
| base64 -d | jq -e '.loop_closed_at and .stop_reason'>/dev/null \
94
+
&&echo"OK audit_closed"||echo"FAIL audit_closed"
95
+
96
+
gh api "repos/$GITHUB_REPOSITORY/check-runs/$check_run_id" --jq .conclusion \
`no_comments` variant: assertion 3 still passes (summary contains the `_no fix iterations on this loop_` note); the `Iterations` table has only its header row.
0 commit comments