mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 11:48:42 +02:00
* fix(openclaw): parse whole buffer instead of line-by-line scanner Follow-up toc87d7676(WOR-10). The stdout/stderr swap fixed the dominant case but `processOutput` still scanned line-by-line and only attempted a whole-buffer parse from a fragile fallback path. Pretty-printed JSON (openclaw 2026.5.x emits the result blob indented across many lines) made every individual line unparseable on its own — `{`, ` "payloads": [`, ` {`, etc. — so the success path hinged entirely on the fallback joining `rawLines` and re-trying. Under load (daemon restarts racing the close-on-cancel goroutine, partial chunked reads when stdout closes mid-flight) the line scanner could see truncated input that never reassembled into valid JSON, surfacing "openclaw returned no parseable output" against runs where the agent had in fact completed the work and posted comments. Roughly 30–40% of recent runs in v0.2.27 logs hit this path; multica still wrote a `task_failed` inbox row for each one even though the underlying issue had moved to `in_review` or `done`. The fix: - processOutput now reads the full stdout buffer with `io.ReadAll` first. - A new `parseWholeBufferOpenclawResult` helper attempts a single `json.Unmarshal` against the entire buffer (after trimming, and after optionally stripping leading non-JSON log lines). When it matches, we build the result and return — the line scanner never runs. - If the whole-buffer parse fails, we fall through to the existing NDJSON line-by-line scanner. This preserves streaming-event support (kept for forward compatibility and other backends) without leaving openclaw's dominant pretty-printed shape at the mercy of timing. - The failure path now emits a `(got N bytes; preview: ...)` suffix on the canonical "no parseable output" error so future debugging isn't blind. The exact canonical phrase is preserved for empty buffers so existing dashboards / log-grep tooling keep matching. Tests: - TestOpenclawProcessOutputWholeBufferPrettyJSON: feeds a hand-crafted multi-line indented blob (multiple payloads, nested agentMeta, usage map) and asserts every field round-trips through the whole-buffer fast path. - TestOpenclawProcessOutputDeeplyIndentedFixture: re-runs the recorded openclaw 2026.5.5 stdout fixture (1070 lines) directly through parseWholeBufferOpenclawResult, asserting the bug-shape parses cleanly on the first attempt without falling through to NDJSON scanning. - TestOpenclawProcessOutputEmptyBufferErrorIncludesByteCount: tightens the empty-buffer failure path, asserts the canonical phrase survives so observability tooling keeps working. All existing tests in the openclaw + buildOpenclawArgs suites stay green (streaming NDJSON event tests, lifecycle tests, structured-error tests, usage-field-variant tests). The two pre-existing flaky timeout-tight codex tests (TestCodexExecuteSemanticInactivityAllowsContinuous*) fail on both this branch and onc87d7676baseline; they are unrelated and out of scope here. Co-authored-by: multica-agent <github@multica.ai> * fix(openclaw): drop dead preview branch, document streaming regression Rebase + review-fix follow-up on top of f27df2d9b. processOutput's preview branch was unreachable: openclawNoParseableOutputError was only called from the `!gotEvents && trimmed == ""` path, which by construction means the entire scanned buffer collapsed to whitespace, so the `(got N bytes; preview: ...)` formatter could never fire on a non-empty buffer. Replace the helper with a single canonical-string constant (callsite is now inline) and update the test name to match what it actually asserts (the canonical empty-buffer error string is preserved for external log-grep / dashboard consumers). Also document on processOutput that the line-scanner path is no longer truly streaming after the io.ReadAll switch: events accumulate until stdout closes. OpenClaw 2026.5.x does not emit streaming events so this regression is invisible today, but flag it for the next backend that might. Misc: switch the scanner's input source from `strings.NewReader(string(buf))` to `bytes.NewReader(buf)` to drop one unnecessary byte/string round-trip. MUL-1908 Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> Co-authored-by: J (Multica agent) <j@multica.local>