From 98baf453a6b6662824d3de577384d03c48ccd8f0 Mon Sep 17 00:00:00 2001 From: Gregory Zak Date: Sun, 19 Jul 2026 08:47:08 -0700 Subject: [PATCH] fix(runtime-e2e): verify unknown default + drop stale merge-status note (#2912) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit axonflow-enterprise#2953 (caller_name) has merged and shipped in platform v9.11.0; it folded in #2903, which changed the fallback default written to policy_details.caller_name when neither caller_name nor tool_type is supplied from "claude_code" to "unknown" (an unidentified caller must not be silently attributed to a specific client). - runtime-e2e/caller_name_audit/test.py: add a third real-stack scenario asserting the neither-supplied case resolves to "unknown", not "claude_code". Verified against a freshly built axonflow-enterprise main (commit 7a5984ec7+, platform v9.11.0) — confirmed via direct platform inspection (audit_logs.policy_details) and through this SDK's own audit_tool_call before landing the assertion. - runtime-e2e/caller_name_audit/README.md: replace the stale "#2953 is an open PR, not yet merged" prerequisite note (accurate when PR #218 merged, no longer true) with the current v9.11.0 platform requirement; document the third scenario. No axonflow SDK code changes -- caller_name/tool_type were already both independently settable, so the SDK needed no "dual-send" cutover. Signed-off-by: Gregory Zak --- runtime-e2e/caller_name_audit/README.md | 29 +++++++++++-------- runtime-e2e/caller_name_audit/test.py | 38 +++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/runtime-e2e/caller_name_audit/README.md b/runtime-e2e/caller_name_audit/README.md index 5de1492..559bd36 100644 --- a/runtime-e2e/caller_name_audit/README.md +++ b/runtime-e2e/caller_name_audit/README.md @@ -20,6 +20,11 @@ that it marshals onto the wire correctly (that's covered by the equals that value verbatim. 2. Legacy `tool_type="mcp"` with **no** `caller_name` → the deprecated fallback still resolves `policy_details.caller_name` to `"mcp"`. +3. **Neither** `caller_name` **nor** `tool_type` supplied → the platform's + default-fallback (getaxonflow/axonflow-enterprise#2903, folded into the + same #2953 merge) resolves `policy_details.caller_name` to `"unknown"` — + not the pre-#2903 default of `"claude_code"`. An unidentified caller must + never be silently attributed to a specific client. The SDK's typed `AuditLogEntry` does not declare a `policy_details` field (it's an internal JSONB blob), so the read side uses a raw `httpx` call @@ -39,18 +44,17 @@ local-dev Enterprise stack in `axonflow-enterprise/main-tree/.env`). In Community mode, tenant-audit reads are tenant-wide regardless, so the header is a no-op there. -## Prerequisite: platform support is not yet on `main` +## Prerequisite: platform version -`caller_name` support (axonflow-enterprise#2953) is implemented but, as of -this writing, still an open PR on the `feat/2912-caller-name-tool-type-deprecation` -branch — not yet merged to `axonflow-enterprise` main. Against a stack built -from `axonflow-enterprise` main, this test will FAIL (the 45s poll of -`GET /api/v1/audit/tenant/{tenant_id}` times out waiting for -`policy_details.caller_name`, which the server doesn't write yet) — that's -not a bug in this test, it means the platform side isn't deployed on -whatever stack you're pointed at. Point your local `axonflow-enterprise` -checkout at that branch (or a later commit that includes it) before running -this test. +`caller_name` support (axonflow-enterprise#2953, which also folded in the +#2903 default-fallback fix) is merged to `axonflow-enterprise` main and +shipped in platform release **v9.11.0**. Point your stack at v9.11.0+ (or a +`main` checkout at/after commit `7a5984ec7`) before running this test. +Against an older platform, the third scenario (`unknown` default) and +possibly `policy_details.caller_name` itself will not resolve as expected, +and the 45s poll of `GET /api/v1/audit/tenant/{tenant_id}` will time out +waiting for a row that never lands with the expected shape — that's a +platform-version mismatch, not a bug in this test. ## Run @@ -65,7 +69,8 @@ The orchestrator's `AuditLogger` batches writes (flush every 10s per `platform/orchestrator/audit_logger.go`), so the test polls `GET /api/v1/audit/tenant/{tenant_id}` for up to 45s before failing. -Exits non-zero if `caller_name` (or the `tool_type` fallback) does not reach +Exits non-zero if `caller_name`, the `tool_type` fallback, or the +neither-supplied `"unknown"` default does not reach `policy_details.caller_name` on the real row. ## Companion unit coverage diff --git a/runtime-e2e/caller_name_audit/test.py b/runtime-e2e/caller_name_audit/test.py index cce33ae..8fa7972 100644 --- a/runtime-e2e/caller_name_audit/test.py +++ b/runtime-e2e/caller_name_audit/test.py @@ -22,10 +22,16 @@ 2. Call it again with only the legacy `tool_type` set (no `caller_name`) to prove the backward-compat fallback still resolves into `policy_details.caller_name` on a real row. - 3. The orchestrator's AuditLogger batches writes (flush every 10s), so + 3. Call it a third time with NEITHER `caller_name` NOR `tool_type` set, + to prove the platform's default-fallback (getaxonflow/axonflow- + enterprise#2903, folded into the same #2953 merge) resolves to + `"unknown"` -- an unidentified caller must NOT be silently attributed + to a specific client. `"unknown"` replaced the pre-#2903 default of + `"claude_code"`. + 4. The orchestrator's AuditLogger batches writes (flush every 10s), so poll `GET /api/v1/audit/tenant/{tenant_id}` (the same route `client.get_audit_logs_by_tenant` hits) until each row lands. - 4. The SDK's typed `AuditLogEntry` does not surface `policy_details` + 5. The SDK's typed `AuditLogEntry` does not surface `policy_details` (it's an internal JSONB blob), so this step reads the raw JSON body directly -- the only way to observe `policy_details.caller_name` from outside the platform -- and asserts it equals what we sent. @@ -77,6 +83,8 @@ WORKFLOW_CALLER_NAME = f"e2e-caller-name-wf-{_RUN_ID}" WORKFLOW_TOOL_TYPE_FALLBACK = f"e2e-tool-type-fallback-wf-{_RUN_ID}" WANT_TOOL_TYPE_FALLBACK = "mcp" +WORKFLOW_NEITHER_SUPPLIED = f"e2e-neither-supplied-wf-{_RUN_ID}" +WANT_DEFAULT_CALLER_NAME = "unknown" # #2903: was "claude_code" pre-fix # Batch writer flushes every 10s (platform/orchestrator/audit_logger.go); # give it generous headroom under CI/local load. @@ -168,6 +176,19 @@ async def main() -> None: f"SDK audit_tool_call (tool_type fallback) -> audit_id={resp2.audit_id} status={resp2.status}" ) + # 3. Neither caller_name nor tool_type supplied -- the platform's + # default-fallback (#2903) must resolve to "unknown", never silently + # attribute an unidentified caller to a specific client. + resp3 = await client.audit_tool_call( + AuditToolCallRequest( + tool_name="e2eNeitherSuppliedTool", + workflow_id=WORKFLOW_NEITHER_SUPPLIED, + ) + ) + print( + f"SDK audit_tool_call (neither supplied) -> audit_id={resp3.audit_id} status={resp3.status}" + ) + policy_details_1 = _fetch_policy_details(WORKFLOW_CALLER_NAME) got_caller_name = policy_details_1.get("caller_name") if got_caller_name != WANT_CALLER_NAME: @@ -195,6 +216,19 @@ async def main() -> None: ) print(f"Wire policy_details (tool_type fallback case): {policy_details_2}") + policy_details_3 = _fetch_policy_details(WORKFLOW_NEITHER_SUPPLIED) + got_default = policy_details_3.get("caller_name") + if got_default != WANT_DEFAULT_CALLER_NAME: + _fail( + f"default fallback: policy_details.caller_name = {got_default!r}, want " + f"{WANT_DEFAULT_CALLER_NAME!r} (#2903) (full policy_details: {policy_details_3})" + ) + print( + f"PASS: neither caller_name nor tool_type supplied -> " + f"policy_details.caller_name = {got_default!r} (#2903 default, not 'claude_code')" + ) + print(f"Wire policy_details (neither-supplied case): {policy_details_3}") + print("ALL PASS: caller_name (#2912) verified end-to-end through the real SDK + platform")