AxonFlow v7.4.0 Release Notes
AxonFlow v7.4.0 - HITL Response Parity - closes the gap that opened between the WCP and MAP human-in-the-loop surfaces as each plane grew on its own. Reviewer tools now get the same rich response shape from both planes, and the MAP-plane listing and approve/reject endpoints land at the same tier gate as the WCP ones.
Three additions that matter in practice:
- Rich approve/reject responses on both planes.
POST /api/v1/workflows/{id}/steps/{step_id}/approve|rejectand the MAP counterpartPOST /api/v1/plans/{id}/steps/{step_id}/approve|rejectnow return a unified shape withdecision,retry_context,approval_id,approved_by/atorrejected_by/at, andpolicies_matched. Before v7.4.0 the WCP response was{workflow_id, step_id, approval_status, approved_by, message}and the MAP response was even thinner ({plan_id, step_id, status, execution_id}). Both planes now surface the same field set. - MAP plan-scoped approve/reject lowered to Evaluation tier. Previously Enterprise-only. Reviewer integrations that target either plane now see the same tier gate.
- New MAP plane-scoped pending-approvals listing.
GET /api/v1/plans/approvals/pendingwith optional?plan_id=filter. Returns{pending_approvals, count}withplan_idpopulated on every entry - the intentional asymmetry with the WCP listing at/api/v1/workflows/approvals/pendingwhich never surfacesplan_id.
No breaking changes. Legacy fields (workflow_id, step_id, status) remain on every approve/reject response. Older SDKs that only read those keep working; newer SDKs pick up the rich fields.
This release matters most if you are:
- Building reviewer UIs or compliance portals that integrate with AxonFlow's HITL surface and want a single response shape across both planes
- Running the MAP control plane for governed agent plans and want the same HITL semantics you already get on WCP
- Using any of the four AxonFlow SDKs; the new versions (Go v5.6.0, TypeScript v5.6.0, Python v6.6.0, Java v5.7.0) expose the richer response shape and the new pending-list method
Community - unified projection helper
The core change is a shared HTTP projection helper for HITL responses. Community builds ship the helper so downstream tools and tests can project the same wire shape without duplicating the field set. Community does not expose the HITL approve/reject endpoints (they are Evaluation+), but the shared code path is what keeps the two planes locked together.
ProjectStepGateToHTTP helper
The orchestrator now has one function that every HITL response flows through. A field added to the response struct surfaces on both planes automatically. A contract test (TestHITLResponseParity) in the orchestrator package asserts the WCP and MAP handler responses share the same key set modulo the intentional plan_id asymmetry.
Plan-to-workflow lookup
workflow_control.GetWorkflowByPlanID (service) + GetByPlanID (Postgres repository, backed by the metadata->>'plan_id' JSONB expression) let the MAP plan-scoped endpoints resolve to the underlying WCP workflow step row and project the same rich shape. This is what makes MAP's approve/reject returns match WCP byte-for-byte instead of rolling a thinner shape.
Evaluation - rich responses + MAP parity
Rich WCP approve/reject responses
POST /api/v1/workflows/{id}/steps/{step_id}/approve and .../reject now return:
{
"workflow_id": "wf-abc-123",
"step_id": "step-2",
"decision": "allow",
"reason": "Approved: High-value transfer requires oversight",
"approval_status": "approved",
"approval_id": "318a270f-7b42-5c56-a191-8dbd1bf2e1e4",
"approved_by": "compliance-officer-7",
"approved_at": "2026-04-22T10:05:00Z",
"policies_matched": [
{
"policy_id": "f9b22075-7e59-4ba2-a31e-bb6e059ae96b",
"policy_name": "High-Value Wire Transfer Oversight",
"action": "require_approval",
"risk_level": "high",
"allow_override": false,
"policy_description": "Require human approval on transactions above $10,000"
}
],
"retry_context": {
"gate_count": 1,
"completion_count": 0,
"prior_completion_status": "none",
"prior_output_available": false,
"prior_output": null,
"prior_completion_at": null,
"idempotency_key": "payment-intent-123",
"last_decision": "require_approval",
"first_attempt_at": "2026-04-22T10:00:00Z",
"last_attempt_at": "2026-04-22T10:00:00Z"
},
"message": "Step approved"
}
decision flips to allow on a successful approval and block on rejection. retry_context mirrors the v7.3.0 gate response shape, so a reviewer tool renders retry state without a second API call. approval_id is the deterministic HITL queue UUID (UUID v5 over (workflow_id, step_id)); it matches the queue row for operators who cross-reference. policies_matched reconstructs the governance trail that triggered the original require_approval decision.
The reject response uses the same shape with rejected_by / rejected_at populated instead of approved_by / approved_at, and decision set to block.
Audit comment and reason now required on the wire
Both POST /approve and POST /reject require a request body with an audit justification:
- Approve:
{"comment": "..."}- minimum 10 characters - Reject:
{"reason": "..."}- minimum 10 characters
The justification lands on the audit log alongside the reviewer identity. All four SDKs updated their signatures in the v7.4.0-train to expose this argument; pre-v7.4.0 SDK calls would silently send an empty body and get a 400.
MAP plan-scoped approve/reject on Evaluation
POST /api/v1/plans/{id}/steps/{step_id}/approve|reject was Enterprise-only before v7.4.0. Now on Evaluation+. The response has the same shape as the WCP response plus a plan_id field echoing the plan path parameter - the single acknowledged asymmetry between the two planes.
curl -X POST "http://localhost:8080/api/v1/plans/plan-abc123/steps/step_0_analyze/approve" \
-H "Content-Type: application/json" \
-H "X-User-ID: compliance-officer-7" \
-d '{"comment": "Approved after full audit review"}'
MAP confirm and step execution modes - the entry side that creates the approval-gated plan in the first place - remain Enterprise-only in this release. Those stay gated on deployment mode. A separate follow-up lowers those to Evaluation+.
New: MAP plane-scoped pending approvals listing
GET /api/v1/plans/approvals/pending returns the same {pending_approvals, count} shape as the existing WCP listing at /api/v1/workflows/approvals/pending, scoped to MAP-backed workflows only. Every entry has plan_id populated.
# All MAP-plane pending approvals for the tenant
curl -X GET "http://localhost:8080/api/v1/plans/approvals/pending"
# Scoped to a single plan
curl -X GET "http://localhost:8080/api/v1/plans/approvals/pending?plan_id=plan-abc123"
Example response:
{
"pending_approvals": [
{
"workflow_id": "wf-map-456",
"workflow_name": "map-confirm-plan-abc123",
"plan_id": "plan-abc123",
"step_id": "step_0_analyze",
"step_index": 0,
"step_name": "Analyze customer transaction",
"step_type": "tool_call",
"decision": "require_approval",
"approval_status": "pending",
"created_at": "2026-04-22T10:00:00Z"
}
],
"count": 1
}
The WCP and MAP listings are reviewer-tool conveniences. Clients that want a plane-neutral view of every approval in the tenant can use /api/v1/hitl/queue (Enterprise) which already sees both planes.
Cross-plane contract test in CI
TestPendingApprovalsPlaneParity (orchestrator) asserts the two pending listings return the same field set modulo the plan_id asymmetry. TestHITLResponseParity does the same for approve/reject. Both fail loudly if either plane drops a field the other surfaces.
SDKs - matching method signatures and response types
All four SDKs ship new versions with the richer response types and the new pending-list method:
- Go SDK v5.6.0 (
axonflow-sdk-go) -ApproveStepResponse/RejectStepResponsegainDecision,Reason,ApprovalStatus,ApprovalID,ApprovedBy/ApprovedAt/RejectedBy/RejectedAt,PoliciesMatched,RetryContext,Message,PlanID. NewGetPendingPlanApprovalsmethod withPendingApprovalsOptions.PlanIDfilter. Signature ofApproveStep/RejectStepnow takes a comment / reason as the third argument. Three pre-existing URL bugs fixed:ApproveStep/RejectStep/GetPendingApprovalsnow hit/api/v1/workflows/...routes instead of the non-existent/api/v1/workflow-control/...paths. - TypeScript SDK v5.6.0 (
axonflow-sdk-typescript) - same rich fields onApproveStepResponse/RejectStepResponseinterfaces. NewgetPendingPlanApprovalsmethod.approveStep/rejectStepoptionalcomment/reasonargument. Same URL fixes. - Python SDK v6.6.0 (
axonflow-sdk-python) - rich optional fields on the pydanticApproveStepResponse/RejectStepResponsemodels. Newget_pending_plan_approvalsmethod withplan_idargument.approve_step/reject_stepoptionalcomment/reason. Same URL fixes. Response deserialization now usesmodel_validate(response)so the server's rich fields (approval_id,approved_at,retry_context,policies_matched) no longer get silently dropped. - Java SDK v5.7.0 (
axonflow-sdk-java) - rich fields onWorkflowTypes.ApproveStepResponseandRejectStepResponse. Back-compat 3-arg constructors preserved so existing test fixtures keep compiling. NewgetPendingPlanApprovalsand async variant. Three-argapproveStep(workflowId, stepId, comment)overload added. Same URL fixes.
SDK CHANGELOGs have per-language detail. Callers that only read workflow_id / step_id / status keep working unchanged.
PendingApproval type extensions
Every SDK's PendingApproval (or equivalent) gains:
plan_id- optional, populated on MAP-plane entries, absent on WCP-plane entriesstep_index- zero-based step position within the workflowdecision- alwaysrequire_approvalfor listed entriesdecision_reason- why the policy engine paused the steppolicies_matched- policies that triggered the approval requirementstep_input- step input payload (may be redacted by PII rules)approval_status-pendingfor listed entries
PendingApprovalsResponse wire-shape alignment
Pre-v7.4.0 every SDK declared {approvals, total} but the server has always emitted {pending_approvals, count}. Callers who deserialised against the typed struct got empty arrays even when the server returned entries. Fixed in every SDK. If you read response.approvals / response.total in your code, rename to response.pending_approvals / response.count.
OpenAPI and documentation
docs/api/orchestrator-api.yaml now carries the /api/v1/plans/approvals/pending path and a corrected schema for the existing WCP pending endpoint. Both reference the shared PendingApprovalsResponse and PendingApproval schemas. The prior schema declared fields (approvals, total, requested_at, reason) that the server never emitted; this release aligns it with the real wire contract.
On the docs site:
- HITL Approval Gates - full API reference for both planes including the new pending-list endpoint
- Human-in-the-Loop - operating model and MAP Plan-Scoped Equivalents section
- Community vs Enterprise feature matrix - rows for plane-scoped approve/reject, plane-scoped pending listings, and cross-plane response parity
The response-parity rule is documented on the HITL Approval Gates page, including the single intentional asymmetry (plan_id populated on MAP responses, empty on WCP responses) and the contract tests that lock the two planes together.
Migration notes
- SDK callers of approve/reject. Update call sites to pass
comment/reasonas the third argument. Server requires min 10 chars; SDK will pass whatever you supply. - SDK callers of pending-list. If your code reads
response.approvalsorresponse.total, rename toresponse.pending_approvals/response.count. - Reviewer-tool integrators. If you have separate code paths for WCP and MAP approve/reject, you can collapse them. The response shape is now identical; branch on
plan_idbeing populated if you need to render plan context. - Customers running v7.3.0 or earlier. v7.4.0 is a minor bump. Rolling restart of the orchestrator and agent is enough. The audit-comment requirement is not new - it has been enforced server-side since the HITL surface shipped; the SDK change is just exposing the argument so the request body is no longer empty.
Full changelog
The per-release narrative above covers the user-visible surface. Per-SDK CHANGELOGs carry method-level detail on the new argument shapes, URL fixes, and typed response additions; they ship in each SDK's installed package and repository README.
