From 33501ca54e7f6e1bab6caa7673ccb44418719d41 Mon Sep 17 00:00:00 2001 From: Paul O'Reilly Date: Wed, 9 Sep 2026 13:56:34 +1200 Subject: [PATCH] fix(bug-verify-sonnet@1): agent self-transitions flow_state, remove dead verdict-tx node verdict-tx (kind: script) never worked: script nodes are harness-exempt (WF-COMP-6/MN-38) and run in their own empty container, so it could never credentially read /workspace/verdict from the agent's node. Option 3: give the agent node's own composite (planning-sonnet-repo) a cp-service-token/v1 layer, and have the agent PATCH its own item's flow_state (verified|rejected) to the CP directly as its last step, before task_complete. Removes verdict-tx entirely. See agent-runtimes work item 88ecd23e and spec/typed-workflows.md WF-COMP-25 (agent-runtimes commit 8972d266) for the finalize.py precedence fix this depends on (ambiguous pipe-separated output.state must never be auto-PATCHed over the agent's own value). Claude-Session: https://claude.ai/code/session_01U3biMxRhFGkwtf4jVTNjcj --- .../planning-sonnet-repo/v1/harness.yaml | 5 ++ .../workflow/bug-verify-sonnet@1.yaml | 71 +++++++++++++------ 2 files changed, 55 insertions(+), 21 deletions(-) diff --git a/harnesses/composites/planning-sonnet-repo/v1/harness.yaml b/harnesses/composites/planning-sonnet-repo/v1/harness.yaml index f03b14a..1e2d6a0 100644 --- a/harnesses/composites/planning-sonnet-repo/v1/harness.yaml +++ b/harnesses/composites/planning-sonnet-repo/v1/harness.yaml @@ -8,3 +8,8 @@ layers: - context: anthropic-cloud-paul-oauth/v1 - context: gitea-ssh/v1 - context: agent-repo/v1 + # AU-51/AU-51b: this composite backs bug-verify-sonnet@1, whose agent node + # self-transitions its own item's flow_state (PATCH /v1/work-items/{id}) + # as its final step — see work item 88ecd23e ("agent self-transition" + # pattern, spec/typed-workflows.md). Needs a CP write credential. + - context: cp-service-token/v1 diff --git a/task-templates/workflow/bug-verify-sonnet@1.yaml b/task-templates/workflow/bug-verify-sonnet@1.yaml index e73bba8..fa80aa2 100644 --- a/task-templates/workflow/bug-verify-sonnet@1.yaml +++ b/task-templates/workflow/bug-verify-sonnet@1.yaml @@ -14,6 +14,21 @@ scope_budget: lines_diff_max: 400 wall_clock_seconds_max: 1800 nodes: + # NOTE (2026-09-09, work item 88ecd23e): the `verdict-tx` script node that + # used to follow this one has been REMOVED. `kind: script` nodes are + # harness-exempt (WF-COMP-6 / MN-38) — they run in their own empty + # container with no credentialed way to clone the agent's branch or read + # /workspace/verdict, so verdict-tx never actually worked (it always ran + # against its own empty /workspace). Replaced with the "agent + # self-transition" pattern (spec/typed-workflows.md): this node's own + # `cp-service-token/v1` layer (via the `planning-sonnet-repo` composite) + # gives it CP write credentials, so the agent PATCHes its own item's + # flow_state directly as its final step, instead of relying on a second + # node to read a hand-off file. finalize_run's output.state auto-advance + # is intentionally disabled for this template (its `output.state` is the + # pipe-separated "verified|rejected" — finalize.py treats any + # pipe-separated output.state as schema-validation-only, never a literal + # value to PATCH, so it never clobbers the value set below). - id: agent kind: agent template: planning-sonnet-repo @@ -21,6 +36,7 @@ nodes: runtime_env: AGENT_REPO_URL: "git@gitea.oreillyit.nz-ai-enablement:skynet/agent-runtimes-agents.git" AGENT_BRANCH: "bug-verify/${item.uuid}" + CP_URL: "http://controlplane.agent-runtimes.svc.cluster.local:8100" prompt: | The agent-runtimes project repo is cloned at /workspace/project. Change to that directory before doing any work. @@ -49,24 +65,37 @@ nodes: - `## Verdict` — exactly one of `verified` or `rejected` - `## Rationale` — why you reached that verdict 4. Commit and push the verification artifact on this branch. - 5. Write the bare verdict token — exactly `verified` or exactly - `rejected`, nothing else, no trailing whitespace or punctuation — - to /workspace/verdict. This file is NOT committed to the repo. - (NOTE 2026-09-06: this SHOULD move to scratchpad/verdict, - committed+pushed, once verdict-tx can clone the branch to read it - back — see the runtime_env note on the verdict-tx node below. - Left as the working /workspace/verdict contract for now.) - 6. Call task_complete. - - id: verdict-tx - kind: script - cmd: scripts/workflow/state-tx-verdict - depends_on: [agent] - runtime_env: - STATE_TX_ALLOWED_STATES: "verified,rejected" - # NOTE (2026-09-06): scratchpad/verdict cross-node wiring is NOT yet - # live — script tasks (dispatcher/task_manager.py::_run_script_task) - # have no credentialed way to clone the agent's branch (the per-task - # SSH key is provisioned through the full ESO/account harness - # resolution pipeline, which script tasks deliberately bypass). Left - # at the /workspace/verdict default pending that follow-up; see - # spec/bugs or the M-something item tracking it. + 5. Transition this work item's own flow_state to your verdict by + PATCHing the control plane directly — you have a scoped CP + write credential for exactly this (`CP_SERVICE_TOKEN_FILE`, + `CP_URL`). Set `flow_state` to exactly `verified` or exactly + `rejected` (matching the `## Verdict` you wrote above) — no other + value is accepted. Run: + + ``` + python3 - <<'PY' + import json, os, urllib.request + + VERDICT = "verified" # or "rejected" — must match your ## Verdict + ITEM_UUID = "${item.uuid}" + + token = open(os.environ["CP_SERVICE_TOKEN_FILE"]).read().strip() + url = os.environ["CP_URL"].rstrip("/") + f"/v1/work-items/{ITEM_UUID}" + body = json.dumps({ + "flow_state": VERDICT, + "description": f"bug-verify-sonnet@1: verdict={VERDICT}", + }).encode() + req = urllib.request.Request(url, data=body, method="PATCH", headers={ + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + "Idempotency-Key": f"bug-verify-verdict-{ITEM_UUID}", + }) + with urllib.request.urlopen(req, timeout=30) as resp: + print(resp.status, resp.read().decode()) + PY + ``` + + Replace `VERDICT` with your actual verdict before running. This is + the ONLY code you may run outside the investigation itself — do + not use this credential for anything else. + 6. Call task_complete only after the PATCH above returns 200.