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
This commit is contained in:
Paul O'Reilly
2026-09-09 13:56:34 +12:00
parent 5a8cd32aec
commit 33501ca54e
2 changed files with 55 additions and 21 deletions

View File

@@ -8,3 +8,8 @@ layers:
- context: anthropic-cloud-paul-oauth/v1 - context: anthropic-cloud-paul-oauth/v1
- context: gitea-ssh/v1 - context: gitea-ssh/v1
- context: agent-repo/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

View File

@@ -14,6 +14,21 @@ scope_budget:
lines_diff_max: 400 lines_diff_max: 400
wall_clock_seconds_max: 1800 wall_clock_seconds_max: 1800
nodes: 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 - id: agent
kind: agent kind: agent
template: planning-sonnet-repo template: planning-sonnet-repo
@@ -21,6 +36,7 @@ nodes:
runtime_env: runtime_env:
AGENT_REPO_URL: "git@gitea.oreillyit.nz-ai-enablement:skynet/agent-runtimes-agents.git" AGENT_REPO_URL: "git@gitea.oreillyit.nz-ai-enablement:skynet/agent-runtimes-agents.git"
AGENT_BRANCH: "bug-verify/${item.uuid}" AGENT_BRANCH: "bug-verify/${item.uuid}"
CP_URL: "http://controlplane.agent-runtimes.svc.cluster.local:8100"
prompt: | prompt: |
The agent-runtimes project repo is cloned at /workspace/project. The agent-runtimes project repo is cloned at /workspace/project.
Change to that directory before doing any work. Change to that directory before doing any work.
@@ -49,24 +65,37 @@ nodes:
- `## Verdict` — exactly one of `verified` or `rejected` - `## Verdict` — exactly one of `verified` or `rejected`
- `## Rationale` — why you reached that verdict - `## Rationale` — why you reached that verdict
4. Commit and push the verification artifact on this branch. 4. Commit and push the verification artifact on this branch.
5. Write the bare verdict token — exactly `verified` or exactly 5. Transition this work item's own flow_state to your verdict by
`rejected`, nothing else, no trailing whitespace or punctuation — PATCHing the control plane directly — you have a scoped CP
to /workspace/verdict. This file is NOT committed to the repo. write credential for exactly this (`CP_SERVICE_TOKEN_FILE`,
(NOTE 2026-09-06: this SHOULD move to scratchpad/verdict, `CP_URL`). Set `flow_state` to exactly `verified` or exactly
committed+pushed, once verdict-tx can clone the branch to read it `rejected` (matching the `## Verdict` you wrote above) — no other
back — see the runtime_env note on the verdict-tx node below. value is accepted. Run:
Left as the working /workspace/verdict contract for now.)
6. Call task_complete. ```
- id: verdict-tx python3 - <<'PY'
kind: script import json, os, urllib.request
cmd: scripts/workflow/state-tx-verdict
depends_on: [agent] VERDICT = "verified" # or "rejected" — must match your ## Verdict
runtime_env: ITEM_UUID = "${item.uuid}"
STATE_TX_ALLOWED_STATES: "verified,rejected"
# NOTE (2026-09-06): scratchpad/verdict cross-node wiring is NOT yet token = open(os.environ["CP_SERVICE_TOKEN_FILE"]).read().strip()
# live — script tasks (dispatcher/task_manager.py::_run_script_task) url = os.environ["CP_URL"].rstrip("/") + f"/v1/work-items/{ITEM_UUID}"
# have no credentialed way to clone the agent's branch (the per-task body = json.dumps({
# SSH key is provisioned through the full ESO/account harness "flow_state": VERDICT,
# resolution pipeline, which script tasks deliberately bypass). Left "description": f"bug-verify-sonnet@1: verdict={VERDICT}",
# at the /workspace/verdict default pending that follow-up; see }).encode()
# spec/bugs or the M-something item tracking it. 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.