Skip to content

[AIT-1245] objects & uts: RTO27 channel-state data lifecycle, objects/unit test-spec corrections, and UTS authoring conventions - #512

Merged
sacOO7 merged 6 commits into
mainfrom
fix/objects-uts-cross-sdk-audit
Jul 31, 2026
Merged

[AIT-1245] objects & uts: RTO27 channel-state data lifecycle, objects/unit test-spec corrections, and UTS authoring conventions#512
sacOO7 merged 6 commits into
mainfrom
fix/objects-uts-cross-sdk-audit

Conversation

@sacOO7

@sacOO7 sacOO7 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

This started as small corrections to the objects objects/unit UTS specs — surfaced while translating them to ably-java and cross-checking against ably-js and ably-cocoa — and, through review, grew into three related pieces of work:

  1. A new normative spec point — RTO27 — specifying the objects-data lifecycle across channel state transitions, plus its UTS test.
  2. Corrections to the objects/unit UTS test specs — so several tests are portable and spec-faithful.
  3. New UTS authoring conventions — documenting identifier naming and how to record SDK deviations, so future derived-test work stays consistent.

Net effect vs main: 7 files. No change to existing normative behaviour beyond specifying RTO27 (which documents behaviour both reference SDKs already implement).


1. New normative point: RTO27 — objects-data lifecycle on channel state transitions

specifications/objects-features.md gains RTO27: on a channel transition to DETACHED/FAILED the client clears every pooled object's data without emitting update events (and clears the SyncObjectsPool); on SUSPENDED it retains the data. This closes a previously-unspecified gap that both reference SDKs already implement (ably-js RealtimeObject.actOnChannelState, ably-java DefaultRealtimeObject.handleStateChange). It sits in the channel-lifecycle cluster (after RTO5) and cross-references RTO20e1.

realtime_object.md gains the matching white-box UTS test (RTO27/channel-state-data-lifecycle-0): it drives the internal channel-state handler directly and inspects the ObjectsPool, because the behaviour is not reachable black-box (access on DETACHED throws per RTO25b, and SUSPENDED is a connection-level state).

Resolves review comment #3 (objects_pool detach-clear — "shouldn't SDKs be consistent?"): promoted from an SDK-internal note to a normative requirement.

2. objects/unit test-spec corrections

  • RTO18d — duplicate-listener test kept, with an OPTIONAL note (realtime_object.md). Asserts the RTE4 reference ("the same listener registered twice fires twice") and adds an OPTIONAL note sanctioning SDKs that de-duplicate listeners by instance (e.g. ably-java) to assert call_count == 1 or skip. Resolves review comment Move the features specification to this repository #1 — the shared test is kept, not deleted; SDK divergence is handled per-SDK.
  • RTO17/RTO18 — removed the "re-attach after detach" sync-event scenario (realtime_object.md), with a short guard note: it is redundant with "re-sync on new ATTACHED" (same onAttached → new-sync path, RTO4c) and not portably expressible (an unsolicited server DETACHED becomes SUSPENDED in some SDKs).
  • Quiescence barriers before positive read-after-send (path_object.md, RTPO13c5 / RTPO14): clarified these guard an inbound server OBJECT message (no apply-on-ack point to await), not a local publish. Resolves review comment Build textile to html #4.
  • null-means-omitted increment contract (internal_live_counter_api.md): assert the equivalence directly where null is indistinguishable from an omitted argument.
  • camelCase internal symbols in the RTO27 test (objectsPool, processChannelState), per the identifier-naming convention below.

Review comment #2 (op-path update-return portability): rather than documenting a divergence in the shared spec, ably-java was made to conform to the RTLC9g/RTLM7f return contract — its op path now returns the LiveObjectUpdate. The portability notes added earlier in this PR are therefore removed and net out of the diff; the general principle is captured in the authoring conventions below.

3. UTS authoring conventions

