Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Trajectory: Fix Swift AsyncStream continuation leaks for issue 1266

> **Status:** ✅ Completed
> **Confidence:** 95%
> **Started:** July 15, 2026 at 06:39 AM
> **Completed:** July 15, 2026 at 06:53 AM

---

## Summary

Fixed Swift SDK AsyncStream continuation leaks with cancellation cleanup, bounded buffers, lazy channel streams, disconnect generation fencing, and 129 passing regression tests

**Approach:** Standard approach

---

## Key Decisions

### Use token-keyed bounded AsyncStream registries in both Swift SDKs

- **Chose:** Use token-keyed bounded AsyncStream registries in both Swift SDKs
- **Reasoning:** Stable UUIDs plus termination tombstones close cancellation-before-registration races; bufferingNewest(256) bounds event/message queues, bufferingNewest(1) bounds state, and lazy channel streams avoid join-only queues.

### Fence Swift stream registration with disconnect generations

- **Chose:** Fence Swift stream registration with disconnect generations
- **Reasoning:** Claude review identified tombstone growth and post-disconnect registration resurrection. Cancel-aware registration tasks eliminate termination tombstones, while a lock-protected generation invalidated at disconnect rejects actor tasks scheduled by older stream epochs.

---

## Chapters

### 1. Work

_Agent: default_

- Use token-keyed bounded AsyncStream registries in both Swift SDKs: Use token-keyed bounded AsyncStream registries in both Swift SDKs
- Fence Swift stream registration with disconnect generations: Fence Swift stream registration with disconnect generations
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"id": "traj_bwj7uf3ksgz6",
"version": 1,
"task": {
"title": "Fix Swift AsyncStream continuation leaks for issue 1266"
},
"status": "completed",
"startedAt": "2026-07-15T10:39:35.461Z",
"completedAt": "2026-07-15T10:53:40.411Z",
"agents": [
{
"name": "default",
"role": "lead",
"joinedAt": "2026-07-15T10:45:05.763Z"
}
],
"chapters": [
{
"id": "chap_vvjmcd71cc1k",
"title": "Work",
"agentName": "default",
"startedAt": "2026-07-15T10:45:05.763Z",
"endedAt": "2026-07-15T10:53:40.411Z",
"events": [
{
"ts": 1784112305764,
"type": "decision",
"content": "Use token-keyed bounded AsyncStream registries in both Swift SDKs: Use token-keyed bounded AsyncStream registries in both Swift SDKs",
"raw": {
"question": "Use token-keyed bounded AsyncStream registries in both Swift SDKs",
"chosen": "Use token-keyed bounded AsyncStream registries in both Swift SDKs",
"alternatives": [],
"reasoning": "Stable UUIDs plus termination tombstones close cancellation-before-registration races; bufferingNewest(256) bounds event/message queues, bufferingNewest(1) bounds state, and lazy channel streams avoid join-only queues."
},
"significance": "high"
},
{
"ts": 1784112604320,
"type": "decision",
"content": "Fence Swift stream registration with disconnect generations: Fence Swift stream registration with disconnect generations",
"raw": {
"question": "Fence Swift stream registration with disconnect generations",
"chosen": "Fence Swift stream registration with disconnect generations",
"alternatives": [],
"reasoning": "Claude review identified tombstone growth and post-disconnect registration resurrection. Cancel-aware registration tasks eliminate termination tombstones, while a lock-protected generation invalidated at disconnect rejects actor tasks scheduled by older stream epochs."
},
"significance": "high"
}
]
}
],
"retrospective": {
"summary": "Fixed Swift SDK AsyncStream continuation leaks with cancellation cleanup, bounded buffers, lazy channel streams, disconnect generation fencing, and 129 passing regression tests",
"approach": "Standard approach",
"confidence": 0.95
},
"commits": [],
"filesChanged": [],
"projectId": "AgentWorkforce/relay",
"tags": [],
"_trace": {
"startRef": "bd1531aaea1bdb442c6c6c02498dc032cd0aecf6",
"endRef": "bd1531aaea1bdb442c6c6c02498dc032cd0aecf6"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Trajectory: Resolve renewed PR 1268 conflicts after broker Swift parity merge

> **Status:** ✅ Completed
> **Confidence:** 98%
> **Started:** July 15, 2026 at 07:30 AM
> **Completed:** July 15, 2026 at 07:31 AM

---

## Summary

Merged current main into PR 1268, composed broker parity additions with stream lifecycle changes, and passed all 151 Swift tests

**Approach:** Standard approach

---

## Key Decisions

### Compose broker parity APIs with stream lifecycle registries

- **Chose:** Compose broker parity APIs with stream lifecycle registries
- **Reasoning:** Current main added independent decoding/path helpers and control/observability tests; PR 1268 added UUID continuation registries and lifecycle tests. Keeping both preserves all behavior, with connection-state fan-out continuing through registry values.

---

## Chapters

### 1. Work

_Agent: default_

- Composed broker parity APIs with stream lifecycle registries while preserving connection-state fan-out.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: This PR does not ship the advertised Swift AsyncStream lifecycle fix: its only added artifact records no changed files or commits. The lifecycle registries, buffer bounds, disconnect fencing, and regression tests need to be included in this PR (or the PR scope/description should be corrected) before merge.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .agentworkforce/trajectories/completed/2026-07/traj_nt7clb37dtr7/trajectory.json, line 46:

<comment>This PR does not ship the advertised Swift `AsyncStream` lifecycle fix: its only added artifact records no changed files or commits. The lifecycle registries, buffer bounds, disconnect fencing, and regression tests need to be included in this PR (or the PR scope/description should be corrected) before merge.</comment>

<file context>
@@ -0,0 +1,53 @@
+    "confidence": 0.98
+  },
+  "commits": [],
+  "filesChanged": [],
+  "projectId": "AgentWorkforce/relay",
+  "tags": [],
</file context>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This finding is based on one conflict-resolution trajectory rather than the PR diff. PR #1268 does include the lifecycle registry, buffering, fencing, and test changes in its Swift source/test files; an individual trajectory record having no associated commit metadata does not mean the PR is empty. No product-code change is needed for this comment.

