detached 7e95a47

feat(agent): route delegated scopes before work in proactive mode (#214)

Interactive proactive delegation now routes when the user states the request
rather than at process start, where neither the task nor the workers it needs
are knowable. The reply that starts the work names the parts the primary keeps
on the critical path and the profile that owns each delegable part.

The routing block is added only when launch-time policy exposes the spawn
action; the spawn-withheld compositions keep the ownership contract and their
existing local-work fallback without naming profiles they cannot dispatch.
commit
7e95a47013630f3b1a12e0c510bca387747922ae
author
bnema · · 2026-09-19 08:51 UTC
committer
GitHub · · 2026-09-19 08:51 UTC
parent
dfb1f93
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
4 files · +79 -3
M README.md +2 -2
@@ -452,7 +452,7 @@ Each child runs without a cumulative token budget or step cap: it inherits the p
452452
453453 Worktree isolation captures a patch and changed-file list without applying it. Shared children use the host checkout. The available logical references are `subagent://<id>`, `subagent://<id>/history`, `subagent://<id>/result`, and `subagent://<id>/patch`.
454454
455-**Proactive delegation.** With `/settings` → **proactive delegation** on (or `/orchestrator`), the interactive primary remains the owner and implementer of the task. It delegates only concrete sidecars that can progress while the primary performs a preselected non-overlapping task; immediate blockers and tightly coupled work stay local. An active worker owns its scope until completion, so the primary does not repeat that investigation, review, testing, or implementation. If an explicit user request or workflow hands blocking work to a child, the primary yields for the host completion update instead. The canonical resident `subagent` tool remains available for user-requested delegation when the setting is off, subject to launch-time permissions. Do not poll the status action or journal files solely to wait for completion.
455+**Proactive delegation.** With `/settings` → **proactive delegation** on (or `/orchestrator`), the interactive primary remains the owner and implementer of the task. Process start reveals neither the task nor which workers it needs, so it routes when the user states the request: the reply that starts the work names the parts it keeps on the critical path and the profile that owns each delegable part, before implementing or spawning anything. It delegates only concrete sidecars that can progress while the primary performs a preselected non-overlapping task; immediate blockers and tightly coupled work stay local. An active worker owns its scope until completion, so the primary does not repeat that investigation, review, testing, or implementation. If an explicit user request or workflow hands blocking work to a child, the primary yields for the host completion update instead. The canonical resident `subagent` tool remains available for user-requested delegation when the setting is off, subject to launch-time permissions. Do not poll the status action or journal files solely to wait for completion.
456456
457457 ### Named subagent profiles
458458
@@ -466,7 +466,7 @@ Opens a dialog with every persistent setting. `up`/`down` to navigate, `enter` o
466466 - **render images when supported** — draw screenshots / `read`-returned images inline using the terminal's image protocol, or fall back to a text placeholder. Auto-detected from `TERM_PROGRAM`; the toggle overrides the detection. The row is greyed out and forced off on terminals that don't speak any image protocol.
467467 - **terminal alerts** — emit a terminal bell when the main session stops with work that may need attention, or when an extension raises a structured alert. Enabled by default; changes apply immediately and persist as `terminal_alerts_enabled`. Terminal emulators may render the bell audibly, visually, or not at all.
468468 - **AI terminal titles** — after the first real prompt of a fresh interactive session, make one small hidden request to the active model and set the terminal title to `zut: <title>`. The title is limited to 40 Unicode characters, persisted with the session, restored on resume, and never added to the conversation. Enabled by default; disable it to avoid the extra model request. The toggle applies immediately, but title generation still waits for the first real prompt and never starts from startup context, resumed history, or slash commands. Persists as `terminal_title_enabled`.
469-- **proactive delegation** — let the interactive primary delegate concrete, bounded sidecars while it keeps ownership of the critical path. Before spawning, it identifies useful local work and gives the child a non-overlapping question, responsibility, package, or file set. Immediate blockers and tightly coupled work stay local; after spawning, the worker owns its scope until completion and the primary continues only its preselected independent task. If a user request or active workflow explicitly delegates blocking work, the primary yields rather than reproducing it. Off by default. When off, the permitted canonical subagent tools and profile manifest remain available for user-requested delegation or when an active skill workflow requires it. The tools accept named profiles, reasoning, per-spawn fast-mode overrides, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion and required-work state are host-driven through `[auto-subagents update]` and `[required-subagents update]` messages; required work stays asynchronous, persists its outcome, and prevents the terminal parent response until a successful completion. If launch-time restrictions prevent spawning, the interactive primary continues locally. Explicit headless `--orchestrate` remains a separate strict manager-only mode and reports unavailable delegation rather than implementing directly. Do not poll or inspect dashboards, metadata, event logs, or files solely to wait. See `/subagents` and [docs/subagents.md](docs/subagents.md) for details.
469+- **proactive delegation** — let the interactive primary delegate concrete, bounded sidecars while it keeps ownership of the critical path. Because the task and the workers it needs are unknown until the user states them, the primary routes at that point: the reply that starts the work names the parts it keeps on the critical path and the profile that owns each delegable part, and it states when nothing is worth delegating instead of forcing workers. Before spawning, it identifies useful local work and gives the child a non-overlapping question, responsibility, package, or file set. Immediate blockers and tightly coupled work stay local; after spawning, the worker owns its scope until completion and the primary continues only its preselected independent task. If a user request or active workflow explicitly delegates blocking work, the primary yields rather than reproducing it. Off by default. When off, the permitted canonical subagent tools and profile manifest remain available for user-requested delegation or when an active skill workflow requires it. The tools accept named profiles, reasoning, per-spawn fast-mode overrides, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion and required-work state are host-driven through `[auto-subagents update]` and `[required-subagents update]` messages; required work stays asynchronous, persists its outcome, and prevents the terminal parent response until a successful completion. If launch-time restrictions prevent spawning, the interactive primary continues locally. Explicit headless `--orchestrate` remains a separate strict manager-only mode and reports unavailable delegation rather than implementing directly. Do not poll or inspect dashboards, metadata, event logs, or files solely to wait. See `/subagents` and [docs/subagents.md](docs/subagents.md) for details.
470470 - **Ponytail coding mode** — include compact engineering guidance in each resolved system prompt; the guidance tells the model to apply it to coding, debugging, and review work rather than ordinary conversation. It favors understanding the real flow, small validated changes, reuse, and preserving safety checks. Enabled by default; changes apply to the next model call and persist as `ponytail_enabled` in `$ZUT_HOME/config.json`. It is included by interactive, print, stream, JSON, RPC, subagent, bot, SDK, and Zutfile agents.
471471 - **fast mode** — request the provider's fast tier where supported. It currently uses OpenAI's Fast service tier for OpenAI, OpenAI Responses, and OpenAI Codex models. Off by default; toggle it with `/fast` or `/settings`; changes apply on the next model call and persist as `fast_mode`. When a model profile is active, the value is saved in that profile and restored when switching profiles. Other providers currently return an unsupported-provider error. Fast mode may cost more and depends on the selected model/account.
472472 - **lsp in main session** — enable the built-in `lsp` tool and code diagnostics for the main agent. Enabled by default; changes persist as `lsp_enabled`.
@@ -452,7 +452,7 @@ Each child runs without a cumulative token budget or step cap: it inherits the p
452 452
453 Worktree isolation captures a patch and changed-file list without applying it. Shared children use the host checkout. The available logical references are `subagent://<id>`, `subagent://<id>/history`, `subagent://<id>/result`, and `subagent://<id>/patch`.453 Worktree isolation captures a patch and changed-file list without applying it. Shared children use the host checkout. The available logical references are `subagent://<id>`, `subagent://<id>/history`, `subagent://<id>/result`, and `subagent://<id>/patch`.
454 454
455-**Proactive delegation.** With `/settings` → **proactive delegation** on (or `/orchestrator`), the interactive primary remains the owner and implementer of the task. It delegates only concrete sidecars that can progress while the primary performs a preselected non-overlapping task; immediate blockers and tightly coupled work stay local. An active worker owns its scope until completion, so the primary does not repeat that investigation, review, testing, or implementation. If an explicit user request or workflow hands blocking work to a child, the primary yields for the host completion update instead. The canonical resident `subagent` tool remains available for user-requested delegation when the setting is off, subject to launch-time permissions. Do not poll the status action or journal files solely to wait for completion.455+**Proactive delegation.** With `/settings` → **proactive delegation** on (or `/orchestrator`), the interactive primary remains the owner and implementer of the task. Process start reveals neither the task nor which workers it needs, so it routes when the user states the request: the reply that starts the work names the parts it keeps on the critical path and the profile that owns each delegable part, before implementing or spawning anything. It delegates only concrete sidecars that can progress while the primary performs a preselected non-overlapping task; immediate blockers and tightly coupled work stay local. An active worker owns its scope until completion, so the primary does not repeat that investigation, review, testing, or implementation. If an explicit user request or workflow hands blocking work to a child, the primary yields for the host completion update instead. The canonical resident `subagent` tool remains available for user-requested delegation when the setting is off, subject to launch-time permissions. Do not poll the status action or journal files solely to wait for completion.
456 456
457 ### Named subagent profiles457 ### Named subagent profiles
458 458
@@ -466,7 +466,7 @@ Opens a dialog with every persistent setting. `up`/`down` to navigate, `enter` o
466 - **render images when supported** — draw screenshots / `read`-returned images inline using the terminal's image protocol, or fall back to a text placeholder. Auto-detected from `TERM_PROGRAM`; the toggle overrides the detection. The row is greyed out and forced off on terminals that don't speak any image protocol.466 - **render images when supported** — draw screenshots / `read`-returned images inline using the terminal's image protocol, or fall back to a text placeholder. Auto-detected from `TERM_PROGRAM`; the toggle overrides the detection. The row is greyed out and forced off on terminals that don't speak any image protocol.
467 - **terminal alerts** — emit a terminal bell when the main session stops with work that may need attention, or when an extension raises a structured alert. Enabled by default; changes apply immediately and persist as `terminal_alerts_enabled`. Terminal emulators may render the bell audibly, visually, or not at all.467 - **terminal alerts** — emit a terminal bell when the main session stops with work that may need attention, or when an extension raises a structured alert. Enabled by default; changes apply immediately and persist as `terminal_alerts_enabled`. Terminal emulators may render the bell audibly, visually, or not at all.
468 - **AI terminal titles** — after the first real prompt of a fresh interactive session, make one small hidden request to the active model and set the terminal title to `zut: <title>`. The title is limited to 40 Unicode characters, persisted with the session, restored on resume, and never added to the conversation. Enabled by default; disable it to avoid the extra model request. The toggle applies immediately, but title generation still waits for the first real prompt and never starts from startup context, resumed history, or slash commands. Persists as `terminal_title_enabled`.468 - **AI terminal titles** — after the first real prompt of a fresh interactive session, make one small hidden request to the active model and set the terminal title to `zut: <title>`. The title is limited to 40 Unicode characters, persisted with the session, restored on resume, and never added to the conversation. Enabled by default; disable it to avoid the extra model request. The toggle applies immediately, but title generation still waits for the first real prompt and never starts from startup context, resumed history, or slash commands. Persists as `terminal_title_enabled`.
469-- **proactive delegation** — let the interactive primary delegate concrete, bounded sidecars while it keeps ownership of the critical path. Before spawning, it identifies useful local work and gives the child a non-overlapping question, responsibility, package, or file set. Immediate blockers and tightly coupled work stay local; after spawning, the worker owns its scope until completion and the primary continues only its preselected independent task. If a user request or active workflow explicitly delegates blocking work, the primary yields rather than reproducing it. Off by default. When off, the permitted canonical subagent tools and profile manifest remain available for user-requested delegation or when an active skill workflow requires it. The tools accept named profiles, reasoning, per-spawn fast-mode overrides, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion and required-work state are host-driven through `[auto-subagents update]` and `[required-subagents update]` messages; required work stays asynchronous, persists its outcome, and prevents the terminal parent response until a successful completion. If launch-time restrictions prevent spawning, the interactive primary continues locally. Explicit headless `--orchestrate` remains a separate strict manager-only mode and reports unavailable delegation rather than implementing directly. Do not poll or inspect dashboards, metadata, event logs, or files solely to wait. See `/subagents` and [docs/subagents.md](docs/subagents.md) for details.469+- **proactive delegation** — let the interactive primary delegate concrete, bounded sidecars while it keeps ownership of the critical path. Because the task and the workers it needs are unknown until the user states them, the primary routes at that point: the reply that starts the work names the parts it keeps on the critical path and the profile that owns each delegable part, and it states when nothing is worth delegating instead of forcing workers. Before spawning, it identifies useful local work and gives the child a non-overlapping question, responsibility, package, or file set. Immediate blockers and tightly coupled work stay local; after spawning, the worker owns its scope until completion and the primary continues only its preselected independent task. If a user request or active workflow explicitly delegates blocking work, the primary yields rather than reproducing it. Off by default. When off, the permitted canonical subagent tools and profile manifest remain available for user-requested delegation or when an active skill workflow requires it. The tools accept named profiles, reasoning, per-spawn fast-mode overrides, and shared/worktree isolation where applicable; lifecycle and result references are persisted. Completion and required-work state are host-driven through `[auto-subagents update]` and `[required-subagents update]` messages; required work stays asynchronous, persists its outcome, and prevents the terminal parent response until a successful completion. If launch-time restrictions prevent spawning, the interactive primary continues locally. Explicit headless `--orchestrate` remains a separate strict manager-only mode and reports unavailable delegation rather than implementing directly. Do not poll or inspect dashboards, metadata, event logs, or files solely to wait. See `/subagents` and [docs/subagents.md](docs/subagents.md) for details.
470 - **Ponytail coding mode** — include compact engineering guidance in each resolved system prompt; the guidance tells the model to apply it to coding, debugging, and review work rather than ordinary conversation. It favors understanding the real flow, small validated changes, reuse, and preserving safety checks. Enabled by default; changes apply to the next model call and persist as `ponytail_enabled` in `$ZUT_HOME/config.json`. It is included by interactive, print, stream, JSON, RPC, subagent, bot, SDK, and Zutfile agents.470 - **Ponytail coding mode** — include compact engineering guidance in each resolved system prompt; the guidance tells the model to apply it to coding, debugging, and review work rather than ordinary conversation. It favors understanding the real flow, small validated changes, reuse, and preserving safety checks. Enabled by default; changes apply to the next model call and persist as `ponytail_enabled` in `$ZUT_HOME/config.json`. It is included by interactive, print, stream, JSON, RPC, subagent, bot, SDK, and Zutfile agents.
471 - **fast mode** — request the provider's fast tier where supported. It currently uses OpenAI's Fast service tier for OpenAI, OpenAI Responses, and OpenAI Codex models. Off by default; toggle it with `/fast` or `/settings`; changes apply on the next model call and persist as `fast_mode`. When a model profile is active, the value is saved in that profile and restored when switching profiles. Other providers currently return an unsupported-provider error. Fast mode may cost more and depends on the selected model/account.471 - **fast mode** — request the provider's fast tier where supported. It currently uses OpenAI's Fast service tier for OpenAI, OpenAI Responses, and OpenAI Codex models. Off by default; toggle it with `/fast` or `/settings`; changes apply on the next model call and persist as `fast_mode`. When a model profile is active, the value is saved in that profile and restored when switching profiles. Other providers currently return an unsupported-provider error. Fast mode may cost more and depends on the selected model/account.
472 - **lsp in main session** — enable the built-in `lsp` tool and code diagnostics for the main agent. Enabled by default; changes persist as `lsp_enabled`.472 - **lsp in main session** — enable the built-in `lsp` tool and code diagnostics for the main agent. Enabled by default; changes persist as `lsp_enabled`.
M docs/subagents.md +7 -0
@@ -188,6 +188,13 @@ retries it automatically.
188188 ## Delegation ownership
189189
190190 Interactive proactive delegation keeps the primary agent on the critical path.
191+Process start says nothing about the task, and the workers it needs only become
192+knowable once the user states the request. Routing therefore happens then: the
193+reply that starts the work names the parts the primary keeps on the critical
194+path and the profile that owns each delegable part, before it implements or
195+spawns anything. It states plainly when nothing is worth delegating rather than
196+forcing a worker. The routing guidance is part of the prompt only when
197+launch-time policy exposes the spawn action.
191198 Before spawning, it selects useful local work and delegates only a concrete,
192199 bounded sidecar with a non-overlapping question, responsibility, package, or
193200 file set. Immediate blockers and tightly coupled work stay local. While a child
@@ -188,6 +188,13 @@ retries it automatically.
188 ## Delegation ownership188 ## Delegation ownership
189 189
190 Interactive proactive delegation keeps the primary agent on the critical path.190 Interactive proactive delegation keeps the primary agent on the critical path.
191+Process start says nothing about the task, and the workers it needs only become
192+knowable once the user states the request. Routing therefore happens then: the
193+reply that starts the work names the parts the primary keeps on the critical
194+path and the profile that owns each delegable part, before it implements or
195+spawns anything. It states plainly when nothing is worth delegating rather than
196+forcing a worker. The routing guidance is part of the prompt only when
197+launch-time policy exposes the spawn action.
191 Before spawning, it selects useful local work and delegates only a concrete,198 Before spawning, it selects useful local work and delegates only a concrete,
192 bounded sidecar with a non-overlapping question, responsibility, package, or199 bounded sidecar with a non-overlapping question, responsibility, package, or
193 file set. Immediate blockers and tightly coupled work stay local. While a child200 file set. Immediate blockers and tightly coupled work stay local. While a child
M packages/agent/settings_store.go +14 -1
@@ -284,6 +284,15 @@ func AutoSubagentsEnabled() bool {
284284 return cfg.AutoSubagentsEnabled != nil && *cfg.AutoSubagentsEnabled
285285 }
286286
287+// ProactiveSubagentsRoutingAddendum is prepended to the interactive
288+// collaboration contract only when launch-time policy exposes the spawn
289+// action: naming a profile is meaningless when the primary cannot dispatch.
290+const ProactiveSubagentsRoutingAddendum = `Route before you work:
291+- Process start tells you nothing about the task or the workers it needs; routing happens when the user states the request you are about to work on.
292+- Name the parts you keep on the critical path and the [subagents_list] profile that owns each delegable part, before you implement or spawn.
293+- State that routing once, in the reply that starts the work, and let the tool calls that follow speak for themselves instead of narrating them.
294+- Say plainly when nothing is worth delegating instead of inventing sidecars, and spawn a clearly described general worker when no profile matches a scope.`
295+
287296 // ProactiveSubagentsSystemAddendum keeps the interactive primary agent on the
288297 // critical path and reserves delegation for genuinely parallel sidecar work.
289298 const ProactiveSubagentsSystemAddendum = `Proactive subagent delegation is enabled. You remain the primary owner and implementer of the user's task.
@@ -333,7 +342,11 @@ const ProactiveSubagentsDelegationUnavailableAddendum = `Proactive delegation is
333342 // ProactiveSubagentsSystemAddendumFor returns the interactive collaboration
334343 // contract with guidance only for manager actions exposed at launch time.
335344 func ProactiveSubagentsSystemAddendumFor(spawnToolAllowed, stopToolAllowed, resumeToolAllowed bool) string {
336- return subagentsSystemAddendumFor(ProactiveSubagentsSystemAddendum, ProactiveSubagentsDelegationUnavailableAddendum, false, spawnToolAllowed, stopToolAllowed, resumeToolAllowed)
345+ base := ProactiveSubagentsSystemAddendum
346+ if spawnToolAllowed {
347+ base = ProactiveSubagentsRoutingAddendum + "\n\n" + base
348+ }
349+ return subagentsSystemAddendumFor(base, ProactiveSubagentsDelegationUnavailableAddendum, false, spawnToolAllowed, stopToolAllowed, resumeToolAllowed)
337350 }
338351
339352 // StrictOrchestratorSystemAddendumFor returns the headless manager-only
@@ -284,6 +284,15 @@ func AutoSubagentsEnabled() bool {
284 return cfg.AutoSubagentsEnabled != nil && *cfg.AutoSubagentsEnabled284 return cfg.AutoSubagentsEnabled != nil && *cfg.AutoSubagentsEnabled
285 }285 }
286 286
287+// ProactiveSubagentsRoutingAddendum is prepended to the interactive
288+// collaboration contract only when launch-time policy exposes the spawn
289+// action: naming a profile is meaningless when the primary cannot dispatch.
290+const ProactiveSubagentsRoutingAddendum = `Route before you work:
291+- Process start tells you nothing about the task or the workers it needs; routing happens when the user states the request you are about to work on.
292+- Name the parts you keep on the critical path and the [subagents_list] profile that owns each delegable part, before you implement or spawn.
293+- State that routing once, in the reply that starts the work, and let the tool calls that follow speak for themselves instead of narrating them.
294+- Say plainly when nothing is worth delegating instead of inventing sidecars, and spawn a clearly described general worker when no profile matches a scope.`
295+
287 // ProactiveSubagentsSystemAddendum keeps the interactive primary agent on the296 // ProactiveSubagentsSystemAddendum keeps the interactive primary agent on the
288 // critical path and reserves delegation for genuinely parallel sidecar work.297 // critical path and reserves delegation for genuinely parallel sidecar work.
289 const ProactiveSubagentsSystemAddendum = `Proactive subagent delegation is enabled. You remain the primary owner and implementer of the user's task.298 const ProactiveSubagentsSystemAddendum = `Proactive subagent delegation is enabled. You remain the primary owner and implementer of the user's task.
@@ -333,7 +342,11 @@ const ProactiveSubagentsDelegationUnavailableAddendum = `Proactive delegation is
333 // ProactiveSubagentsSystemAddendumFor returns the interactive collaboration342 // ProactiveSubagentsSystemAddendumFor returns the interactive collaboration
334 // contract with guidance only for manager actions exposed at launch time.343 // contract with guidance only for manager actions exposed at launch time.
335 func ProactiveSubagentsSystemAddendumFor(spawnToolAllowed, stopToolAllowed, resumeToolAllowed bool) string {344 func ProactiveSubagentsSystemAddendumFor(spawnToolAllowed, stopToolAllowed, resumeToolAllowed bool) string {
336- return subagentsSystemAddendumFor(ProactiveSubagentsSystemAddendum, ProactiveSubagentsDelegationUnavailableAddendum, false, spawnToolAllowed, stopToolAllowed, resumeToolAllowed)345+ base := ProactiveSubagentsSystemAddendum
346+ if spawnToolAllowed {
347+ base = ProactiveSubagentsRoutingAddendum + "\n\n" + base
348+ }
349+ return subagentsSystemAddendumFor(base, ProactiveSubagentsDelegationUnavailableAddendum, false, spawnToolAllowed, stopToolAllowed, resumeToolAllowed)
337 }350 }
338 351
339 // StrictOrchestratorSystemAddendumFor returns the headless manager-only352 // StrictOrchestratorSystemAddendumFor returns the headless manager-only
M packages/agent/settings_store_test.go +56 -0
@@ -83,6 +83,62 @@ func TestSubagentPoliciesSeparateProactiveAndStrictOwnership(t *testing.T) {
8383 }
8484 }
8585
86+func TestProactiveAddendumRoutesBeforeWorkOnlyWhenSpawningIsAvailable(t *testing.T) {
87+ proactive := ProactiveSubagentsSystemAddendumFor(true, false, false)
88+ for _, want := range []string{
89+ "Route before you work:",
90+ "routing happens when the user states the request you are about to work on",
91+ "the [subagents_list] profile that owns each delegable part",
92+ "let the tool calls that follow speak for themselves",
93+ "Say plainly when nothing is worth delegating",
94+ } {
95+ if !strings.Contains(proactive, want) {
96+ t.Fatalf("proactive addendum missing %q:\n%s", want, proactive)
97+ }
98+ }
99+
100+ // Without the spawn action there is nothing to route to, and the
101+ // spawn-withheld branches in the same prompt tell the primary to stay
102+ // local; naming profiles there would contradict them.
103+ for _, tc := range []struct {
104+ name string
105+ stop, resume bool
106+ }{
107+ {name: "spawn withheld"},
108+ {name: "stop only", stop: true},
109+ {name: "resume only", resume: true},
110+ {name: "stop and resume", stop: true, resume: true},
111+ } {
112+ t.Run(tc.name, func(t *testing.T) {
113+ got := ProactiveSubagentsSystemAddendumFor(false, tc.stop, tc.resume)
114+ for _, unwanted := range []string{"Route before you work:", "[subagents_list]", "spawn a clearly described general worker"} {
115+ if strings.Contains(got, unwanted) {
116+ t.Fatalf("spawn-withheld addendum kept %q:\n%s", unwanted, got)
117+ }
118+ }
119+ if !strings.Contains(got, ProactiveSubagentsSystemAddendum) {
120+ t.Fatalf("spawn-withheld addendum lost the ownership contract:\n%s", got)
121+ }
122+ // Fully unavailable delegation falls back to the dedicated notice;
123+ // an enabled lifecycle action uses the shorter local-work line.
124+ fallback := "Continue non-delegated work locally."
125+ if !tc.stop && !tc.resume {
126+ fallback = ProactiveSubagentsDelegationUnavailableAddendum
127+ }
128+ if !strings.Contains(got, fallback) {
129+ t.Fatalf("spawn-withheld addendum lost its local-work fallback %q:\n%s", fallback, got)
130+ }
131+ })
132+ }
133+
134+ // Routing is the interactive collaboration step; the headless strict
135+ // contract divides scopes itself and must not inherit it.
136+ strict := StrictOrchestratorSystemAddendumFor(true, false, false)
137+ if strings.Contains(strict, "Route before you work:") || strings.Contains(strict, "[subagents_list]") {
138+ t.Fatalf("strict orchestrator inherited interactive routing guidance:\n%s", strict)
139+ }
140+}
141+
86142 func TestSubagentPoliciesHandleUnavailableDelegationByMode(t *testing.T) {
87143 proactive := ProactiveSubagentsSystemAddendumFor(false, false, false)
88144 if !strings.Contains(proactive, "Continue the user's task locally") || strings.Contains(proactive, "report this limitation") {
@@ -83,6 +83,62 @@ func TestSubagentPoliciesSeparateProactiveAndStrictOwnership(t *testing.T) {
83 }83 }
84 }84 }
85 85
86+func TestProactiveAddendumRoutesBeforeWorkOnlyWhenSpawningIsAvailable(t *testing.T) {
87+ proactive := ProactiveSubagentsSystemAddendumFor(true, false, false)
88+ for _, want := range []string{
89+ "Route before you work:",
90+ "routing happens when the user states the request you are about to work on",
91+ "the [subagents_list] profile that owns each delegable part",
92+ "let the tool calls that follow speak for themselves",
93+ "Say plainly when nothing is worth delegating",
94+ } {
95+ if !strings.Contains(proactive, want) {
96+ t.Fatalf("proactive addendum missing %q:\n%s", want, proactive)
97+ }
98+ }
99+
100+ // Without the spawn action there is nothing to route to, and the
101+ // spawn-withheld branches in the same prompt tell the primary to stay
102+ // local; naming profiles there would contradict them.
103+ for _, tc := range []struct {
104+ name string
105+ stop, resume bool
106+ }{
107+ {name: "spawn withheld"},
108+ {name: "stop only", stop: true},
109+ {name: "resume only", resume: true},
110+ {name: "stop and resume", stop: true, resume: true},
111+ } {
112+ t.Run(tc.name, func(t *testing.T) {
113+ got := ProactiveSubagentsSystemAddendumFor(false, tc.stop, tc.resume)
114+ for _, unwanted := range []string{"Route before you work:", "[subagents_list]", "spawn a clearly described general worker"} {
115+ if strings.Contains(got, unwanted) {
116+ t.Fatalf("spawn-withheld addendum kept %q:\n%s", unwanted, got)
117+ }
118+ }
119+ if !strings.Contains(got, ProactiveSubagentsSystemAddendum) {
120+ t.Fatalf("spawn-withheld addendum lost the ownership contract:\n%s", got)
121+ }
122+ // Fully unavailable delegation falls back to the dedicated notice;
123+ // an enabled lifecycle action uses the shorter local-work line.
124+ fallback := "Continue non-delegated work locally."
125+ if !tc.stop && !tc.resume {
126+ fallback = ProactiveSubagentsDelegationUnavailableAddendum
127+ }
128+ if !strings.Contains(got, fallback) {
129+ t.Fatalf("spawn-withheld addendum lost its local-work fallback %q:\n%s", fallback, got)
130+ }
131+ })
132+ }
133+
134+ // Routing is the interactive collaboration step; the headless strict
135+ // contract divides scopes itself and must not inherit it.
136+ strict := StrictOrchestratorSystemAddendumFor(true, false, false)
137+ if strings.Contains(strict, "Route before you work:") || strings.Contains(strict, "[subagents_list]") {
138+ t.Fatalf("strict orchestrator inherited interactive routing guidance:\n%s", strict)
139+ }
140+}
141+
86 func TestSubagentPoliciesHandleUnavailableDelegationByMode(t *testing.T) {142 func TestSubagentPoliciesHandleUnavailableDelegationByMode(t *testing.T) {
87 proactive := ProactiveSubagentsSystemAddendumFor(false, false, false)143 proactive := ProactiveSubagentsSystemAddendumFor(false, false, false)
88 if !strings.Contains(proactive, "Continue the user's task locally") || strings.Contains(proactive, "report this limitation") {144 if !strings.Contains(proactive, "Continue the user's task locally") || strings.Contains(proactive, "report this limitation") {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close