Formalised two previously-implicit conventions so derived-test work is consistent:

  • Identifier naming (writing-test-specs.md § Identifier Naming, summarised in README.md): spec-surface identifiers mirror the specification's own names (PascalCase types, camelCase fields/methods); behaviours the spec describes but doesn't name get a camelCase coinage; snake_case is reserved for test-harness constructs; wire/enum values keep protocol casing; and file names are unrelated to symbol casing.
  • Recording deviations (writing-derived-tests.md): the SDK-deviation choice is disposition-based (env-gated skip for a bug you expect to fix; adapted assertion — preferred — for a permanent/intentional divergence); a new "Idiomatic translation vs genuine deviations" subsection (idiomatic naming is not a deviation; public-behaviour differences are, at any tier; internal-API-shape differences are unit-tier-only adaptations that must preserve coverage); all four deviation-section headings kept present in every deviations.md; Status/Resolution entry fields; and the S-1…S-n shape-deviation vocabulary convention.

Cross-SDK validation

  • Divergences verified in source: ably-js (eventemitter.ts, realtimeobject.ts), ably-cocoa (ARTEventEmitter.m), ably-java (EventEmitter.java, DefaultRealtimeObject.kt).
  • ably-java: the affected objects/unit suite was regenerated and runs green; RTO27 has a matching test; the op path now returns the LiveObjectUpdate (conforms to RTLC9g/RTLM7f).
  • RTO27 behaviour matches both reference SDKs.