"id": "traj_nt7clb37dtr7",
"version": 1,
"task": {
"title": "Resolve renewed PR 1268 conflicts after broker Swift parity merge"
},
"status": "completed",
"startedAt": "2026-07-15T11:30:37.003Z",
"completedAt": "2026-07-15T11:31:36.846Z",
"agents": [
{
"name": "default",
"role": "lead",
"joinedAt": "2026-07-15T11:31:36.445Z"
}
],
"chapters": [
{
"id": "chap_47tubwavnk4u",
"title": "Work",
"agentName": "default",
"startedAt": "2026-07-15T11:31:36.445Z",
"endedAt": "2026-07-15T11:31:36.846Z",
"events": [
{
"ts": 1784115096446,
"type": "decision",
"content": "Compose broker parity APIs with stream lifecycle registries: Compose broker parity APIs with stream lifecycle registries",
"raw": {
"question": "Compose broker parity APIs with stream lifecycle registries",
"chosen": "Compose broker parity APIs with stream lifecycle registries",
"alternatives": [],
"reasoning": "Current main added independent decoding/path helpers and control/observability tests; PR 1268 added UUID continuation registries and lifecycle tests. Keeping both preserves all behavior, with connection-state fan-out continuing through registry values."
},
"significance": "high"
}
]
}
],
"retrospective": {
"summary": "Merged current main into PR 1268, composed broker parity additions with stream lifecycle changes, and passed all 151 Swift tests",
"approach": "Standard approach",
"confidence": 0.98
},
"commits": [],
"filesChanged": [],
"projectId": "AgentWorkforce/relay",
"tags": [],
"_trace": {
"startRef": "38e00002cfba37694c8d0a9bd45d3c748d29c605",
"endRef": "38e00002cfba37694c8d0a9bd45d3c748d29c605"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Trajectory: Resolve PR 1268 merge conflicts with current main

> **Status:** ✅ Completed
> **Confidence:** 98%
> **Started:** July 15, 2026 at 07:01 AM
> **Completed:** July 15, 2026 at 07:02 AM

---

## Summary

Merged current origin/main into PR 1268, retained all Unreleased patch changelog entries, and passed all 129 Swift tests

**Approach:** Standard approach

---

## Key Decisions

### Keep all Unreleased patch changelog entries

- **Chose:** Keep all Unreleased patch changelog entries
- **Reasoning:** The only merge conflict combined independent user-visible fixes from PR 1268 and current main, all at patch impact, so no entry should be dropped.

---

## Chapters

### 1. Work

_Agent: default_

- Keep all Unreleased patch changelog entries: Keep all Unreleased patch changelog entries
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"id": "traj_u94plhvjk6ew",
"version": 1,
"task": {
"title": "Resolve PR 1268 merge conflicts with current main"
},
"status": "completed",
"startedAt": "2026-07-15T11:01:37.458Z",
"completedAt": "2026-07-15T11:02:30.385Z",
"agents": [
{
"name": "default",
"role": "lead",
"joinedAt": "2026-07-15T11:02:29.972Z"
}
],
"chapters": [
{
"id": "chap_rb8pg5ihhb6g",
"title": "Work",
"agentName": "default",
"startedAt": "2026-07-15T11:02:29.972Z",
"endedAt": "2026-07-15T11:02:30.385Z",
"events": [
{
"ts": 1784113349973,
"type": "decision",
"content": "Keep all Unreleased patch changelog entries: Keep all Unreleased patch changelog entries",
"raw": {
"question": "Keep all Unreleased patch changelog entries",
"chosen": "Keep all Unreleased patch changelog entries",
"alternatives": [],
"reasoning": "The only merge conflict combined independent user-visible fixes from PR 1268 and current main, all at patch impact, so no entry should be dropped."
},
"significance": "high"
}
]
}
],
"retrospective": {
"summary": "Merged current origin/main into PR 1268, retained all Unreleased patch changelog entries, and passed all 129 Swift tests",
"approach": "Standard approach",
"confidence": 0.98
},
"commits": [],
"filesChanged": [],
"projectId": "AgentWorkforce/relay",
"tags": [],
"_trace": {
"startRef": "049ff657924e52309956da08695bbd5d57990739",
"endRef": "049ff657924e52309956da08695bbd5d57990739"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Trajectory: Address PR 1268 review feedback

> **Status:** ✅ Completed
> **Task:** PR-1268
> **Confidence:** 96%
> **Started:** July 15, 2026 at 03:06 PM
> **Completed:** July 15, 2026 at 03:11 PM

---

## Summary

Addressed PR 1268 review feedback by synchronizing requested channel stream registration before subscribe, documenting the ordering contract, bounding lifecycle tests with throwing deadlines, removing obsolete broker subscription state, applying Swift registry cleanups, and correcting duplicated trajectory prose. All 151 Swift tests pass.

**Approach:** Standard approach

---

## Key Decisions

### Coordinate requested channel stream registration with subscribe

- **Chose:** Coordinate requested channel stream registration with subscribe
- **Reasoning:** Preserves lossless subscription startup for consumers that request events first while keeping join-only subscriptions free of unused event buffers; documented the required ordering in API docs and quick start.

---

## Chapters

### 1. Work

_Agent: default_

- Coordinate requested channel stream registration with subscribe: Coordinate requested channel stream registration with subscribe
- Addressed every valid new review thread: registration ordering, bounded test waits, obsolete broker state, Swift dictionary cleanups, and duplicated trajectory prose. Two Dictionary.Values suggestions remain intentionally unapplied because finishAll mutates registry values; the trajectory empty-commit warning misreads one conflict-resolution record as the whole PR.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"id": "traj_ym4mjwfa0kmx",
"version": 1,
"task": {
"title": "Address PR 1268 review feedback",
"source": {
"system": "plain",
"id": "PR-1268"
}
},
"status": "completed",
"startedAt": "2026-07-15T19:06:53.525Z",
"completedAt": "2026-07-15T19:11:25.090Z",
"agents": [
{
"name": "default",
"role": "lead",
"joinedAt": "2026-07-15T19:11:18.474Z"
}
],
"chapters": [
{
"id": "chap_dd5ahsddm9v4",
"title": "Work",
"agentName": "default",
"startedAt": "2026-07-15T19:11:18.474Z",
"endedAt": "2026-07-15T19:11:25.090Z",
"events": [
{
"ts": 1784142678475,
"type": "decision",
"content": "Coordinate requested channel stream registration with subscribe: Coordinate requested channel stream registration with subscribe",
"raw": {
"question": "Coordinate requested channel stream registration with subscribe",
"chosen": "Coordinate requested channel stream registration with subscribe",
"alternatives": [],
"reasoning": "Preserves lossless subscription startup for consumers that request events first while keeping join-only subscriptions free of unused event buffers; documented the required ordering in API docs and quick start."
},
"significance": "high"
},
{
"ts": 1784142678946,
"type": "reflection",
"content": "Addressed every valid new review thread: registration ordering, bounded test waits, obsolete broker state, Swift dictionary cleanups, and duplicated trajectory prose. Two Dictionary.Values suggestions remain intentionally unapplied because finishAll mutates registry values; the trajectory empty-commit warning misreads one conflict-resolution record as the whole PR.",
"raw": {
"confidence": 0.94
},
"significance": "high",
"tags": ["confidence:0.94"]
}
]
}
],
"retrospective": {
"summary": "Addressed PR 1268 review feedback by synchronizing requested channel stream registration before subscribe, documenting the ordering contract, bounding lifecycle tests with throwing deadlines, removing obsolete broker subscription state, applying Swift registry cleanups, and correcting duplicated trajectory prose. All 151 Swift tests pass.",
"approach": "Standard approach",
"confidence": 0.96
},
"commits": [],
"filesChanged": [],
"projectId": "AgentWorkforce/relay",
"tags": [],
"_trace": {
"startRef": "de5c3fd53e953523a02c60223dd50d32e7c3902b",
"endRef": "de5c3fd53e953523a02c60223dd50d32e7c3902b"
}
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- `AgentRelaySDK` and `AgentRelayBrokerSDK` (Swift) release cancelled async-stream consumers, bound event buffering, and avoid creating channel event queues for join-only subscriptions, preventing reconnect-driven memory growth.
- `agent-relay-broker` retries the Codex model-detection spawn (`codex debug models`) on `ExecutableFileBusy` (`ETXTBSY`), so a concurrent `fork`/`exec` race under load no longer aborts detection and spuriously falls back away from the requested model.
- `agent-relay-broker` bounds each initial Relaycast startup handshake attempt and retries on timeout with backoff (per-attempt deadline scaled by the configured workspace count), so a stalled backend connection no longer hangs `agent-relay node up`/`init` until an external supervisor kills the broker (which surfaced as an opaque "broker exited with code null during initial handshake"). Returned errors are surfaced immediately rather than replayed. Tunable via `AGENT_RELAY_HANDSHAKE_TIMEOUT_MS` and `AGENT_RELAY_HANDSHAKE_ATTEMPTS`.

Expand Down
Loading
Loading