Add UTS tests for LiveObjects path-based API - #2219
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughNew LiveObjects runtime behavior and UTS coverage cover protocol variants, object APIs, CRDT state, synchronization, lifecycle flows, proxy faults, value types, tombstones, polling utilities, and adapted UTS deviations. ChangesLiveObjects behavior and test coverage
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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 |
Translate portable UTS test specs from specification/uts/objects/ into working ably-js TypeScript tests covering PathObject, Instance, batch operations, LiveCounter/LiveMap CRDT internals, ObjectsPool sync state machine, RealtimeObject lifecycle, value types, and subscriptions. 19 test files: 14 unit (270 tests), 4 integration (15 tests), 1 proxy fault injection (5 tests). 1 test pending (LiveMap#clear() not yet implemented). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove client-side port allocation (TOCTOU race) and let the proxy auto-assign free ports via OS. Read the assigned port from the session creation response. Update binary to v0.2.0 with new asset naming. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds parentReferences, PublicAPI::ObjectMessage, RTO24/25/26 tests; updates objectMessage/subscription assertions; removes batch API tests. 308 passing, 4 pending deviations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The webpack build type-checks the whole tsconfig program (which
includes test/**/*), so type errors in the UTS LiveObjects tests
failed `npm run build` and therefore `npm ci`.
- Add siteCode and objectsGCGracePeriod to the mock CONNECTED
message's connectionDetails defaults, so tests can set these
real protocol fields without widening a mismatched type.
- Pass the LiveObjects plugin module namespace to `plugins`
instead of the LiveObjects const export, matching the internal
ModularPlugins type (typeof import('plugins/liveobjects')) and
the existing JS tests. Runtime behaviour is unchanged.
…son+msgpack variants - objects_faults: restructure the RTO20e proxy test to the corrected spec sequence (ably/specification#501) — force SYNCING via injected ATTACHED with HAS_OBJECTS, mutate while SYNCING, wait for the publish ACK in the proxy log, then inject channel ERROR and assert the pending operation fails with 92008 / statusCode 400 / cause 90000 (RTO20e1). Removes the 90001 DEVIATION, which documented the spec error now fixed at source. - objects_gc: replace the deviation comments with the real spec references — undefined reads are RTLM5d2h-compliant (spec says undefined/null), and the timer-based GC sweep is covered at the unit tier (RTO10/RTO10b1/RTLM19 unit tests), so only observable tombstone semantics belong here. - objects_sync: align the RTO4 subscribe-only test title with the corrected spec wording and drop its DEVIATION comment. - Add describeWithProtocols helper and run objects_lifecycle, objects_sync and objects_gc once per protocol variant (json, msgpack) as their specs require, without any command-line or environment parameter. All suites verified against the nonprod sandbox: objects_faults 5/5, lifecycle/sync/gc 24/24 across both protocol variants.
[AIT-1130] fix(uts): align objects integration tests with corrected specs and run both protocol variants
…aining nondeterminism - add pollUntilSuccess to the integration sandbox helpers: any error from the polled read means "keep polling" (logged as it happens); on timeout the most recent error is thrown so failures stay diagnosable - poll all eventually-consistent message-store reads (getMessage, getMessageVersions, annotations.get) through pollUntilSuccess in the REST and realtime mutable_messages tests, replacing the fixed 2s propagation sleep and fail-fast polls that raced store visibility - use pollUntilSuccess for the objects_faults value polls, where value() raises by design while the channel is transiently detached mid-recovery; objects sync/lifecycle/gc value polls stay on plain pollUntil (an error there is a real bug) and drop redundant default interval/timeout options - RTO10/RTO10b1 GC unit tests: backdate the tombstone serialTimestamp instead of stubbing Date.now; RTO10b1 now genuinely discriminates the server-provided 5s grace period from the 24h default - align the RSC15f cached-fallback tick with the spec (150ms) and document the sanctioned RTLM19 Date.now stub in live_map's deviation header
…helper The helper's only call site was LiveMap.overrideWithObjectState, which is invoked exclusively from the OBJECT_SYNC flow (RTO5c1a1, RTO5c1b1a/b). Every such path runs _rebuildAllParentReferences (RTO5c10) before any subscriber callbacks fire, unconditionally clearing and re-deriving all parentReferences from the final post-sync pool state — so the helper's intermediate updates were never observable. parentReferences has a single reader (getFullPaths, used by _notifyPathSubscriptions), which during sync only runs after the rebuild. In the tombstone branch the refs were already removed by LiveMap.clearData (RTLO4e5) before the helper ran. Also scope-tightens previousDataRef into the else branch of overrideWithObjectState, its only consumer. Supersedes #2234, which is applied here instead.
…s with stack The truthiness check inherited from pollUntil is deliberate, matching the spec's poll_until_success convention (a bare boolean condition polls until true), so a falsy return means "keep polling" by design - now stated in the doc comment along with what to do when the natural result could be falsy. Retry warnings pass the error object to console.warn instead of interpolating it, so stacks are preserved.
[AIT-1141] fix(liveobjects): spec-compliance fixes from the objects deviations audit (nonce length, inbound op handling, counter noop guards)
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
test/uts/objects/unit/live_map.test.ts (1)
264-290: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winKeep RTLM9b coverage on the entry-level comparison.
Because
ObjectMessage.serialis empty,canApplyOperationrejects the message before RTLM9b runs. This assertion therefore covers the object-level RTLO4a3/RTLM15b rejection, not the “both entry serials empty” rule. Use a valid message serial with empty entry serials, or rename this test and add a separate RTLM9b case.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/uts/objects/unit/live_map.test.ts` around lines 264 - 290, Update the RTLM9b test to use a valid non-empty ObjectMessage.serial while keeping the map entry’s timeserial empty, so applyOperation reaches and verifies the entry-level empty-serial comparison. Preserve the rejection and unchanged-data assertions; if object-level empty-serial behavior is still needed, cover it in a separately named test.
🧹 Nitpick comments (4)
test/uts/realtime/integration/sandbox.ts (1)
224-251: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse one
pollUntilSuccessimplementation.The same implementation remains duplicated in
test/uts/realtime/integration/mutable_messages.test.tsLines 231-250 andtest/uts/objects/integration/proxy/objects_faults.test.tsLines 231-250. Remove those local copies and import this shared helper so timeout and error-propagation behavior cannot drift.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/uts/realtime/integration/sandbox.ts` around lines 224 - 251, The shared pollUntilSuccess helper in sandbox.ts should be the sole implementation. Remove the duplicate local definitions from mutable_messages.test.ts and objects_faults.test.ts, then import and use the shared pollUntilSuccess in both tests while preserving their existing call sites and options.src/plugins/liveobjects/livemap.ts (1)
707-716: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate of
_logNoPayloadWarninginlivecounter.ts— noted for the consolidation below.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugins/liveobjects/livemap.ts` around lines 707 - 716, Consolidate the duplicate `_logNoPayloadWarning` implementation shared by `LiveMap` and `LiveCounter`; remove the redundant `liveobjects/livemap.ts` version and reuse the existing shared or canonical implementation while preserving the warning message and discard behavior.src/plugins/liveobjects/livecounter.ts (1)
265-301: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCorrect noop semantics for missing create count;
_logNoPayloadWarningduplicates theLiveMapversion.The
_mergeInitialDataFromCreateOperation/_applyCounterIncnoop handling matches the cited RTLC16/RTLC9 UTS expectations. Separately,_logNoPayloadWarning(Lines 292-301) is byte-for-byte the same helper added inlivemap.tsaside from the class name in the log message — a good candidate to hoist onto theLiveObjectbase class. Flagging for the consolidation note below.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugins/liveobjects/livecounter.ts` around lines 265 - 301, Consolidate the duplicate _logNoPayloadWarning helper by moving its shared warning behavior from LiveCounter and LiveMap into the LiveObject base class. Update both subclasses to use the inherited method, preserving the existing log action, severity, message, and malformed-payload handling.src/plugins/liveobjects/liveobject.ts (1)
246-262: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider centralizing the "well-formed but stale" skip-log in
_canApplyOperation.Both
LiveCounter.applyOperationandLiveMap.applyOperationduplicate anif (opSerial && opSiteCode) { ...LOG_MICRO... }block right after this call, purely to avoid re-logging when this method already logged the malformed-serial warning. Consider having_canApplyOperationaccept anaction/description argument and emit both the malformed-serial and stale-serial logs itself, removing the need for subclasses to duplicate the guard.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugins/liveobjects/liveobject.ts` around lines 246 - 262, Extend _canApplyOperation to accept the operation action or description and centralize logging for both malformed and stale serials, including the existing LOG_MICRO stale-operation message. Update LiveCounter.applyOperation and LiveMap.applyOperation to pass the action context and remove their duplicated opSerial/opSiteCode logging guards, while preserving the existing skip decisions and log content.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/uts/deviations.md`:
- Around line 231-237: Update test/uts/deviations.md lines 231-237 to document
that increment(null) is runtime-reachable in JavaScript and follows the amount
?? 1 default, behaving like an omitted argument rather than being unreachable;
update test/uts/objects/unit/live_counter_api.test.ts lines 124-126 to add an
explicit increment(null) case asserting the counter increases by 1.
---
Outside diff comments:
In `@test/uts/objects/unit/live_map.test.ts`:
- Around line 264-290: Update the RTLM9b test to use a valid non-empty
ObjectMessage.serial while keeping the map entry’s timeserial empty, so
applyOperation reaches and verifies the entry-level empty-serial comparison.
Preserve the rejection and unchanged-data assertions; if object-level
empty-serial behavior is still needed, cover it in a separately named test.
---
Nitpick comments:
In `@src/plugins/liveobjects/livecounter.ts`:
- Around line 265-301: Consolidate the duplicate _logNoPayloadWarning helper by
moving its shared warning behavior from LiveCounter and LiveMap into the
LiveObject base class. Update both subclasses to use the inherited method,
preserving the existing log action, severity, message, and malformed-payload
handling.
In `@src/plugins/liveobjects/livemap.ts`:
- Around line 707-716: Consolidate the duplicate `_logNoPayloadWarning`
implementation shared by `LiveMap` and `LiveCounter`; remove the redundant
`liveobjects/livemap.ts` version and reuse the existing shared or canonical
implementation while preserving the warning message and discard behavior.
In `@src/plugins/liveobjects/liveobject.ts`:
- Around line 246-262: Extend _canApplyOperation to accept the operation action
or description and centralize logging for both malformed and stale serials,
including the existing LOG_MICRO stale-operation message. Update
LiveCounter.applyOperation and LiveMap.applyOperation to pass the action context
and remove their duplicated opSerial/opSiteCode logging guards, while preserving
the existing skip decisions and log content.
In `@test/uts/realtime/integration/sandbox.ts`:
- Around line 224-251: The shared pollUntilSuccess helper in sandbox.ts should
be the sole implementation. Remove the duplicate local definitions from
mutable_messages.test.ts and objects_faults.test.ts, then import and use the
shared pollUntilSuccess in both tests while preserving their existing call sites
and options.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: aa2386e6-4395-42a0-b78b-3a7add642e3b
📒 Files selected for processing (33)
src/plugins/liveobjects/livecounter.tssrc/plugins/liveobjects/livecountervaluetype.tssrc/plugins/liveobjects/livemap.tssrc/plugins/liveobjects/livemapvaluetype.tssrc/plugins/liveobjects/liveobject.tssrc/plugins/liveobjects/objectid.tssrc/plugins/liveobjects/restobject.tstest/browser/modular.test.jstest/realtime/message.test.jstest/uts/deviations.mdtest/uts/helpers.tstest/uts/objects/helpers/standard_test_pool.tstest/uts/objects/integration/objects_gc.test.tstest/uts/objects/integration/objects_lifecycle.test.tstest/uts/objects/integration/objects_sync.test.tstest/uts/objects/integration/proxy/objects_faults.test.tstest/uts/objects/unit/instance.test.tstest/uts/objects/unit/live_counter.test.tstest/uts/objects/unit/live_counter_api.test.tstest/uts/objects/unit/live_map.test.tstest/uts/objects/unit/live_map_api.test.tstest/uts/objects/unit/live_object_subscribe.test.tstest/uts/objects/unit/objects_pool.test.tstest/uts/objects/unit/path_object_subscribe.test.tstest/uts/objects/unit/public_object_message.test.tstest/uts/objects/unit/realtime_object.test.tstest/uts/objects/unit/value_types.test.tstest/uts/realtime/integration/mutable_messages.test.tstest/uts/realtime/integration/presence/presence_lifecycle.test.tstest/uts/realtime/integration/sandbox.tstest/uts/rest/integration/mutable_messages.test.tstest/uts/rest/integration/sandbox.tstest/uts/rest/unit/fallback.test.ts
🚧 Files skipped from review as they are similar to previous changes (12)
- test/uts/helpers.ts
- test/uts/objects/integration/objects_sync.test.ts
- test/uts/objects/integration/objects_lifecycle.test.ts
- test/uts/objects/integration/objects_gc.test.ts
- test/uts/objects/unit/instance.test.ts
- test/uts/objects/integration/proxy/objects_faults.test.ts
- test/uts/objects/unit/live_map_api.test.ts
- test/uts/objects/unit/objects_pool.test.ts
- test/uts/objects/helpers/standard_test_pool.ts
- test/uts/objects/unit/live_object_subscribe.test.ts
- test/uts/objects/unit/realtime_object.test.ts
- test/uts/objects/unit/value_types.test.ts
There was a problem hiding this comment.
Pull request overview
This PR brings the LiveObjects path-based API up to parity with the portable UTS specs by adding a large new UTS test suite and making a set of targeted LiveObjects implementation changes (primitive PathObject.instance() support, sync safety fixes, more tolerant operation handling, and nonce generation updates) to satisfy those specs.
Changes:
- Added a comprehensive UTS Objects test suite (unit + integration + proxy fault injection) plus shared fixtures/helpers to standardize mock pools and protocol builders.
- Updated LiveObjects core logic to match UTS expectations (primitive-wrapping
instance(), root object retention during sync cleanup, parent-reference rebuild after sync, and skipping malformed operations instead of throwing). - Improved test harness reliability for eventual consistency and async ordering (new polling helpers; reduced fixed sleeps).
Reviewed changes
Copilot reviewed 48 out of 49 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| test/uts/rest/unit/fallback.test.ts | Adjusts fake-timer advancement to align with fallback retry timeout behavior. |
| test/uts/rest/integration/sandbox.ts | Re-exports pollUntilSuccess for REST integration tests. |
| test/uts/rest/integration/mutable_messages.test.ts | Uses pollUntilSuccess to avoid eventual-consistency flakes in message store reads. |
| test/uts/realtime/unit/presence/realtime_presence_reentry.test.ts | Replaces ad-hoc flush loops with pollUntil for deterministic waiting. |
| test/uts/realtime/unit/channels/channel_server_initiated_detach.test.ts | Simplifies “wait for reattach” logic using pollUntil. |
| test/uts/realtime/unit/channels/channel_properties.test.ts | Simplifies “wait for reattach” logic using pollUntil. |
| test/uts/realtime/unit/auth/connection_auth.test.ts | Replaces ad-hoc polling loop with pollUntil. |
| test/uts/realtime/unit/auth/auth_callback_errors.test.ts | Replaces ad-hoc polling loops with pollUntil. |
| test/uts/realtime/integration/sandbox.ts | Adds pollUntilSuccess helper and exports it for reuse. |
| test/uts/realtime/integration/presence/presence_lifecycle.test.ts | Makes presence counting robust by tracking unique clientIds across enter/present. |
| test/uts/realtime/integration/mutable_messages.test.ts | Replaces fixed sleeps with polling for eventual consistency across updates/versions. |
| test/uts/realtime/integration/helpers/proxy.ts | Updates proxy binary versioning and session creation logic; adds versioned asset naming/checksums. |
| test/uts/objects/unit/value_types.test.ts | Adds UTS coverage for LiveCounter/LiveMap value types and their wire encoding. |
| test/uts/objects/unit/public_object_message.test.ts | Adds UTS coverage for public ObjectMessage/ObjectOperation user-facing mapping. |
| test/uts/objects/unit/path_object.test.ts | Adds UTS coverage for PathObject read APIs (pathing, resolution, compact/compactJson). |
| test/uts/objects/unit/path_object_subscribe.test.ts | Adds UTS coverage for PathObject subscribe semantics (depth, bubbling, path-following, quiescence). |
| test/uts/objects/unit/path_object_mutations.test.ts | Adds UTS coverage for PathObject write APIs and error behavior. |
| test/uts/objects/unit/parent_references.test.ts | Adds UTS coverage for parent reference tracking and getFullPaths traversal. |
| test/uts/objects/unit/object_id.test.ts | Adds UTS coverage for ObjectId determinism/format/base64url properties. |
| test/uts/objects/unit/live_object_subscribe.test.ts | Adds UTS coverage for Instance-based subscription behavior, noop suppression, tombstones. |
| test/uts/objects/unit/live_map_api.test.ts | Adds UTS coverage for LiveMap API behavior and wire message generation. |
| test/uts/objects/unit/live_counter_api.test.ts | Adds UTS coverage for LiveCounter API behavior and wire message generation. |
| test/uts/objects/unit/instance.test.ts | Adds UTS coverage for Instance wrapper behavior over live objects and primitives. |
| test/uts/objects/integration/proxy/objects_faults.test.ts | Adds proxy fault-injection integration tests for LiveObjects sync/lifecycle resilience. |
| test/uts/objects/integration/protocol_variants.ts | Adds a helper to run integration suites on json+msgpack variants. |
| test/uts/objects/integration/objects_sync.test.ts | Adds end-to-end sync integration tests (attach→sync→synced, reattach resync). |
| test/uts/objects/integration/objects_lifecycle.test.ts | Adds end-to-end lifecycle tests covering propagation across clients and REST provisioning. |
| test/uts/objects/integration/objects_gc.test.ts | Adds integration tests for tombstone semantics and recreatability. |
| test/uts/objects/helpers/standard_test_pool.ts | Introduces shared mock pool fixture + protocol/object message builders for Objects UTS. |
| test/uts/mock_websocket.ts | Extends default CONNECTED payload with siteCode and objectsGCGracePeriod. |
| test/uts/helpers.ts | Adds unit-tier pollUntil helper and exports it for use across UTS unit tests. |
| test/uts/deviations.md | Documents adapted/omitted UTS cases where JavaScript semantics differ (and other deviations). |
| test/realtime/message.test.js | Fixes a race by waiting for filtered-channel deliveries before asserting. |
| test/realtime/liveobjects.test.js | Updates expectation to match new primitive-wrapping PathObject.instance() behavior. |
| test/browser/modular.test.js | Makes encrypted history channel name unique per client-class variant to avoid cross-variant interference. |
| src/plugins/liveobjects/restobject.ts | Switches RestObject ID nonce generation to ObjectId.generateNonce. |
| src/plugins/liveobjects/pathobject.ts | Makes PathObject.instance() wrap primitives; tightens batch() to require LiveObject target. |
| src/plugins/liveobjects/objectspool.ts | Prevents sync cleanup from removing the root object. |
| src/plugins/liveobjects/objectid.ts | Adds async nonce generation helper (generateNonce) with required entropy. |
| src/plugins/liveobjects/liveobject.ts | Makes malformed serial/siteCode operations non-fatal (log + skip) and updates merge return type. |
| src/plugins/liveobjects/livemapvaluetype.ts | Uses ObjectId.generateNonce for map create-with-objectId operations. |
| src/plugins/liveobjects/livemap.ts | Skips malformed/missing payload ops via warnings; avoids throwing on unsupported actions. |
| src/plugins/liveobjects/livecountervaluetype.ts | Uses ObjectId.generateNonce for counter create-with-objectId operations. |
| src/plugins/liveobjects/livecounter.ts | Skips malformed/missing payload ops via warnings; treats missing increment number as noop. |
| liveobjects.d.ts | Updates PathObject.instance() documentation to reflect primitive-wrapping behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
A faulty OBJECT_DELETE operation (or a tombstoned object state) targeting
the root object would tombstone it: data cleared, all subscriptions
deregistered, and - tombstone being a terminal state - no re-sync could
ever resurrect it. After the GC grace period the sweep would then evict
root from the ObjectsPool entirely, violating RTO3b; root cannot be
re-created as a zero-value object ('root' is not a parseable object id),
so every subsequent objects operation on the channel fails unrecoverably.
Per RTLO4e the realtime system only tombstones orphaned or uninitialized
objects - root is by definition neither - so such a message indicates a
fault and is now rejected client-side, consistent with the existing
warn-and-skip handling for other invalid inbound operations.
- LiveObject.tombstone() rejects the root object with a warning and a
noop update (RTLO4e10); single choke point covering both the RTLO5
operation path and the RTLC6f/RTLM6f sync paths
- ObjectsPool._onGCInterval() never evicts root (RTO10c1b1, mirroring
the existing RTO5c2a guard in deleteExtraObjectIds); root's tombstoned
map entries still get RTLM19 entry-level GC
- overrideWithObjectState() in LiveMap/LiveCounter returns the tombstone
branch early since tombstone() can now return a noop update
Derived UTS tests: RTLO4e10 operation/sync path tests in live_map.test.ts,
channel-level RTO10c1b1 GC test in realtime_object.test.ts.
Spec twin: ably/specification#509
Raised in #2219 review discussion r3558197916
…ejection warning A rejected root tombstone indicates a faulty inbound message, and the site/serial is what identifies its origin; matches the adjacent RTLO4a3 warning style. Addresses review feedback on #2270.
[AIT-1204] fix(liveobjects): reject tombstoning of the root object (RTLO4e10, RTO10c1b1)
…or GC-evicted RTO10c1b as written compelled a compliant SDK to violate RTO3b: an OBJECT_DELETE (or a tombstoned ObjectState) targeting the root object would tombstone it - clearing its data, deregistering all listeners, and (since tombstone is a terminal state) making the channel's objects permanently unrecoverable - after which the GC sweep would evict root from the ObjectsPool entirely. - RTLO4e10: LiveObject.tombstone must reject the root object with a warning and a noop LiveObjectUpdate. Placed in the shared tombstone method so it covers both the RTLO5 operation path and the RTLC6f/RTLM6f sync paths. - RTO10c1b1: the GC sweep must never remove the root object from the ObjectsPool (mirrors RTO5c2a), as a safeguard for the RTO3b invariant. UTS test specs: - internal_live_map.md: new RTLO4e10 tests for the operation and sync paths; the three existing tombstone-mechanics tests (RTLO5, RTLM6f, RTLO4e9) are repointed from objectId "root" to a tombstoneable non-root map, reconciling the spec with the ids the ably-js derived tests already used. - realtime_object.md: new channel-level RTO10c1b1 test verifying root survives a GC sweep after a rogue OBJECT_DELETE and stays functional. Raised in ably/ably-js#2219 review discussion r3558197916.
…on missing proxy port Addresses the two open review threads on #2219: - RTLC12e1 deviation note reworded: increment(null) is runtime-reachable in JS; what is unreachable is the 40003 failure path, because the public API defines a nullish amount as equivalent to an omitted argument (amount ?? 1). New table case pins that contract by asserting the increment-by-1 default, so a later signature or coalescing change surfaces as a conscious decision. Also fixes the note's reference to a nonexistent test file name. - createProxySession now validates that the proxy control server returned a numeric port and throws with the raw response, instead of letting tests connect to localhost:undefined and fail later with a less diagnosable error.
Summary
specification/uts/objects/into 290 working ably-js TypeScript testsLiveMap#clear()not yet implemented in ably-js)Test plan
npx mocha --no-config --require tsx/cjs 'test/uts/objects/**/*.test.ts' --timeout 60000🤖 Generated with Claude Code
Summary by CodeRabbit
PathObject.instance()now returns anInstancewrapper for primitive values (not just live objects).AnyPathObject.instanceto reflect primitive support.