Non-goals

  • No change to existing normative behaviour beyond specifying RTO27.
  • Per-SDK deviations (e.g. ably-java's EventEmitter listener de-duplication) are recorded in each SDK's own deviations.md, not in the UTS.

Corrections and clarifications to the objects UTS unit test specs, surfaced while
translating them to ably-java and cross-checked against ably-js and ably-cocoa source.

- RTO17-RTO18 (realtime_object.md): remove the "re-attach after detach" sync-event
  scenario. It is redundant with "re-sync on new ATTACHED" (identical onAttached ->
  new-sync path) and not portably expressible - an unsolicited server DETACHED
  transitions the channel to SUSPENDED (not DETACHED) in some SDKs. ably-js already
  folds it into "re-sync on new ATTACHED".
- RTO18d (realtime_object.md, objects-features.md): remove the duplicate-listener UTS
  test and add an editor's note. Whether re-registering the same listener fires once or
  twice is a platform-idiomatic design choice (verified in source: ably-js and ably-cocoa
  append -> twice; ably-java de-duplicates by listener instance -> once), rooted in the
  general EventEmitter contract (RTE4). Not universally testable; each SDK pins its own.
- Op-path LiveObjectUpdate portability (internal_live_counter.md, internal_live_map.md):
  note that the update returned by applyOperation may be observed via the return value OR
  the emitted update event / resulting state, so event-based / typed SDKs can satisfy the
  same requirement.
- Read-after-send quiescence (path_object.md): add a poll_until barrier before the
  positive reads in RTPO13c5 / RTPO14 (inbound ops apply asynchronously).
- objects_pool.md: add a NOTE documenting the SDK-internal "DETACHED/FAILED clears objects
  data; SUSPENDED retains" behaviour and why it is deliberately not a UTS requirement
  (no normative point; unobservable via the public API), guarded by SDK-local tests.
- internal_live_counter_api.md: assert the null-means-omitted increment contract directly
  where null is not distinguishable from an omitted argument.
@github-actions
github-actions Bot temporarily deployed to staging/pull/512 July 29, 2026 09:33 Inactive

@paddybyers paddybyers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls review the comments.

(Also fyi @mschristensen @VeskeR )

Comment thread specifications/objects-features.md Outdated
Comment thread uts/objects/unit/internal_live_counter.md Outdated
Comment thread uts/objects/unit/objects_pool.md Outdated
Comment thread uts/objects/unit/path_object.md Outdated
@sacOO7
sacOO7 marked this pull request as draft July 29, 2026 10:24
@sacOO7

sacOO7 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

This PR is in progress locally, will update and mark it as ready for review

…c point + UTS test

Addresses the PR review on the objects UTS cross-SDK corrections.

- RTO18d (realtime_object.md, objects-features.md): restore the duplicate-listener UTS
  test (asserting the RTE4 "fires twice" contract) instead of removing it, and drop the
  editor's note from the spec. The UTS tests the spec's assertion; SDKs that de-duplicate
  listeners by instance (e.g. ably-java) may assert fires-once via an optional note on the
  test — the deviation stays with the SDK, not the UTS.

- RTO27 (objects-features.md, NEW): add an explicit normative point for the objects-data
  lifecycle on channel state transitions — on DETACHED/FAILED clear every pooled object's
  data without emitting update events (and clear the SyncObjectsPool); on SUSPENDED retain
  it. Placed in the channel-lifecycle cluster (after RTO5) and cross-referenced from RTO20e1.
  Closes a previously-unspecified gap both reference SDKs already implement (ably-js
  RealtimeObject.actOnChannelState, ably-java DefaultRealtimeObject.handleStateChange).

- RTO27 UTS test (realtime_object.md): add a white-box test that drives the internal
  channel-state handler directly and inspects the ObjectsPool (mirroring the
  internal_live_counter / internal_live_map tests), so the clear (not observable via the
  public API) and SUSPENDED (a connection-level state) are both testable at the unit tier.
  Reframe the objects_pool.md / realtime_object.md notes to reference RTO27.

- path_object.md: reword the RTPO13c5 / RTPO14 read-after-send quiescence barrier to clarify
  it guards an INBOUND server OBJECT message (no apply-on-ack point to await), not a local
  publish.
@github-actions
github-actions Bot temporarily deployed to staging/pull/512 July 29, 2026 13:00 Inactive
…ming conventions

Follow-up to the RTO27 work and the PR review.

- objects_pool.md: remove the DETACHED/FAILED detach-clear NOTE. It was a stopgap for a
  missing spec point; now that RTO27 is normative and has its own white-box test in
  realtime_object.md, the note is redundant (and was a RealtimeObject concern, not a bare
  ObjectsPool one).

- realtime_object.md: rename the RTO27 test's abstract white-box symbols to the UTS
  camelCase convention (objects_pool -> objectsPool, handle_channel_state ->
  processChannelState); drop the dangling "See objects_pool.md" pointer and the redundant
  RTO27 cross-reference from the no-re-attach-after-detach note, trimming that note to its
  two load-bearing reasons (redundant + not portably expressible); extend the header spec
  points to RTO22-RTO27 to match the file's contents.

- docs/writing-test-specs.md, README.md: add an "Identifier Naming" convention section
  (spec surface mirrors the specification's names — PascalCase types, camelCase
  fields/methods; behaviours the spec describes but doesn't name get a camelCase coinage;
  snake_case is reserved for test-harness constructs; wire/enum values keep protocol/spec
  casing). Validated against objects/realtime/rest UTS specs and the features.md/protocol.md
  parent specs.
@github-actions
github-actions Bot temporarily deployed to staging/pull/512 July 29, 2026 15:15 Inactive
…ation recording conventions

- internal_live_counter.md, internal_live_map.md: remove the "SDK portability note" blocks.
  ably-java's op path now returns the LiveObjectUpdate (RTLC9g/RTLM7f), so the boolean-return
  divergence they described no longer exists; the UTS simply asserts the return contract, and any
  SDK that diverges records it in its own deviations.md rather than in the shared spec. These notes
  were added earlier in this PR, so they net out of the final diff.

- docs/writing-derived-tests.md (§ Recording deviations, § Test patterns, decision tree): make the
  deviation-handling guidance explicit and internally consistent so it can be followed without
  ambiguity:
  - the SDK-deviation choice is disposition-based — env-gated skip for a bug you expect to fix,
    adapted assertion (preferred) for a permanent/intentional divergence with stable behaviour;
    §2c and the decision tree now point at both patterns instead of "adapt" unconditionally.
  - new "Idiomatic translation vs genuine deviations" subsection: idiomatic public-API naming is
    not a deviation (just translate it); public-behaviour differences are deviations at any tier;
    internal-API shape differences are unit-tier-only adaptations that must preserve coverage.
  - keep all four section headings present in every deviations.md (mark empty ones "(none)"); add
    Status/Resolution entry fields and the shape-deviation vocabulary convention.
  - fixes: "four advantages" count, and "Test impact" wording no longer assumes adaptation.
@github-actions
github-actions Bot temporarily deployed to staging/pull/512 July 30, 2026 09:18 Inactive
@sacOO7 sacOO7 changed the title objects: UTS test-spec corrections from cross-SDK (js/java/cocoa) audit objects & uts: RTO27 channel-state data lifecycle, objects/unit test-spec corrections, and UTS authoring conventions Jul 30, 2026
@sacOO7
sacOO7 marked this pull request as ready for review July 30, 2026 09:25
@sacOO7
sacOO7 requested review from paddybyers and ttypic July 30, 2026 09:25
…d wording

- realtime_object.md (RTO27): assert the nested pooled map map:profile@1000 independently —
  cleared on DETACHED/FAILED, retained on SUSPENDED — checked via the pool (not via root
  navigation) so a nested-object regression can't be hidden by the root clear. Addresses the
  CodeRabbit review on ably-js#2278; mirrored in the ably-java and ably-js derived tests.

- writing-derived-tests.md: drop the redundant "Also record…" lead-in so the deviation entry
  fields read as a single 1–7 list, and qualify the two non-core fields — Status "(for an SDK
  deviation)" and Resolution "(once resolved)" — so they don't read as mandatory on every entry
  (a UTS Spec Error / Mock Infra entry has no such disposition; Resolution is omitted until there
  is one).
