cp_write: true -> state-tx (test-validate@1, frozen-fixture-emit@1) and, in
the archived bug-ancestor-auto-advance@1, tag-set + state-tx.
cp_write: false -> every other script node (verdict-gate, policy-pick,
preflight-*, frozen-fixture-emit, parent-link-check).
tests/invariants/test_cp_write_field_set_correctly.py pins the set.
HOLD: do not merge until the CP release that adds WorkflowNode.cp_write.
The current CP schema is extra="forbid" and rejects the new key
(verified: nodes.0.cp_write extra_forbidden), so on the old CP these
template edits are NOT inert.
Claude-Session: https://claude.ai/code/session_01N218bERyNeLhbLmDRDvbWF
82 lines
3.5 KiB
YAML
82 lines
3.5 KiB
YAML
name: bug-ancestor-auto-advance
|
|
version: 1
|
|
description: >-
|
|
M43 demo — script-only auto-advance for a bug whose parent milestone carries
|
|
the opt-in tag. Functionally what R1 (bug-ai-resolve-seed) does today, minus
|
|
the per-bug tagging step: the trigger rule matches on the PARENT's tag
|
|
(match.ancestor, spec/workflow-triggers.md §3c), this workflow re-checks the
|
|
link in-band, tags the bug `ai-resolve` (so the existing R2 verify rule picks
|
|
it up), then transitions it to verify-pending. Zero LLM nodes.
|
|
runtime: script
|
|
input:
|
|
# A freshly-created bug has no flow_state yet, so eligibility sees its
|
|
# lifecycle state (ItemView.workflow_state = flow_state or state). Accept
|
|
# both lifecycle spellings seen across backends.
|
|
state: pending
|
|
tags_required: []
|
|
tags_forbidden: [blocked, paused, ai-resolve]
|
|
required_artifacts: []
|
|
escalation_input_states: [pending, open]
|
|
output:
|
|
state: verify-pending
|
|
scope_budget:
|
|
# Schema minimum is 1; no node here touches a repo, so these are nominal.
|
|
files_modified_max: 1
|
|
lines_diff_max: 1
|
|
wall_clock_seconds_max: 300
|
|
nodes:
|
|
# Gate: re-verify in-band that the parent milestone still carries the
|
|
# opt-in tag (the trigger's ancestor read may be up to 60s stale on the
|
|
# consumer path, WT-ANC-5). Fails closed — refuse_dispatch on a miss.
|
|
- id: parent-link-check
|
|
kind: script
|
|
cmd: scripts/workflow/parent-link-check
|
|
cp_write: false
|
|
on_fail: refuse_dispatch
|
|
runtime_env:
|
|
CP_URL: "https://agents.oreillyit.nz/api"
|
|
WORKFLOW_ITEM_UUID: "${item.uuid}"
|
|
PARENT_LINK_TAG: auto-resolve-bugs
|
|
PARENT_LINK_RELATION: parent
|
|
# Opt the bug into the existing verify pipeline (R2 bug-ai-resolve-verify
|
|
# requires tags_required: [ai-resolve]).
|
|
- id: tag-ai-resolve
|
|
kind: script
|
|
cmd: scripts/workflow/tag-set
|
|
cp_write: true
|
|
depends_on: [parent-link-check]
|
|
on_fail: escalate
|
|
runtime_env:
|
|
CP_URL: "https://agents.oreillyit.nz/api"
|
|
WORKFLOW_ITEM_UUID: "${item.uuid}"
|
|
TAG_SET_ADD: ai-resolve
|
|
# Advance the bug; R2 then dispatches bug-verify-sonnet@1 at verify-pending.
|
|
- id: state-tx
|
|
kind: script
|
|
cmd: scripts/workflow/state-tx
|
|
cp_write: true
|
|
depends_on: [tag-ai-resolve]
|
|
on_fail: escalate
|
|
runtime_env:
|
|
CP_URL: "https://agents.oreillyit.nz/api"
|
|
STATE_TX_NEW_STATE: verify-pending
|
|
STATE_TX_FROM_STATES: "pending,open"
|
|
# NOTE (script-node credentials): kind: script nodes are harness-exempt
|
|
# (WF-COMP-6), but the flag-on script path composes the workflow-script/v1
|
|
# composite (shared-variables/v1 + cp-service-token/v1; WF-COMP-31). The
|
|
# cp-service-token/v1 layer restages the AU-51 internal-service bearer token
|
|
# and exports CP_SERVICE_TOKEN_FILE=/run/agent/cp-service-token/token, which
|
|
# all three nodes above read via
|
|
# `open(os.environ["CP_SERVICE_TOKEN_FILE"]).read().strip()`. The legacy
|
|
# WF-COMP-26 mount (`secrets_required: [CP_SERVICE_TOKEN]` and
|
|
# `runtime_env.CP_SERVICE_TOKEN_FILE=/run/secrets/CP_SERVICE_TOKEN`, backed by
|
|
# the dispatcher's legacy-key branch) is removed (WF-COMP-33 step 10). Each
|
|
# script still resolves its own actor identity (OIDC_IDENTITY_EMAIL or
|
|
# CP_Caller-Identity header — see scripts/workflow/tag-set and
|
|
# parent-link-check) for the write envelope.
|
|
#
|
|
# STATE_TX_FROM_STATES on the state-tx node is the compare-and-set guard: the
|
|
# live flow_state must be one of the listed states (or already the target),
|
|
# else state-tx exits 2 (unexpected_current_state). pending,open mirror
|
|
# `input.state` + `escalation_input_states`.
|