Skip to content

Commit 5926136

Browse files
authored
Merge pull request #29 from Forward-Future/codex/thread-deployed-notifications-20260620
Notify source threads after verified deployment
2 parents ac1fc73 + c3db60b commit 5926136

18 files changed

Lines changed: 1716 additions & 43 deletions

File tree

‎.mergequeue.example.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ merge_to_live_target_minutes = 10
4949
auto_promote = true
5050
intent_scope = "head" # safest: a trusted source agent refreshes replacement heads
5151
pause_on_failure = true
52+
# Receives best-effort events, including retryable thread-deployed messages.
5253
# webhook_url_env = "DEPLOYBOT_WEBHOOK_URL"
5354

5455
# [[pipeline.verifications]]

‎README.md‎

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,10 @@ they cannot create the original per-pull-request deploy intent.
149149

150150
## Delivery controller
151151

152-
`deploybot status` reports active metadata-only agent threads, every PR stage,
153-
deploy requests, queue order, overlaps, exact-`main` CI, deployment, and pipeline
154-
pause state. It never stores prompts, transcripts, source, or credentials.
152+
`deploybot status` reports active metadata-only agent threads, pending native
153+
notifications, every PR stage, deploy requests, queue order, overlaps,
154+
exact-`main` CI, deployment, and pipeline pause state. It never stores prompts,
155+
transcripts, source, or credentials.
155156

156157
`deploybot react` promotes ready intent, skips blockers, drains independent
157158
work, and creates integration PRs when configured. A conflict produces a repair
@@ -171,6 +172,28 @@ and emits a new wake-up event. `follow` tracks newer cumulative `main` revisions
171172
until exact CI, deployment, and optional HTTP checks pass. A CI or deploy failure
172173
can pause further merges until `deploybot unpause`.
173174

175+
At merge time, DeployBot records a non-expiring notification obligation. At
176+
exact-main verification, it promotes every contained obligation to `pending`,
177+
moves the matching source thread to `deployed` when that thread has not moved
178+
on, and returns a stable `thread_notifications` payload for each one.
179+
The provider adapter posts the supplied message into that native thread; for
180+
Codex it wakes the thread with `send_message_to_thread`. The source thread then
181+
acknowledges delivery and becomes `completed`:
182+
183+
```bash
184+
deploybot thread acknowledge --provider codex --thread-id "$CODEX_THREAD_ID" \
185+
--notification-id "$DEPLOYBOT_NOTIFICATION_ID"
186+
```
187+
188+
DeployBot does not treat a registry comment as user notification. If native
189+
delivery fails, an independent outbox entry stays visible under pending
190+
`notifications`, even if the source thread starts new work, and the same
191+
`notification_id` can be retried. When `pipeline.webhook_url_env` is configured,
192+
the provider-neutral webhook also receives the `thread-deployed` payload.
193+
Source adapters attach a native thread heartbeat before returning from an
194+
asynchronous deploy request, so the originating thread can retrieve, acknowledge,
195+
and display the final notification even when no interactive coordinator remains.
196+
174197
```toml
175198
[pipeline]
176199
ci_workflows = ["CI"]
@@ -251,6 +274,7 @@ deploybot plan --json
251274
deploybot doctor --json
252275
deploybot inspect [PR] --json
253276
deploybot thread update --provider CLIENT --thread-id ID --phase PHASE [metadata]
277+
deploybot thread acknowledge --provider CLIENT --thread-id ID --notification-id ID
254278
deploybot request [PR] [--provider CLIENT] [--thread-id ID] [--thread-url URL]
255279
deploybot cancel-request [PR]
256280
deploybot refresh-request [PR]

‎adapters/claude-code/skills/deploybot/SKILL.md‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,29 @@ cumulative base heads until CI, deployment, and configured health checks verify.
7070
Use `diagnose`/`deploybot doctor` for setup drift and `delivery_metrics` for p50,
7171
p95, and slow-stage evidence. A failed cumulative CI or deployment pauses the
7272
controller; only a designated coordinator may unpause after recovery.
73+
74+
## Notify Source Threads
75+
76+
After exact-main verification, `follow_release` returns one
77+
`thread_notifications` entry per source thread and records that thread as
78+
`deployed`. Deliver every entry's `message` into the recorded native thread so
79+
the user can see completion by looking at that thread. In Codex, use the app's
80+
`send_message_to_thread` tool to wake that thread with the supplied message and
81+
instruct it to make the supplied message user-visible, then call
82+
`acknowledge_thread_deployment` without doing more code work. Pass that entry's
83+
`notification_id` to the acknowledgement. If already operating in the source
84+
thread, show the message first, acknowledge it, and finish with the same status.
85+
86+
Treat `notification_id` as the idempotency key. Never acknowledge a thread
87+
before its native-thread delivery succeeds, and never substitute a registry
88+
comment for the native message. If native delivery is unavailable or fails,
89+
leave the notification `pending`; a later coordinator or the provider-neutral
90+
`thread-deployed` webhook can retry it.
91+
92+
The source thread that calls `request_deployment` also owns a durable wake-up.
93+
If it will stop running before verification, attach the provider's native
94+
thread heartbeat or follow-up monitor before returning. In Codex, use a thread
95+
heartbeat automation. On wake, read `pipeline_status`; once this thread is
96+
listed under pending `notifications`, first show its supplied message to the
97+
user, then acknowledge it and remove the heartbeat. Do not use a tight polling
98+
loop.

‎adapters/claude-code/skills/manage-merge-queue/SKILL.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,13 @@ honor dependencies, route overlap or cumulative validation through
1919
`create_integration_pull_request`, return repair packets to the source thread,
2020
and use `resume_pull_request` after fresh review. Finish with `follow_release`;
2121
a failed CI or deployment pauses the pipeline until verified recovery.
22+
23+
When `follow_release` returns `thread_notifications`, send each supplied
24+
message to its native source thread. The source thread calls
25+
`acknowledge_thread_deployment` with the matching `notification_id` and reports
26+
completion to the user. Leave failed notifications `pending` so they remain
27+
retryable.
28+
29+
Before a requesting source thread stops running, attach a native follow-up
30+
monitor that checks `pipeline_status` and wakes it to report and acknowledge its
31+
matching pending notification.

‎adapters/codex/agent-merge-queue/skills/deploybot/SKILL.md‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,29 @@ cumulative base heads until CI, deployment, and configured health checks verify.
7070
Use `diagnose`/`deploybot doctor` for setup drift and `delivery_metrics` for p50,
7171
p95, and slow-stage evidence. A failed cumulative CI or deployment pauses the
7272
controller; only a designated coordinator may unpause after recovery.
73+
74+
## Notify Source Threads
75+
76+
After exact-main verification, `follow_release` returns one
77+
`thread_notifications` entry per source thread and records that thread as
78+
`deployed`. Deliver every entry's `message` into the recorded native thread so
79+
the user can see completion by looking at that thread. In Codex, use the app's
80+
`send_message_to_thread` tool to wake that thread with the supplied message and
81+
instruct it to make the supplied message user-visible, then call
82+
`acknowledge_thread_deployment` without doing more code work. Pass that entry's
83+
`notification_id` to the acknowledgement. If already operating in the source
84+
thread, show the message first, acknowledge it, and finish with the same status.
85+
86+
Treat `notification_id` as the idempotency key. Never acknowledge a thread
87+
before its native-thread delivery succeeds, and never substitute a registry
88+
comment for the native message. If native delivery is unavailable or fails,
89+
leave the notification `pending`; a later coordinator or the provider-neutral
90+
`thread-deployed` webhook can retry it.
91+
92+
The source thread that calls `request_deployment` also owns a durable wake-up.
93+
If it will stop running before verification, attach the provider's native
94+
thread heartbeat or follow-up monitor before returning. In Codex, use a thread
95+
heartbeat automation. On wake, read `pipeline_status`; once this thread is
96+
listed under pending `notifications`, first show its supplied message to the
97+
user, then acknowledge it and remove the heartbeat. Do not use a tight polling
98+
loop.

‎adapters/codex/agent-merge-queue/skills/manage-merge-queue/SKILL.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,13 @@ dependencies, and use `create_integration_pull_request` for overlaps or a
2020
cumulative batch gate. Return repair packets to their source thread and use
2121
`resume_pull_request` after fresh review. Finish with `follow_release`; a failed
2222
CI or deployment pauses the pipeline until verified recovery.
23+
24+
When `follow_release` returns `thread_notifications`, send each supplied
25+
message to its native source thread. In Codex use `send_message_to_thread`;
26+
the source thread calls `acknowledge_thread_deployment` with the matching
27+
`notification_id` and reports completion to the user. Leave failed
28+
notifications `pending` so they remain retryable.
29+
30+
Before a requesting source thread stops running, attach a native thread
31+
heartbeat that checks `pipeline_status` and wakes it to report and acknowledge
32+
its matching pending notification.

‎adapters/cursor/.cursor/rules/deploybot.mdc‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,12 @@ exact heads, blockers, overlap, CI, and deployment. Only the user's exact
1414
the stable Cursor thread ID, never prompts or transcripts. Refresh intent only
1515
after replacement-head review. Only the coordinator may react, integrate,
1616
drain, follow, pause, or resume repaired work.
17+
18+
After exact-main verification, deliver each returned `thread_notifications`
19+
message into its native source thread, then call
20+
`acknowledge_thread_deployment` with the matching `notification_id`. Leave a
21+
failed notification `pending` so a later coordinator can retry it; a registry
22+
comment is not a user notification.
23+
24+
Before the requesting source thread stops, attach a native follow-up monitor
25+
that wakes it when `pipeline_status` lists its pending notification.

‎adapters/cursor/AGENTS.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,11 @@ Never poll, merge an unlabeled PR, or absorb unrelated work. Let the event worke
1515
promote fresh exact heads, use one integration PR for overlaps or cumulative
1616
validation, return repair packets to the source thread, atomically resume after
1717
fresh review, and follow cumulative `main` through verified deployment.
18+
19+
For each verified `thread_notifications` entry, post its message back to the
20+
native source thread and only then call `acknowledge_thread_deployment`. Leave
21+
failed notifications `pending` for a later retry, and pass the matching
22+
`notification_id` when acknowledging.
23+
24+
Before a requesting source thread stops, attach a native follow-up monitor that
25+
wakes it when `pipeline_status` lists its pending notification.

‎docs/reference.md‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ DeployBot resolves the pull request for the current branch.
2323
| `deploybot init [--force]` | Write a safe starter policy. Existing files are preserved unless `--force` is supplied. |
2424
| `deploybot ensure-labels` | Create or refresh the configured queue, blocked, intent, pause, and registry labels. |
2525
| `deploybot doctor [--json]` | Check authentication, policy, labels, actors, checks, workflows, and branch protection without changing repository state. |
26-
| `deploybot status [--json]` | Read active thread metadata, PR stages, deploy intent, queue state, exact-main CI, deployment, and pipeline control state. |
26+
| `deploybot status [--json]` | Read active thread metadata, pending native notifications, PR stages, deploy intent, queue state, exact-main CI, deployment, and pipeline control state. |
2727
| `deploybot plan [--json]` | Read the ordered queue, dependencies, blockers, and source-overlap groups. |
2828
| `deploybot inspect [PR] [--json]` | Evaluate one exact PR head without granting merge authority. |
2929
| `deploybot metrics [--limit N] [--json]` | Summarize p50, p95, and maximum delivery timings for recent merged PRs. The default limit is 25. |
@@ -36,7 +36,8 @@ batch marker.
3636

3737
| Command | Purpose |
3838
| --- | --- |
39-
| `deploybot thread update --provider CLIENT --thread-id ID --phase PHASE [--title TEXT] [--branch NAME] [--pr NUMBER] [--url URL]` | Publish metadata-only thread state. Valid phases are `working`, `pr-draft`, `pr-review`, `ready`, `deploy-requested`, `queued`, `merged`, `blocked`, `completed`, and `abandoned`. |
39+
| `deploybot thread update --provider CLIENT --thread-id ID --phase PHASE [--title TEXT] [--branch NAME] [--pr NUMBER] [--url URL]` | Publish metadata-only thread state. Valid client-published phases are `working`, `pr-draft`, `pr-review`, `ready`, `deploy-requested`, `queued`, `merged`, `blocked`, `completed`, and `abandoned`; `deployed` is controller-owned. |
40+
| `deploybot thread acknowledge --provider CLIENT --thread-id ID --notification-id ID` | Mark the matching `deployed` notification `completed` only after its native-thread message is delivered. Repeated acknowledgement is safe; stale IDs are rejected. |
4041
| `deploybot request [PR] [--provider CLIENT] [--thread-id ID] [--thread-url URL]` | Record the user's durable deploy intent for the current exact head, even while gates are pending. |
4142
| `deploybot cancel-request [PR]` | Cancel an unmerged durable deploy request. |
4243
| `deploybot refresh-request [PR]` | Bind existing user intent to a freshly reviewed replacement head. |
@@ -97,6 +98,7 @@ arguments shown below are the tool-specific arguments.
9798
| `follow_release` | `follow --json` | optional `timeout_seconds` |
9899
| `delivery_metrics` | `metrics --json` | optional `limit` |
99100
| `update_agent_thread` | `thread update` | `provider`, `thread_id`, `phase`; optional `pull_request`, `title`, `branch`, `url` |
101+
| `acknowledge_thread_deployment` | `thread acknowledge` | `provider`, `thread_id`, `notification_id` |
100102
| `block_pull_request` | `block PR` | `pull_request`, `reason` |
101103
| `unblock_pull_request` | `unblock PR` | `pull_request` |
102104
| `resume_pull_request` | `resume PR` | `pull_request` |
@@ -156,7 +158,7 @@ Provider fields are:
156158
| `pause_label` | `"deploybot-paused"` |
157159
| `registry_label` | `"deploybot-registry"` |
158160
| `registry_title` | `"DeployBot delivery registry"` |
159-
| `thread_active_hours` | Positive integer; default 72. |
161+
| `thread_active_hours` | Positive integer; default 72. Notification obligations and pending messages use their own non-expiring outbox. |
160162
| `ci_workflows` | Workflow names followed as exact-main CI. Default: `["CI"]`. |
161163
| `deploy_workflows` | Deployment workflow names. Default: `["Deploy"]`. |
162164
| `batch_settle_seconds` | Non-negative window for coalescing near-ready deploy requests before freezing a batch. Default: 15. |
@@ -167,7 +169,7 @@ Provider fields are:
167169
| `auto_promote` | Default `true`. |
168170
| `intent_scope` | Currently must be `"head"`. |
169171
| `pause_on_failure` | Default `true`. |
170-
| `webhook_url_env` | Optional environment-variable name containing a best-effort event webhook URL. GitHub remains authoritative if notification fails. |
172+
| `webhook_url_env` | Optional environment-variable name containing a best-effort event webhook URL. It receives retryable `thread-deployed` payloads after exact-main verification. GitHub remains authoritative if notification fails. |
171173
| `verifications` | Optional array of post-deployment HTTP verification tables. Default: empty. |
172174
| `name` | Required health-check display name in each `pipeline.verifications` entry. |
173175
| `url` | Required URL checked after deployment in each `pipeline.verifications` entry. |
@@ -207,3 +209,11 @@ list aligned with `pipeline.ci_workflows`.
207209
webhook URL.
208210
- The composite Action maps `github.token` to `GH_TOKEN`; callers do not need to
209211
create a separate token when its documented permissions are sufficient.
212+
213+
The `thread-deployed` event contains `notification_id`, `repository`,
214+
`provider`, `thread_id`, `main_sha`, and a user-facing `message`, plus available
215+
thread, pull-request, CI, and deployment URLs. Consumers must deduplicate on
216+
`notification_id`, deliver `message` into the native provider thread, and call
217+
`acknowledge_thread_deployment` only after that succeeds. Until acknowledgement,
218+
the independent outbox record remains `pending` even if thread lifecycle moves
219+
on, and later release followers can retry the same notification.

‎skills/deploybot/SKILL.md‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,29 @@ cumulative base heads until CI, deployment, and configured health checks verify.
7070
Use `diagnose`/`deploybot doctor` for setup drift and `delivery_metrics` for p50,
7171
p95, and slow-stage evidence. A failed cumulative CI or deployment pauses the
7272
controller; only a designated coordinator may unpause after recovery.
73+
74+
## Notify Source Threads
75+
76+
After exact-main verification, `follow_release` returns one
77+
`thread_notifications` entry per source thread and records that thread as
78+
`deployed`. Deliver every entry's `message` into the recorded native thread so
79+
the user can see completion by looking at that thread. In Codex, use the app's
80+
`send_message_to_thread` tool to wake that thread with the supplied message and
81+
instruct it to make the supplied message user-visible, then call
82+
`acknowledge_thread_deployment` without doing more code work. Pass that entry's
83+
`notification_id` to the acknowledgement. If already operating in the source
84+
thread, show the message first, acknowledge it, and finish with the same status.
85+
86+
Treat `notification_id` as the idempotency key. Never acknowledge a thread
87+
before its native-thread delivery succeeds, and never substitute a registry
88+
comment for the native message. If native delivery is unavailable or fails,
89+
leave the notification `pending`; a later coordinator or the provider-neutral
90+
`thread-deployed` webhook can retry it.
91+
92+
The source thread that calls `request_deployment` also owns a durable wake-up.
93+
If it will stop running before verification, attach the provider's native
94+
thread heartbeat or follow-up monitor before returning. In Codex, use a thread
95+
heartbeat automation. On wake, read `pipeline_status`; once this thread is
96+
listed under pending `notifications`, first show its supplied message to the
97+
user, then acknowledge it and remove the heartbeat. Do not use a tight polling
98+
loop.

0 commit comments

Comments
 (0)