@github-actions
github-actions Bot temporarily deployed to staging/pull/512 July 30, 2026 10:25 Inactive
sacOO7 added a commit to ably/ably-js that referenced this pull request Jul 30, 2026
… with @SPEC RTO27

- realtime_object.test.ts (RTO27): assert the nested pooled map map:profile@1000 independently —
  cleared on DETACHED/FAILED, retained on SUSPENDED — via the pool (size 3 -> 0 / stays 3), so a
  nested-object regression can't be hidden by the root clear. Addresses the CodeRabbit review;
  mirrors the spec change in ably/specification#512 and the ably-java derived test.
- realtimeobject.ts: annotate actOnChannelState with @SPEC RTO27 (and inline RTO27a/a1/a2/b) —
  comment-only; the behaviour was previously unannotated.
@sacOO7 sacOO7 changed the title objects & uts: RTO27 channel-state data lifecycle, objects/unit test-spec corrections, and UTS authoring conventions [AIT-1245] objects & uts: RTO27 channel-state data lifecycle, objects/unit test-spec corrections, and UTS authoring conventions Jul 30, 2026

@paddybyers paddybyers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the LiveObjects specification and its Universal Test Suite (UTS) documentation/tests by introducing a new normative spec point (RTO27) for object-data lifecycle across channel state transitions, correcting several objects/unit UTS specs for portability/spec-faithfulness, and formalising UTS authoring conventions (identifier naming + recording SDK deviations).

Changes:

  • Add normative spec point RTO27 describing objects-data handling on DETACHED/FAILED/SUSPENDED transitions, plus a matching white-box unit UTS test.
  • Refine/correct objects/unit UTS specs (listener duplication optionality note, remove non-portable “re-attach after detach” scenario, add quiescence barriers, clarify null/omitted increment semantics).
  • Document UTS authoring conventions for identifier casing and deviation-handling patterns.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
