In arcp-runtime/src/main/java/dev/arcp/runtime/session/JobRecord.java the subscribers accessor at line 164 returns the field's CopyOnWriteArrayList directly. The intent appears to be "the caller can iterate and add subscribers" — SessionLoop.handleSubscribe (arcp-runtime/src/main/java/dev/arcp/runtime/session/SessionLoop.java around line 729) calls rec.subscribers().add(new JobRecord.Subscriber(this, rec.jobId())) directly, and handleUnsubscribe calls rec.subscribers().removeIf(...). That works but it is encapsulation through convention: any other caller (and any future caller — e.g., a TCK test, an example, a downstream user extending JobRecord usage) can mutate the list arbitrarily, including clearing it or inserting nulls, with no validation or invariant enforcement. The class also returns the concrete CoWAL type, leaking the implementation choice into the API.\n\nFix prompt: In arcp-runtime/src/main/java/dev/arcp/runtime/session/JobRecord.java change the subscribers field to remain CopyOnWriteArrayList but rename the public accessor to subscribers() returning List<Subscriber> declared as the JDK interface and wrapped in Collections.unmodifiableList(...) for reads, and add dedicated mutators: addSubscriber(Subscriber) and removeSubscribersWhere(Predicate<Subscriber>). Update SessionLoop.handleSubscribe and handleUnsubscribe to call the new methods. The existing iteration in emitJobEvent around line 774 should iterate via the unmodifiable view. This makes the encapsulation explicit and tightens the API surface without functional change.
In arcp-runtime/src/main/java/dev/arcp/runtime/session/JobRecord.java the subscribers accessor at line 164 returns the field's CopyOnWriteArrayList directly. The intent appears to be "the caller can iterate and add subscribers" — SessionLoop.handleSubscribe (arcp-runtime/src/main/java/dev/arcp/runtime/session/SessionLoop.java around line 729) calls
rec.subscribers().add(new JobRecord.Subscriber(this, rec.jobId()))directly, and handleUnsubscribe callsrec.subscribers().removeIf(...). That works but it is encapsulation through convention: any other caller (and any future caller — e.g., a TCK test, an example, a downstream user extending JobRecord usage) can mutate the list arbitrarily, including clearing it or inserting nulls, with no validation or invariant enforcement. The class also returns the concrete CoWAL type, leaking the implementation choice into the API.\n\nFix prompt: In arcp-runtime/src/main/java/dev/arcp/runtime/session/JobRecord.java change the subscribers field to remain CopyOnWriteArrayList but rename the public accessor tosubscribers()returningList<Subscriber>declared as the JDK interface and wrapped inCollections.unmodifiableList(...)for reads, and add dedicated mutators:addSubscriber(Subscriber)andremoveSubscribersWhere(Predicate<Subscriber>). Update SessionLoop.handleSubscribe and handleUnsubscribe to call the new methods. The existing iteration in emitJobEvent around line 774 should iterate via the unmodifiable view. This makes the encapsulation explicit and tightens the API surface without functional change.