Repository navigation
Verify transcript parity across workflow spawn modes (#52) - #58
Conversation
beesarmy
left a comment
There was a problem hiding this comment.
Summary
Stack-side verification that the unified workflow path emits canonical transcript events across spawn modes. The core claim checks out: none/local now produce identical tool_call/tool_result/result skeletons end-to-end through the real LocalWorkflowRunner, failure runs persist a canonical error event, and per-mode gaps are documented. Approving — all findings below are non-blocking.
Verified independently:
- CI green (
e2e-mock,unit-and-integrationon 3.12/3.13); no other reviewer feedback pending. - The pin
91d48abmatches the current head of upstreamlightspeed-core/lightspeed-cloud-agents#273(still OPEN), anduv.lockis consistent withpyproject.toml. - Mock change is backward compatible: with
tool_call=None,_select_bodyreduces exactly to the previous code path (same_response_text_for_request+_canned_response_bodyflow, including thejson_schemastructured-output branch). - No
src/changes — verification-only on the stack side, as described. New mock helpers (_select_body,_canned_function_call_body,_input_has_function_call_output) each have direct unit tests, and both touched e2e tests extend real-HTTP assertions.
Findings (non-blocking):
- [LOW] The commit message says "bump lightspeed-cloud-agents pin to rev 295a7a8" but the actual pin is
91d48ab(the re-bump after the evaluator round). Also, "lightspeed-cloud-agents#273" is ambiguous — there is no PR 273 in thejameswnlfork; the dependency islightspeed-core/lightspeed-cloud-agents#273. Suggest fixing the rev in the commit message on merge and using the fully-qualified repo in the PR body. - [MED] The
ephemeralleg of issue #52's acceptance criterion 1 ("the same workflow step yields the same transcript event types across none, local, and ephemeral") is covered at contract/shape level only:REFERENCE_EPHEMERAL_JSONLis hand-authored rather than captured from a real ephemeral run, and it contains athinkingevent the scriptednone/localruns never emit — so same-step event equality is asserted fornone/localbut not against a realephemeraltranscript. The PR body is transparent about the live-sandbox constraint, so this is follow-up material, not a merge blocker. Suggested follow-up: "Capture golden ephemeral transcript for parity tests — run the parity step once against a live sandbox, store the resulting event skeleton as the reference (replacing the hand-writtenREFERENCE_EPHEMERAL_JSONL), and assertnone/localskeletons equal it." - [LOW]
tests/integration/cloud_agents/test_workflow_transcript_parity.pyimports fromtests.e2e.cloud_agents.mock_llm_server— the first integration→e2e cross-suite import. Works fine, but if the mock gains more consumers, consider moving it to shared test helpers. - [LOW]
_step_eventsreaches into the privaterunner._transcript_store(hence the module-levelprotected-accessdisable). The publicget_step_transcripts()accessor — already used bytest_get_step_transcripts_exposes_canonical_events— could serve all tests uniformly. - [LOW] Nits in the mock:
_input_has_function_call_outputassumesinputis str-or-list (a truthy non-list would raiseTypeErroriterating); and the fixedcall_id="call_mock_1"would be reused if the first turn were ever retried. Neither is reachable in these deterministic tests, but a defensiveisinstance(input_value, list)guard would be cheap.
🤖 Reviewed by beesarmy via Claude Code
Review of PR #58: Verify transcript parity across workflow spawn modes (#52)Checked at PR head Summary: The stack-side tests are well built: real Blocking1. The pin breaks
|
Pin lightspeed-cloud-agents to the transcript-parity branch head (lightspeed-core/lightspeed-cloud-agents#273, rev 91d48ab) and verify on the stack side that the same workflow step yields the same canonical transcript events across spawn modes none/local (end-to-end) and ephemeral (contract level). - bump lightspeed-cloud-agents pin to rev 91d48ab (canonical events from DirectExecutor/SubprocessExecutor; revert to branch=main once lightspeed-core#273 merges) - integration tests: one-step and multi-step workflows through the real LocalWorkflowRunner + in-memory stores against a scripted mock LLM -- none/local yield identical event sequences (tool_call, tool_result, result with the sandbox data-key contract), read via the public get_step_transcripts accessor; failed runs persist error events; verified RED against the pre-parity pin (6/9 failing) - mock LLM server: opt-in scripted tool-call mode (Responses API function_call round-trip, unique call ids, defensive input guard) for deterministic cross-mode tool runs - e2e: one-step none and ephemeral transcript assertions now pin the canonical event shape over real HTTP - docs: workflow transcript contract + documented per-mode gaps in integration-architecture.md; demo script notes canonical jq projections Co-Authored-By: Claude <noreply@anthropic.com>
ea3810e to
6dd38f2
Compare
beesarmy
left a comment
There was a problem hiding this comment.
Reviewed current head 6dd38f2 and cloud-agents pin 91d48ab. The focused parity and mock-server suites pass: 16 tests, using the existing stack virtual environment with PYTHONPATH explicitly selecting the exact pinned cloud-agents source and this PR checkout. No live ephemeral sandbox or full suite was run.
The stack-side one-step/multi-step verification is useful, but this dependency bump also changes the ChatWorkflowRunner used by /query/direct and /query/direct/stream. The pin still contains the blocking tool-history regression described in the existing owner comment. This review supersedes beesarmy's earlier approval of ea3810e: passing workflow parity tests do not make the pin safe for multi-turn chat.
I independently reviewed cloud-agents lightspeed-core#273 and posted three reproduced findings: mismatched call/result IDs and parallel-call names (P1), truncated arguments persisted as invalid JSON for replay (P2), and local output-schema failures emitting only result rather than error events (P2). See lightspeed-core/lightspeed-cloud-agents#273 (review). Please repin pyproject.toml and uv.lock after those fixes.
For regression coverage, add a two-turn chat/direct-query case that performs a tool call and replays the saved turn, including a large argument. The new mock currently returns HTTP 200 even when function_call_output.call_id does not match the function_call in input; I reproduced that behavior, so it would mask the provider rejection unless strengthened. The current failure-parity test exercises only spawn:none connection failures; a spawn:local output-schema parsing failure would expose the missing error event.
The hand-authored ephemeral reference remains contract coverage rather than a real three-mode comparison, as already documented in the PR and earlier review. I am not treating that separately tracked follow-up as an additional blocker.
| # Pinned to the transcript-parity branch (canonical transcript events | ||
| # from all spawn modes) until lightspeed-cloud-agents#273 merges to main; | ||
| # then revert to branch = "main" and re-lock. | ||
| lightspeed-cloud-agents = { git = "https://github.com/jameswnl/lightspeed-cloud-agents.git", rev = "91d48ab073f014564a22443f40a870cc16f47711" } |
There was a problem hiding this comment.
[P1] Repin after fixing the tool-history regression in cloud-agents
This revision changes ChatWorkflowRunner._save_turn to persist tool messages without IDs, but its _build_message_history increments the fallback counter for both a call and its return. Replaying a single parity_probe exchange produces call_parity_probe_0 for the call and call_parity_probe_1 for the return. The stack's workflow/query_executor.py uses this ChatWorkflowRunner for /query/direct and /query/direct/stream, so the next chat turn after tool use sends an unmatched tool output to the provider and can fail. Multiple calls also inherit the last call's name for every result. This is a runtime regression introduced by the dependency bump, despite the workflow-only parity tests passing. Fix the pairing upstream and update both this pin and uv.lock before merging; add save/replay or two-turn direct-query coverage with matching-ID validation. Upstream reproduction and requested fixes: lightspeed-core/lightspeed-cloud-agents#273 (review).
beesarmy
left a comment
There was a problem hiding this comment.
Re-reviewed b946979 and its cloud-agents pin b5a1902. The earlier stack review requests have been addressed: the pin and lock agree; the mock rejects unmatched tool-output IDs; two-turn chat regressions inspect actual provider requests and preserve the real small-argument output; large-argument exchanges are excluded from replay; both spawn modes and both parsing paths have failure assertions; and shared canonical data-key checks now cover the strengthened HTTP tests. No additional stack-specific defect found in these changes.
Validation: 25 parity/mock-server tests passed against these exact two source checkouts using the existing stack virtual environment with explicit PYTHONPATH. The focused cloud-agents suite also passed 194 tests. No live ephemeral sandbox or full suite was run. The live golden-transcript follow-up remains documented.
I found one remaining dependency issue while auditing the updated pairing implementation: explicit-ID results leave stale entries in pending_calls, so a later ID-less result reuses a completed call's ID when replaying mixed history. I reproduced this across two turns. The pinned revision includes that bug, so please update the pin/lock once the small upstream correction lands. Full finding: lightspeed-core/lightspeed-cloud-agents#273 (review).
| # Pinned to the transcript-parity branch (canonical transcript events | ||
| # from all spawn modes) until lightspeed-core/lightspeed-cloud-agents#273 merges to main; | ||
| # then revert to branch = "main" and re-lock. | ||
| lightspeed-cloud-agents = { git = "https://github.com/jameswnl/lightspeed-cloud-agents.git", rev = "b5a1902fb34a99492ba8c3b37580246e27890a60" } |
There was a problem hiding this comment.
[P2] Repin after the remaining mixed-history pairing fix
The updated pin fixes the original canonical-only replay failures, but _build_message_history still keeps calls in its pending queue after an explicitly identified return. Replaying an explicit-ID prior turn followed by a new ID-less tool turn produces old_tool call/return = old-id, new_tool call = call_new_tool_1, new_tool return = old-id. This affects the same ChatWorkflowRunner used by the direct-query bridge whenever it loads mixed history. The new tests exercise entirely ID-less saved turns, so all 25 focused stack tests pass without exposing this case. Please bump this pin and uv.lock after the upstream queue fix; see lightspeed-core/lightspeed-cloud-agents#273 (review).
|
Coverage follow-up for head
The small-argument and large-argument two-turn chat cases, orphan-ID mock rejection, both spawn modes' connection failures, and all four spawn/path combinations for schema parsing failures are exercised. Shared canonical data-key assertions run through none/local integration tests; the real ephemeral HTTP assertion still requires live-sandbox execution and was not run here. The remaining gap is mixed explicit/synthesized tool-ID history, identified upstream. All current two-turn regression cases begin with new ID-less persistence, so they do not enter the broken explicit-ID return branch. The full cloud-agents unit suite also leaves that branch uncovered. Please include that case with the upstream fix before repinning. Coverage details: lightspeed-core/lightspeed-cloud-agents#273 (review). |
beesarmy
left a comment
There was a problem hiding this comment.
Approved after the requested 30-minute follow-up review of head 71de7f0. pyproject.toml and uv.lock consistently pin cloud-agents 1194ff0, which fixes the remaining mixed-history queue bug. I verified the matching-ID correction, its mixed-ID/out-of-order regressions, and the original reproduction upstream; all blocking findings from my earlier reviews are resolved. No new stack-specific defect found.
Validation and coverage:
- 149 cloud-agents integration/unit and mock-server tests passed locally, with 1 skip, using this exact stack checkout and dependency source.
- Mock server: 96% line / 93% branch coverage; all 45 added executable lines and all branch arcs starting on added lines are covered.
- Canonical-event helper: all 8 added executable lines are covered; overall helper coverage is lower because existing live HTTP/database utilities are outside the local suites.
- Upstream focused/integration tests passed 206 cases with branch coverage; the previously missing explicit-ID handling and new matching/removal logic are exercised.
- Current-head CI is green for unit/integration on Python 3.12 and 3.13 and for e2e-mock.
The live ephemeral golden-transcript comparison remains the documented follow-up; no live sandbox was run locally. The earlier non-blocking upstream coverage suggestions remain follow-up work rather than merge blockers.
Closes #52. Uses the merged lightspeed-core/lightspeed-cloud-agents#273 implementation. The dependency source is restored to
branch = "main"; uv.lock resolves the merge commit1788b17d84420ca07c8df423a460bcae5b369f66.Summary
Verify canonical workflow transcripts through the real LocalWorkflowRunner and in-memory stores using a scripted mock LLM. Equivalent one-step and multi-step runs produce matching none/local events through the public transcript accessor. Shared assertions check event shape and data keys in none/local integration tests and none/ephemeral HTTP e2e tests.
The dependency update also changes chat history used by
/query/directand/query/direct/stream. Two-turn ChatWorkflowRunner regressions inspect actual provider requests for matching tool call/result IDs and preserved outputs. Large-argument coverage verifies that potentially truncated exchanges are omitted from replay while audit events remain intact. The mock rejects tool outputs whose call ID has no matching call in the request.Failure tests cover none/local connection failures and output-schema parsing failures through both model-request and agent paths. Documentation explains aggregate/per-turn result-event differences, summing token usage, unknown cost, and bounded replay.
Ephemeral coverage remains contract coverage plus strengthened HTTP assertions; a captured live-sandbox golden transcript is a separate follow-up. No live ephemeral run was performed in this update.
Validation
uv run make test-unit: 3,382 passed, 1 skipped; coverage 90.21%.The latest dependency also fixes mixed explicit/synthesized call IDs: explicit returns consume their pending call by ID, allowing out-of-order explicit results without leaving completed calls available to later ID-less turns. The 25 parity/mock tests and full stack unit suite were rerun successfully against this pin.
After the upstream merge, all 25 parity/mock tests and 3,382 stack unit tests passed again against the merged dependency.