uts/README.md Adds a concise identifier-naming convention summary with a link to the full rules.
uts/objects/unit/realtime_object.md Updates spec-point coverage, adjusts sync scenarios, and adds the new RTO27 white-box unit test.
uts/objects/unit/path_object.md Adds quiescence barriers before positive reads after inbound OBJECT messages.
uts/objects/unit/internal_live_counter_api.md Clarifies how to handle null-vs-omitted increment semantics in derived tests.
uts/docs/writing-test-specs.md Adds a detailed “Identifier Naming” convention section for UTS pseudocode.
uts/docs/writing-derived-tests.md Refines guidance on deviation patterns and adds an “Idiomatic translation vs genuine deviations” section.
specifications/objects-features.md Introduces new normative spec point RTO27 for object-data lifecycle on channel state transitions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread specifications/objects-features.md Outdated
Comment thread uts/objects/unit/internal_live_counter_api.md Outdated
…ment note

Addresses the two review comments on PR #512:

- RTO27b generalized from SUSPENDED-only to a catch-all: SUSPENDED plus every
  other non-{DETACHED,FAILED} state (INITIALIZED/ATTACHING/DETACHING) retains the
  stored objects data unchanged, so RTO27a+RTO27b are now total over all
  non-ATTACHED transitions (no behaviour change in ably-js/ably-java, which
  already fall through for those states).
- Add reciprocal cross-references between RTO4 (ATTACHED) and RTO27 (non-ATTACHED)
  so the two halves of the channel-state lifecycle are mutually discoverable.
- internal_live_counter_api.md: reword the null-row note around the omitted-argument
  call increment() rather than increment(null), keeping the null-means-omitted aside
  scoped to languages that treat null and the absent value as equivalently nullish,
  and explicitly not endorsing null as a valid amount.
@sacOO7
sacOO7 merged commit d669aa7 into main Jul 31, 2026
2 checks passed
@sacOO7
sacOO7 deleted the fix/objects-uts-cross-sdk-audit branch July 31, 2026 09:02
sacOO7 added a commit to ably/ably-js that referenced this pull request Jul 31, 2026
…x, path_object barriers

Derived-test updates matching the objects/unit UTS spec changes in ably/specification#512.
Test specs only — no SDK changes.

- RTO27 (realtime_object): new tests for the objects-data lifecycle on channel state
  transitions — DETACHED/FAILED clear every pooled object's data to its zero value without
  emitting update events (objects remain in the pool); SUSPENDED retains the data. Driven via
  the internal actOnChannelState handler and asserted against _objectsPool (not black-box
  observable).
- RTO17/RTO18 (realtime_object): rename "ATTACHED without HAS_OBJECTS emits SYNCED only" to
  "... emits SYNCING then SYNCED", matching RTO4c (sync state always transitions SYNCING ->
  SYNCED, even without the HAS_OBJECTS flag).
- RTPO13c5 / RTPO14 (path_object): replace the flushAsync() barrier with the spec's
  poll_until(condition) — poll until the inbound MAP_SET ("back_ref") has applied before the
  positive read.

Verified: realtime_object 41 passing, path_object 30 passing.
sacOO7 added a commit to ably/ably-js that referenced this pull request Jul 31, 2026
… with @SPEC RTO27

- realtime_object.test.ts (RTO27): assert the nested pooled map map:profile@1000 independently —
  cleared on DETACHED/FAILED, retained on SUSPENDED — via the pool (size 3 -> 0 / stays 3), so a
  nested-object regression can't be hidden by the root clear. Addresses the CodeRabbit review;
  mirrors the spec change in ably/specification#512 and the ably-java derived test.
- realtimeobject.ts: annotate actOnChannelState with @SPEC RTO27 (and inline RTO27a/a1/a2/b) —
  comment-only; the behaviour was previously unannotated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants