[Feature] Path based LiveObjects Implementation#1214
Draft
sacOO7 wants to merge 48 commits into
Draft
Conversation
sacOO7
commented
Jun 11, 2026
Collaborator
- PathObject documentation -> https://ably.com/docs/liveobjects/concepts/path-object
- Public API doc -> PathObject based LiveObjects API
- PathObject spec -> https://sdk.ably.com/builds/ably/specification/main/objects-features/
- PathObjectSubscriptionOptions: validate depth fail-fast per RTPO19c1a,
throwing AblyException with ErrorInfo(400, 40003) when depth <= 0.
Depth is now a primitive int; the "no depth / infinite depth" state is
expressed via a new no-arg constructor (mirrors ably-js `{}` options),
so no null handling is needed
- LiveMapValue: defensively copy binary payloads on creation and access,
making the RTLMV3d immutability guarantee real for byte[] values
- ObjectData#getBytes: document that the returned array is the underlying
message payload and must be treated as read-only
- JsonObjectPathObject/JsonArrayPathObject: reword "primitive resolution"
javadoc for clarity
- LiveMapPathObject#at: fix javadoc equivalence example to compile
(get() returns base PathObject, so chain via asLiveMap())
…al-public-api [LiveObjects] Implement path-based LiveObjects public API
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…ccessor Introduce the public, strongly-typed, path-based LiveObjects entry point on a realtime channel, accessed via `channel.object`. - RealtimeObject: exposes `get()` returning the root LiveMapPathObject, and extends ObjectStateChange to subscribe to objects sync-state events (on/off/offAll). - ObjectStateChange / ObjectStateEvent: the SYNCING/SYNCED sync-state subscription API surface. - ChannelBase.object: a public field providing `channel.object` access. When the LiveObjects plugin is not installed, the field is assigned RealtimeObject.Unavailable - a null-object guard whose methods fail fast with a clear plugin-missing error (statusCode 400, code 40019) instead of an NPE, keeping the `channel.object.<method>()` syntax consistent in both cases. The plugin-present branch is intentionally left as a TODO until the LiveObjects plugin exposes the new io.ably.lib.object.RealtimeObject type (getInstance currently returns the legacy io.ably.lib.objects.RealtimeObjects). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ture - Replaced ObjectsSubscription import with Subscription as per requirement
…tion-for-path-based-interfaces [AIT-928] feat(liveobjects): add path-based RealtimeObject and channel.object accessor
…mplementation' into chore/liveobjects-add-basic-implementation
… relevant error codes under `Errors.kt`
- Marked PathObject#getValue as nullable when value doesn't exist at given path
- Implemented JsonSerializer annotation for better json handling
Point the JSON and MsgPack serializers in io.ably.lib.object.serialization
at the new WireObjectMessage wire model instead of the legacy
io.ably.lib.objects.ObjectMessage, so the new `object` package has no
dependency on the legacy `objects` package.
- DefaultSerialization: implement the new ObjectSerializer interface and
(de)serialize WireObjectMessage arrays (reflectively loaded via
ObjectSerializer.Holder).
- Json/MsgpackSerialization: bind the Wire* types; replace legacy
objectError with the object package's objectStateError (same 500/92000).
- WireObjectMessage: restore the gson annotations required for wire-format
fidelity - @SerializedName("object") on objectState and
@JsonAdapter(WireObjectDataJsonSerializer) on WireObjectData.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes checkstyle AvoidStarImport violation on com.google.gson.*. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Added basic helper unit/integration tests to `io.ably.lib.object`
…g `RecoveryKeyContext#channelSerials` as a treemap
…-to-liveobjects [AIT-1037] Refactored `object` package name to `liveobjects`
…veobjects-implementation
Move the human-readable UTS guide and its self-contained HTML rendering into the uts/ module as README.md and index.html. Both cover the UTS concept, the three test tiers, the spec docs, the uts/ module layout, mock/proxy infrastructure, the two example tests, deviations, and appendices. Spec-doc references link to GitHub; paths are fully qualified; the two artifacts are kept in sync. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ier Gradle tasks Reorganise the uts module under a domain-rooted io.ably.lib.uts package that cleanly separates infrastructure from tests, and unit from integration: infra/ shared awaits (Utils.kt) infra/unit/ mock transports + ConnectionDetails builder infra/integration/ SandboxApp infra/integration/proxy/ ProxyManager, ProxySession unit/realtime/ ConnectionRecoveryTest (mocked) integration/proxy/realtime/ AuthReauthTest (sandbox + proxy) - The ConnectionDetails test builder no longer sits in io.ably.lib.types, so it obtains the package-private constructor reflectively (as liveobjects/TestUtils.kt does). - Add runUtsUnitTests / runUtsIntegrationTests Gradle tasks (filtered by package), mirroring runLiveObjectsUnitTests / runLiveObjectsIntegrationTests. - check.yml now runs :uts:runUtsUnitTests; integration-test.yml gains a check-uts job running :uts:runUtsIntegrationTests. - Bring uts/README.md and uts/index.html in sync with the new structure.
Reflect the new test-source structure in the UTS guide, website, and the uts-to-kotlin skill: - Add the direct-sandbox (`integration/standard/<module>/`) tier alongside the existing unit and proxy tiers, and document that every tier is now organised by module (`realtime`, `liveobjects`, …). - Update §2 tier table, §4.2 directory tree + mental model, §7.3 SandboxApp (shared by both integration kinds), and §12 run commands in README.md, and mirror all of it in index.html (tags verified balanced, sections intact). - Generalise the skill's spec→test path mapping to `<module>`, add a direct-sandbox row, and split integration specs into fault-injecting (proxy) vs happy-path (direct sandbox) flows. - Correct stale "both tiers" wording now that there are three tiers.
…ckage mapping Rework the uts-to-kotlin skill to translate a whole UTS module at once instead of a single spec file: - Take a UTS module directory (e.g. .../specification/uts/objects) and validate it sits directly under uts/ with a standard tier structure. - Resolve the target ably-java package via uts-package-mapping.json (a new config file alongside the skill): a shared `testRoot` parent plus a `packages` table mapping each source module to its per-tier output dir (so objects -> liveobjects is explicit). Offer to create a mapping when one is missing. - Let the user pick a tier (unit / integration / proxy) and then translate all specs or a selected subset, looping each through the existing per-spec translation steps. Phase 1 (selection: Steps A-D) is new; Phase 2 (per-spec translation: Steps 1-7) keeps the existing rules, with Step 1/2 adjusted to consume the looped spec and the pre-resolved target.
…aluate mode Make the skill's selection phase deterministic and add an explicit translate-vs-evaluate choice: - Add scripts/resolve_uts.py — a bundled resolver that validates the module directory, reads uts-package-mapping.json, and emits JSON with, per tier, the target dir, Kotlin package, and the candidate specs with derived class names. This replaces the per-run hand-work (regex validation, path joins, snake_case->PascalCase) that the model previously improvised, so Phase 1 is byte-for-byte deterministic. Exclusions are checked relative to the tier base (robust to the checkout location), and --create guards the target name. - Rewrite Phase 1 (Steps A-E) around the resolver: resolve, confirm/create mapping, choose tier, choose specs, choose translate-only vs evaluate. - Gate Step 6 (run/fix) behind evaluate mode per writing-derived-tests.md's Translation (always) vs Evaluation (only when an implementation exists) split; translate-only stops after compile + review. - Make the reference fetch mandatory (WebFetch added to allowed-tools). - Fix the file template to use the resolver's package/className (no hardcoded realtime, no double Test suffix) and the spec's full @uts id; correct stale uts/test/... proxy doc paths.
…rence The objects UTS specs are written in ably-js-style pseudocode, but ably-java is a typed SDK (RTTS1-10 partition). Add references/objects-mapping.md mapping each ably-js symbol to its ably-java equivalent: entry point, async (CompletableFuture/await), the typed PathObject/Instance hierarchies and as* casts, the LiveMapValue write union, creation value types, subscriptions, sync-state events, ValueType, message/operation getters, error codes, path dot-escaping, and the internal-graph caveat for unit specs. Wire it in deterministically: each module declares its translation reference via a `notes` field in uts-package-mapping.json; resolve_uts.py resolves it to `translationNotes` (absolute path, or null), and SKILL.md makes it required reading before Phase 2 when present.
…ap them Translate objects/helpers/standard_test_pool.md into ably-java test helpers in uts/.../unit/liveobjects/helpers.kt: the standard object pool, the protocol- and object-message builders (emitting the integer-coded wire JSON the SDK's Gson expects), setupSyncedChannel/NoAck over the existing MockWebSocket, and buildPublicObjectMessage — which reaches the internal PAOM3/PAOOP3 construction (WireObjectMessage -> DefaultObjectMessage) by reflection, so it runs today even though the rest of :liveobjects is unimplemented. Add testRuntimeOnly(:liveobjects) so that reflection resolves while keeping the compile classpath decoupled. Wire the mapping reference at it: objects-mapping.md gains a "Unit-test helpers" section mapping each spec helper to its Kotlin name, and §11/§13 are corrected to note public_object_message.md is translatable via buildPublicObjectMessage rather than internal-only.
…efactor - Add ChannelHistoryTest (RTL10d) and TokenRequestTest (RSA9/RSA9a/RSA9g) direct-sandbox integration tests, parameterised over json/msgpack. - Add junit-jupiter-params dependency (@ParameterizedTest / @valuesource). - Consolidate the sandbox host into a single SandboxApp.sandboxHost constant (removed from ProxyManager; ProxySession defaults both hosts to it). - Sync uts/README.md, uts/index.html, and the uts-to-kotlin SKILL.md for the direct-sandbox tier: new ChannelHistoryTest walkthrough, integration-tests umbrella section, section renumber. - Rename unit liveobjects helpers.kt -> Helpers.kt (case-only).
- objects-mapping.md: correct helper filename casing helpers.kt -> Helpers.kt (broken source-nav on case-sensitive filesystems); tag bare code fences as `text` to clear markdownlint MD040. - README.md: tag bare fences as `text`; correct the msgpack rationale — ably-java does implement msgpack (useBinaryProtocol = true by default), the real reason proxy tests force JSON is that the proxy only handles text WebSocket frames. - index.html: mirror the README msgpack correction. - SKILL.md: label the unit-tier test scaffold as such and point integration/proxy flows at the SandboxApp/ProxySession section. - resolve_uts.py: validate the module path via Path.parts instead of a forward-slash-only regex, so Windows paths are accepted. - ConnectionRecoveryTest: use CopyOnWriteArrayList for the query-param capture list (written on the SDK transport thread, read on the coroutine dispatcher) to remove a visibility race / flake risk. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Translation review (SKILL.md Step 7) relied on eyeballing the spec and the generated Kotlin side-by-side to confirm every test case, setup step, operation and assertion was carried over — exactly the mechanical comparison the model does inconsistently. Add a bundled extractor so the review reconciles a concrete ledger instead. scripts/audit_translation.py (regex-only, no semantic judgement, deterministic): - idCoverage: spec `**Test ID**` set vs Kotlin `@UTS` tags -> missing / orphan. - perTest[].sections: every non-comment pseudo-block line, grouped by section and tagged assert / await / step, so setup + operations + assertions are all enumerated; assertionShortfall flags likely-dropped assertions. - Robustness: never crashes — tolerant decoding plus a top-level guard always emit one JSON object. Exit 0 clean / 2 missing-or-orphan / 64 couldn't-run, via a fail() helper matching resolve_uts.py's error shape. SKILL.md Step 7 now runs the audit first and reconciles its output line by line (coverage + completeness), keeping setup-fidelity and deviation-honesty as the semantic checks; paths reference the resolver's specs[].file / targetDir.
[AIT-1081] Update liveobjects `uts-to-kotlin` skill to generate tests
read_text()/write_text() default to the locale encoding on Windows (often cp1252, not UTF-8) and apply newline translation, which would both mis-handle non-ASCII content and rewrite the git-tracked uts-package-mapping.json with CRLF. Pin the read to utf-8 and write via write_bytes (binary mode does zero newline translation on any OS or Python version, keeping the file LF — unlike write_text(newline=...), which needs Python 3.10+). Aligns with the utf-8 reads already in audit_translation.py.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.