Skip to content

feat(adapter): surface the publish warnings the server returns - #147

Merged
guangyu-reflexio merged 1 commit into
mainfrom
feat/surface-publish-warnings
Jul 28, 2026
Merged

feat(adapter): surface the publish warnings the server returns#147
guangyu-reflexio merged 1 commit into
mainfrom
feat/surface-publish-warnings

Conversation

@guangyu-reflexio

Copy link
Copy Markdown
Contributor

Follow-up to ReflexioAI/reflexio#387, which added a warnings channel to publish responses and wired the openclaw adapter to log it. This adapter ignored the response entirely, so half the first-party install base still got a 200 and no signal.

Why it matters more here

The pinned-request_id path posts the payload straight through _make_request, bypassing InteractionData construction — so unknown keys genuinely reach the server instead of being stripped client-side first. On that path the server's warnings are the only signal. The defect being caught: 50 interactions keyed Content instead of content stored as 50 rows with content = '', returning 200.

The structural bit

publish had three success returns nested inside one try whose except returns PublishResult(False). publish_unpublished advances the buffer watermark only on a truthy result — so putting the warning read inside that try would mean a raise reports an accepted publish as failed, and the same batch is re-sent on every later hook. Duplicates forever, caused by the code meant to improve observability.

So the body moved to _attempt_publish, which returns (result, response), and the read happens in publish after it returns — outside that except and every nested one. A second guard covers the logging handler itself, not just the extraction.

_publish_warnings is deliberately documented as defensive, not total: getattr absorbs only AttributeError, a mapping can override get, and str runs a caller-supplied __str__. That claim was wrong in the openclaw version and review caught it; not repeating it here. Both response shapes are handled — parsed JSON dict on the raw path, response object on the client path.

Testing

Covers both publish paths, the quiet case, and seven response shapes. Four of those are absorbed by the isinstance guard; three actually escape — a warnings property raising RuntimeError, a dict subclass overriding get, and an item whose __str__ raises. Removing the guard fails exactly those three and nothing else, which is the check the openclaw tests originally missed: they asserted totality while only exercising shapes that could never violate it.

One test drives the real Adapter through publish_unpublished with a hostile response and asserts published_up_to is still written — the invariant itself rather than a proxy for it. It also fails with the guard removed.

71 passed across the two touched files.

Pre-existing, untouched

9 suite failures (test_install_scripts, test_opencode_support) reproduce on a clean main with this branch stashed. ruff-format drift spans 15 files repo-wide; CI runs pytest only. Reformatting two of fifteen inside a focused PR would be arbitrary noise.

The openclaw plugin logs reflexio's `warnings` — fields it could not bind,
interactions skipped as empty. This adapter ignored the response entirely,
so half the first-party install base still got a 200 and no signal for the
exact defect the channel was built for: 50 mis-keyed interactions stored as
50 empty rows.

It matters more here than in openclaw. The pinned-request_id path posts the
payload straight through `_make_request`, so unknown keys really do reach
the server rather than being stripped client-side first — the server's
warnings are the only signal that path has.

`publish` is split so the diagnostic read sits outside `_attempt_publish`
and every try inside it. `publish_unpublished` advances the buffer watermark
only on a truthy result, so a raise while reading warnings would report an
ACCEPTED publish as failed and re-send the same batch on every later hook —
duplicates forever, caused by the observability code. The three success
paths now return their response alongside the result so the read has
something to work with after the try has closed.

`_publish_warnings` is defensive but not total, and says so: `getattr`
absorbs only AttributeError, a mapping can override `get`, and `str` runs a
caller's `__str__`. The caller's guard covers those and the logging handler.
Both shapes are handled — the raw path returns parsed JSON, the client path
a response object.

Tests cover both publish paths and the three shapes that actually escape the
isinstance guard; removing the guard fails exactly those three. One test
drives the real Adapter through `publish_unpublished` and asserts the
watermark still advances, which is the invariant rather than a proxy for it.

Pre-existing and untouched: 9 suite failures (install-scripts, opencode-dist)
that also fail on a clean main, and ruff-format drift across 15 files.
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 2 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 52876305-4bf8-48ba-a9e8-1981e24c7011

📥 Commits

Reviewing files that changed from the base of the PR and between fdbefb2 and d16c22c.

📒 Files selected for processing (3)
  • plugin/src/claude_smart/reflexio_adapter.py
  • tests/test_adapter.py
  • tests/test_publish.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/surface-publish-warnings

Comment @coderabbitai help to get the list of available commands.

@guangyu-reflexio
guangyu-reflexio merged commit f73a8ef into main Jul 28, 2026
9 checks passed
@guangyu-reflexio
guangyu-reflexio deleted the feat/surface-publish-warnings branch July 28, 2026 05:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant