-
Notifications
You must be signed in to change notification settings - Fork 3k
feat(core): make self-paced /loop lean on monitor/background-task notifications #5844
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1f185e4
dae2e3c
49091d8
1575172
db93f36
487cd55
096021c
9aec4ad
25bf763
3433744
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -43,19 +43,24 @@ Examples: | |||||
| Use this path only when the user supplied a prompt and no interval. | ||||||
|
|
||||||
| 1. Do not call CronCreate for this path. | ||||||
| 2. Run the parsed prompt immediately now. | ||||||
| 2. If this tick opens with a `<task-notification>` block (a monitor or background event re-invoked you, not a bare `/loop` wakeup prompt), handle that event before re-running the prompt. | ||||||
| - If the notification says the watched condition was met, cancel any pending fallback LoopWakeup with CronDelete if you still have its ID, then finish the loop. | ||||||
| - If a monitor auto-stopped on idle or max-events, restart it once if the watch is still useful, re-arm the fallback, report the restart count to the user, and include that count in the LoopWakeup prompt or reason (for example, `monitor restarted 1/1 time`) so it survives context compaction. If it auto-stops again on the next tick, end the loop and report the repeated auto-stop to the user. | ||||||
| - If the signal is ambiguous, re-arm a shorter follow-up and investigate on the next tick. If the signal remains ambiguous for three consecutive ticks, end the loop and report that the watch could not reach a clear conclusion. | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] The three-tick ambiguity cap has no persistence mechanism, unlike the adjacent restart counter which explicitly carries state via Consider mirroring the restart-branch pattern: add an instruction to include the ambiguity count in the LoopWakeup prompt (e.g., — qwen3.7-max via Qwen Code /review |
||||||
| 3. Run the parsed prompt immediately now. | ||||||
| - If it is a slash command, invoke it via the Skill tool. | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] The ambiguous-signal branch has no retry cap, unlike the other two branches: "condition met" terminates immediately, "auto-stopped" restarts once then terminates, but "ambiguous" re-arms with no limit. If monitored resources consistently produce ambiguous output, the loop re-arms indefinitely (up to the 24h chain cap).
Suggested change
— qwen3.7-max via Qwen Code /review |
||||||
| - Otherwise, act on it directly. | ||||||
| 3. Before ending the turn, decide whether another check is useful. | ||||||
| 4. Before ending the turn, decide whether another check is useful. | ||||||
| - Call LoopWakeup only if continued follow-up is useful. | ||||||
| - Do not call LoopWakeup if the task is complete. | ||||||
| - Do not call LoopWakeup if the task is blocked on user input or external state that cannot be checked later. | ||||||
| - Do not call LoopWakeup just to keep polling when no useful next check exists. | ||||||
| 4. When scheduling a continuation, call LoopWakeup with: | ||||||
| - `delaySeconds`: the next useful delay in seconds. The runtime clamps to 60–3600 (1–60 min); follow the tool's own guidance on picking a value (it accounts for the prompt-cache window). | ||||||
| - `prompt`: `/loop ${original prompt}` | ||||||
| - `reason`: a short reason for the chosen delay. | ||||||
| 5. Briefly tell the user what was done now. If a wakeup was scheduled, include when the next check is expected. If no wakeup was scheduled, say the loop is complete or not continuing. | ||||||
| - If you started a background agent or a Monitor, it wakes you via a terminal `<task-notification>` on exit, failure, cancellation, or monitor auto-stop — so set LoopWakeup as a long fallback rather than a short poll. Do not omit it just because something is watching: the work may hang, or a Monitor may auto-stop on idle or max-events (and one owned by another agent routes its notification only to that agent). Omit LoopWakeup only on the terminal conditions above (complete, blocked, or repeated monitor auto-stop). | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] When a task-notification arrives and the loop ends (condition met or repeated auto-stop), the previously scheduled 1200–1800s fallback wakeup is still pending in the CronScheduler. It will fire 20–30 minutes later as a stale prompt, and Consider adding a note here to cancel the pending fallback wakeup with — qwen3.7-max via Qwen Code /review
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] This line says "a background agent or a Monitor" but omits background shells (
Suggested change
— qwen3.7-max via Qwen Code /review |
||||||
| 5. When scheduling a continuation, call LoopWakeup with: | ||||||
| - `delaySeconds`: the next useful delay in seconds. The runtime clamps to 60–3600 (1–60 min); follow the tool's own guidance on picking a value — it accounts for the prompt-cache window and for the fallback-heartbeat case when a background task will wake you. | ||||||
| - `prompt`: `/loop ${original prompt}` plus any state the next tick must preserve, such as `monitor restarted 1/1 time`. | ||||||
| - `reason`: a short reason for the chosen delay. Include the monitor restart count here when re-arming after an auto-stop. | ||||||
| 6. Briefly tell the user what was done now. If a wakeup was scheduled, include when the next check is expected. If no wakeup was scheduled because a notification ended the loop, mention whether the stale fallback was cancelled; if the wakeup ID was lost, ignore or answer the stale wakeup briefly when it fires. | ||||||
|
|
||||||
| ## Fixed-interval recurring path | ||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -47,6 +47,38 @@ describe('bundled loop skill', () => { | |||||||||||||||||||||||||||||||||||||||||||||
| expect(body).not.toContain('delayMinutes'); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| it('teaches the self-paced loop to lean on monitor/background-task notifications', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] The new test asserts 15+ substrings from SKILL.md but misses the two core safety justifications added at SKILL.md line 58:
Suggested change
— qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||||||||||||||||||||
| const { body } = loadLoopSkill(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('<task-notification>'); | ||||||||||||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] The new test asserts four substrings but misses the most load-bearing behavioral directive added by this PR: If a future edit softens or removes these directives, the test still passes. Consider adding: expect(body).toContain('Do not omit it just because something is watching');
expect(body).toContain('not a bare `/loop` wakeup prompt');— qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('set LoopWakeup as a long fallback'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('auto-stop on idle or max-events'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('handle that event before re-running the prompt'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('terminal `<task-notification>`'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).not.toContain('per stdout line'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain( | ||||||||||||||||||||||||||||||||||||||||||||||
| 'If the notification says the watched condition was met', | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('cancel any pending fallback LoopWakeup'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('CronDelete'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('If a monitor auto-stopped'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('restart it once'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('monitor restarted 1/1 time'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('so it survives context compaction'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('report the repeated auto-stop to the user'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('report the restart count'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('If the signal is ambiguous'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('three consecutive ticks'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('Do not omit it just because something is watching'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('the work may hang'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain( | ||||||||||||||||||||||||||||||||||||||||||||||
| 'one owned by another agent routes its notification only to that agent', | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('repeated monitor auto-stop'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('not a bare `/loop` wakeup prompt'); | ||||||||||||||||||||||||||||||||||||||||||||||
| expect(body).toContain('stale fallback was cancelled'); | ||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| it('keeps fixed-interval inputs on the recurring cron path', () => { | ||||||||||||||||||||||||||||||||||||||||||||||
| const { body } = loadLoopSkill(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Suggestion] After handling a notification in step 2, the model continues to step 3 ("Run the parsed prompt immediately now") unconditionally. But the three sub-cases describe different outcomes:
Consider making the short-circuit explicit, e.g.: "handle that event and end the turn (do not continue to step 3)" or restructuring so steps 3–6 only run when there is no notification.
— qwen3.7-max via Qwen Code /review