@zq-silk/yui 0.8.9 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/ARCHITECTURE.md +48 -46
  2. package/README.md +72 -42
  3. package/dist/cli/commandCatalog.js +16 -8
  4. package/dist/cli.js +27 -32
  5. package/dist/commands/executionAuditCommands.js +2 -2
  6. package/dist/commands/globalRoleCommands.js +0 -12
  7. package/dist/commands/sessionCommands.js +116 -0
  8. package/dist/commands/taskBaseCommands.js +1 -11
  9. package/dist/commands/taskCommands.js +123 -340
  10. package/dist/commands/taskCompletionGate.js +15 -12
  11. package/dist/commands/taskContextCommand.js +18 -11
  12. package/dist/commands/taskNextActionCommand.js +4 -5
  13. package/dist/commands/taskWorkspaceCommands.js +2 -2
  14. package/dist/controller/clientRuntime.js +65 -0
  15. package/dist/controller/fileSchedulerStoreAdapter.js +2 -49
  16. package/dist/doctor/doctor.js +16 -12
  17. package/dist/execution/executionGroup.js +0 -3
  18. package/dist/executor/agentAdapter.js +39 -42
  19. package/dist/executor/agentExecutor.js +4 -2
  20. package/dist/executor/codexConfigConflict.js +40 -16
  21. package/dist/executor/effectiveLaunch.js +33 -3
  22. package/dist/executor/fileRoleLaunchPlanner.js +15 -24
  23. package/dist/integration/deliveryObligation.js +72 -0
  24. package/dist/integration/gitIntegrationService.js +1 -1
  25. package/dist/lifecycle/exactRunTerminalization.js +12 -8
  26. package/dist/observability/orchestrationMetrics.js +12 -26
  27. package/dist/profile/agentProfile.js +1 -1
  28. package/dist/repository/taskBaseFreshness.js +5 -11
  29. package/dist/repository/taskWorkspaceCoordinator.js +2 -0
  30. package/dist/repository/taskWorkspacePreparer.js +173 -26
  31. package/dist/review/reviewRound.js +41 -24
  32. package/dist/role/role.js +0 -9
  33. package/dist/runtime/{firstProgressStopLoss.js → firstProgressAdvisory.js} +7 -21
  34. package/dist/scheduler/leaderWakeupProcessor.js +0 -25
  35. package/dist/setup/setupCommand.js +0 -5
  36. package/dist/storage/migration/productionRegistry.js +138 -0
  37. package/dist/storage/sqliteStore.js +2 -1
  38. package/dist/storage/taskStore.js +27 -27
  39. package/dist/storage/upgrade/upgradeOrchestrator.js +35 -7
  40. package/dist/task/completionReadiness.js +35 -25
  41. package/dist/task/nextAction.js +70 -78
  42. package/dist/task/task.js +12 -19
  43. package/dist/web/assets/client/components.js +3 -1
  44. package/dist/web/assets/client/i18n.js +6 -0
  45. package/dist/web/webSnapshot.js +0 -3
  46. package/i18n/README.zh-CN.md +33 -24
  47. package/package.json +1 -1
  48. package/skills/yui-leader/SKILL.md +55 -52
  49. package/skills/yui-operator/SKILL.md +31 -40
  50. package/skills/yui-reviewer/SKILL.md +18 -12
  51. package/skills/yui-worker/SKILL.md +5 -3
package/ARCHITECTURE.md CHANGED
@@ -5,31 +5,32 @@ runtimes. The user talks to one Operator. The Operator routes each request to
5
5
  the right Project and Task; that Task's Leader owns decomposition, execution
6
6
  choice, review, integration, and completion.
7
7
 
8
- ## One outcome model, two delivery paths
9
-
10
- `Task` is the bounded user outcome. Its delivery path is derived from existing
11
- fields rather than stored as another state machine: no Project bindings means
12
- `no-project`; a Project-backed Task without `requireIntegration` is `direct`;
13
- and `requireIntegration=true` is `integrated`.
14
-
15
- Direct delivery lets the Leader implement and verify a low-risk result in the
16
- managed Task main without creating a WorkItem. Integrated delivery uses
17
- `WorkItem` as its only bounded execution unit; it holds the objective,
18
- acceptance criteria, dependencies, assigned Task Role when applicable,
19
- lifecycle, and compact reviewed result.
20
-
21
- For each integrated WorkItem, a Leader chooses one of three execution paths:
22
-
23
- 1. **Direct**: the Leader executes a roleless WorkItem.
24
- 2. **Native subagent**: the Leader creates a child through its current Agent
25
- conversation. The child inherits the Leader Agent and is not a Yui entity.
26
- 3. **Task Role AgentRun**: Yui dispatches a Role-bound WorkItem to a
27
- Task-managed native Agent Session.
28
-
29
- There is no Yui subagent launcher or child-session record. A native child on
30
- direct delivery remains inside the Leader Session and Task-main boundary; a
31
- native child implementing integrated work uses the WorkItem lifecycle. Managed
32
- independent execution additionally records an AgentRun.
8
+ ## One outcome, Leader-chosen execution topology
9
+
10
+ `Task` is the bounded user outcome. Its optional `type` describes intent, not
11
+ execution topology; software Projects normally use `feature` or `bugfix`, while
12
+ other Projects may define their own types. Yui does not store a direct versus
13
+ integrated delivery mode.
14
+
15
+ A software bugfix is Leader-owned: the Leader implements and verifies it in the
16
+ managed Task main without manufacturing a WorkItem. If the scope proves to need
17
+ independent delivery owners, reclassify it as a feature before creating those
18
+ units. For a feature, the Leader judges whether the whole result is small
19
+ enough to own in the same way or large enough to need independently owned
20
+ delivery units.
21
+
22
+ `WorkItem` means one substantial, independently acceptable requirement with a
23
+ clear owner. Create multiple WorkItems only when multiple Workers can own and
24
+ advance those requirements independently, normally in parallel. Internal
25
+ implementation steps, test runs, review findings, and local fixes remain Run,
26
+ Event, report, or commit evidence under the existing Task or WorkItem; they are
27
+ not new WorkItems.
28
+
29
+ Each WorkItem may use a native subagent inside the Leader conversation or a
30
+ Task Role AgentRun backed by a durable Worker Session. There is no Yui
31
+ subagent launcher or child-session record. AgentRun is an execution attempt,
32
+ not a requirement, and repeated Runs may continue the same compatible Role
33
+ Session.
33
34
 
34
35
  ## Profiles, Roles, and Agents
35
36
 
@@ -49,8 +50,10 @@ independent execution additionally records an AgentRun.
49
50
  - A `WorkItemCandidate` is the explicit result currently awaiting Leader
50
51
  acceptance. It snapshots the WorkItem revision, summary, and either a
51
52
  yielded execution Run or a Leader-managed direct source.
52
- - `ReviewRound` records review of one candidate under the same WorkItem and
53
- references that immutable candidate. It is not another WorkItem.
53
+ - `ReviewRound` records one semantic judgment. A WorkItem Review references
54
+ that WorkItem's immutable Candidate. A Task-final Review references the
55
+ frozen Task heads directly and has no synthetic WorkItem/Candidate anchor.
56
+ It is never another WorkItem.
54
57
 
55
58
  Adding another Agent requires an explicit adapter implementation. Profiles do
56
59
  not choose adapters, own Sessions, or carry credentials.
@@ -65,7 +68,7 @@ effort, round, result, and checks.
65
68
 
66
69
  ## Lifecycle and acceptance
67
70
 
68
- Integrated Leader-direct and native-subagent WorkItem execution follows:
71
+ Native-subagent WorkItem execution follows:
69
72
 
70
73
  ```text
71
74
  todo -> running -> done | failed
@@ -92,16 +95,12 @@ Every result awaiting acceptance is stored as an explicit WorkItem candidate.
92
95
  `always` dispatches a review AgentRun for every candidate, whether it comes
93
96
  from a yielded execution Run or a Leader-managed direct result; `leader`
94
97
  leaves every candidate for the Leader to accept directly or review explicitly.
95
- `final` applies automatically only to integrated delivery: it keeps WorkItem
96
- acceptance and Integration independent, then `task complete` queues one fresh
97
- Task-scoped ReviewRound over the frozen committed heads of every bound Project.
98
- A direct Task may use a bounded native review. When a managed final Review is
99
- required, it must be promoted before Task main advances; promotion fails after
100
- a commit or delivery evidence exists so earlier work cannot lose ChangeSet
101
- provenance.
102
- Any established immutable Task-final contract or Round remains an obligation.
103
- A changed integrated head queues a new round; the previous report remains
104
- evidence. This final Reviewer evaluates the whole
98
+ `final` keeps WorkItem acceptance and Integration independent and supplies the
99
+ default Reviewer Role when the Leader decides the frozen Task result warrants
100
+ an independent final Review. An immutable Task-final contract can require that
101
+ Review. A Leader-requested Round remains evidence without becoming policy: a
102
+ later Task head does not require another Round unless the Leader requests one
103
+ or an explicit Task contract requires it. This final Reviewer evaluates the whole
105
104
  Task, so normal delivery does not pay for a complete review of every WorkItem.
106
105
  Review Runs complete only their exact ReviewRound, leave the WorkItem awaiting
107
106
  acceptance, and never trigger another review or append a Candidate. Successful
@@ -114,9 +113,12 @@ never merges it automatically.
114
113
  Roles describe Agent capability, but they do not own repository workspaces. A
115
114
  `ManagedWorkspace` is keyed by its durable owner (`Task`, `WorkItem`,
116
115
  `ReviewRound`, or `IntegrationAttempt`); an AgentRun carries only a launch
117
- snapshot. Review workspaces are fresh writable copies at the Candidate's
118
- frozen commit, so diagnostics cannot redirect Develop or become a ChangeSet
119
- source. Each ReviewRound has an independent lifecycle and explicit cleanup.
116
+ snapshot. Review workspaces are writable copies at the frozen commit, so
117
+ diagnostics cannot redirect Develop or become a ChangeSet source. Task-final
118
+ Rounds keep independent immutable records but may reassign one clean physical
119
+ workspace to the next Round for the same Reviewer Role. This lets the native
120
+ Reviewer Session continue while every Run remains bound to its exact Round and
121
+ head.
120
122
 
121
123
  Dependencies are enforced at dispatch. A Role cannot have overlapping active
122
124
  Runs, and terminal Task state fences new messages, dispatches, retries, and
@@ -147,8 +149,8 @@ Projects and Task-main context for the rest. The managed dispatch and
147
149
  writable set. Provider permission is binding configuration: every managed Role
148
150
  defaults to `bypass`, while `default` and `configured` preserve provider-native
149
151
  behavior. Provider permission and Profile access intent do not grant Project
150
- writes. A direct source write requires the exact Leader Task-main owner. An
151
- integrated source write requires an exact WorkItem write scope and matching
152
+ writes. A Leader-owned source write requires the exact Task-main owner. A
153
+ WorkItem source write requires an exact WorkItem write scope and matching
152
154
  managed workspace; a review write instead
153
155
  requires an exact ReviewRound owner and frozen Candidate base. Profiles and
154
156
  Skills constrain behavior even when provider prompts are bypassed. Provider
@@ -185,9 +187,9 @@ independently acceptable ownership.
185
187
 
186
188
  Capture at the same HEAD reuses the existing ChangeSet. A repaired HEAD creates
187
189
  a new candidate; only the latest reviewed candidate may satisfy acceptance.
188
- An isolated WorkItem cannot be accepted, or an integrated Task completed, while
189
- any writable Project's latest result is uncaptured or unintegrated. Direct
190
- completion instead requires a clean committed exact Task-main snapshot.
190
+ An isolated WorkItem cannot be accepted, or a Task with WorkItems completed,
191
+ while any writable Project's latest result is uncaptured or unintegrated.
192
+ Leader-owned completion instead requires a clean committed exact Task-main snapshot.
191
193
  Workspace roots are
192
194
  multi-Project; ChangeSets and Integration Attempts remain single-Project Git
193
195
  boundaries.
package/README.md CHANGED
@@ -144,6 +144,21 @@ Input by itself do not block. A blocker reports the total and exact available
144
144
  Task/Role/Run/native-session/launch identities and reason, leaves the scene
145
145
  unchanged, and tells the user to re-run `yui update` after the listed work clears;
146
146
  it never kills, resets, rebinds, retries, or drains on the user's behalf.
147
+ After every listed Turn or Run finishes, the user can stop all idle managed
148
+ Sessions from a normal shell and retry the update:
149
+
150
+ ```sh
151
+ yui session stop --all
152
+ yui update
153
+ ```
154
+
155
+ `session stop --all` first checks every current Session and refuses without
156
+ stopping anything while any Session still has a running Turn/Run or pending
157
+ lifecycle work. Once clear, it fences new Leader dispatch, stops and drains the
158
+ Controller, rechecks the runtime facts, then stops every exact idle Session. The
159
+ Controller remains stopped so `yui update` can enter its offline window. When the
160
+ installed version predates this command, exit every listed managed Session
161
+ manually instead; a staged new CLI cannot write the older migration-required Home.
147
162
 
148
163
  Only that user re-run may enter the existing full migration. Once its preflight
149
164
  is clear, the update parent captures and stops the exact old Controller PID. The
@@ -250,8 +265,8 @@ yui project update app --alias app-cli
250
265
  yui project refresh app
251
266
  yui project list
252
267
 
253
- yui task create "Fix CSV escaping" --project app --delivery direct
254
- yui task create "Ship CSV export" --project app --delivery integrated
268
+ yui task create "Fix CSV escaping" --project app --type bugfix
269
+ yui task create "Ship CSV export" --project app --type feature
255
270
  yui task update <task-id> --priority high --tags release,csv --due-at 2026-08-01T00:00:00Z
256
271
  yui task update <task-id> --clear-priority --clear-tags --clear-due-at
257
272
  yui task show <task-id>
@@ -259,18 +274,18 @@ yui task context <task-id>
259
274
  yui task activate <task-id>
260
275
  ```
261
276
 
262
- Project delivery is explicit but uses the existing Task schema. `direct` means
263
- the Leader implements and verifies one low-risk result on clean committed Task
264
- main; it needs no WorkItem, IntegrationAttempt, or policy-created final
265
- ReviewRound. `integrated` requires WorkItem, ChangeSet, and committed Integration
266
- evidence and remains the path for guarded, cross-Project, migration,
267
- authorization, concurrency/recovery, destructive, and release changes. Global
268
- final-review policy applies automatically only to integrated delivery. A direct
269
- Task can use a bounded native review. If an independently managed final Review
270
- is required, promote before Task main advances; promotion fails closed after a
271
- commit or delivery evidence exists so earlier work cannot lose ChangeSet
272
- provenance. Legacy `--require-integration` is an alias for
273
- `--delivery integrated`, and integrated delivery cannot be downgraded.
277
+ Task type describes intent rather than selecting an execution protocol.
278
+ Software Projects use `bugfix` or `feature`: a bugfix is Leader-owned; if it
279
+ grows into independently owned delivery requirements, reclassify it as a
280
+ feature before creating WorkItems. The Leader decides whether a feature is
281
+ small enough to deliver on Task main or large enough for independently owned WorkItems. A WorkItem is
282
+ one substantial requirement for one Worker, not a development step, test run,
283
+ review finding, or local fix. Multiple WorkItems are useful only when distinct
284
+ Workers can advance meaningful requirements independently. A WorkItem's
285
+ governing Candidate defines its delivery obligation: its current ChangeSets
286
+ must reach committed Integration or an explicit superseded queue disposition
287
+ before Task-final Review or completion. Older Candidate and ChangeSet records
288
+ remain audit evidence without keeping the Task open.
274
289
 
275
290
  `project refresh` is the explicit network operation for a stable Project checkout. It fetches the
276
291
  configured stable branch directly from the Project remote URL and advances only through a clean,
@@ -313,9 +328,9 @@ yui config show
313
328
  yui config workflow clear review
314
329
  ```
315
330
 
316
- For Project-backed software delivery, use `--trigger final` to keep WorkItem
317
- acceptance and Integration independent and run one fresh ReviewRound over the
318
- complete frozen integrated Task candidate before completion:
331
+ For Project-backed software delivery, use `--trigger final` to supply the
332
+ default Reviewer Role when the Leader decides the complete frozen Task result
333
+ needs an independent Review:
319
334
 
320
335
  ```sh
321
336
  yui config workflow set review --role reviewer --trigger final
@@ -330,10 +345,13 @@ or a Leader-managed direct result; `leader` leaves the candidate awaiting
330
345
  acceptance so the Leader can accept it directly or run
331
346
  `yui task work review <task-id>/<work-item-id>`. A configured review rule therefore keeps
332
347
  Leader-managed candidates awaiting a decision instead of marking them done.
333
- `final` does not create WorkItem ReviewRounds; `task complete` queues one
334
- Task-scoped ReviewRound after every bound Project has a committed Integration,
335
- and re-queues a new round only when those frozen heads change. The final
336
- Reviewer follows Project Policy/Knowledge and reports reachable, material,
348
+ `final` does not create WorkItem ReviewRounds or decide Task topology. The
349
+ Leader explicitly requests a Task-scoped Review, unless an immutable Task
350
+ contract requires one. The Round freezes Task main directly, so even a
351
+ Leader-owned Task with no WorkItem can be reviewed. A changed frozen head needs
352
+ a new semantic Round; the same compatible Reviewer Session may continue in its
353
+ stable workspace, while every Run remains bound to its exact Round and head.
354
+ The Reviewer follows Project Policy/Knowledge and reports reachable, material,
337
355
  actionable findings across the complete Task.
338
356
  A ReviewRound freezes the Candidate's exact Git commit and creates a fresh,
339
357
  ReviewRound-owned writable worktree on a unique branch. Its AgentRun may edit,
@@ -385,7 +403,7 @@ expands:
385
403
  yui task create "Update authentication" \
386
404
  --project backend --project frontend \
387
405
  --base backend=develop --base frontend=main \
388
- --delivery integrated
406
+ --type feature
389
407
  yui task project add <task-id> shared-sdk --base main
390
408
  ```
391
409
 
@@ -401,7 +419,9 @@ yui task base status <task-id> --refresh
401
419
  The default check is offline and uses local remote-tracking refs. `--refresh`
402
420
  is the explicit authorization to query the configured remote; Yui never
403
421
  fetches, rebases, merges, or force-pushes as a hidden side effect of Task
404
- completion.
422
+ completion. Behind, diverged, or unavailable remote state is reported as
423
+ delivery-risk evidence for the Leader; it does not replace the Leader's choice
424
+ of delivery base. A dirty Task workspace remains a completion blocker.
405
425
 
406
426
  Implementation WorkItems declare the Projects they may modify. Their workspace
407
427
  keeps the same relative layout, creates isolated worktrees only for that write
@@ -565,15 +585,15 @@ bounded next options. Yield records immutable Run/Candidate or Review evidence
565
585
  only; it does not imply acceptance, WorkItem completion, ChangeSet capture,
566
586
  Integration, or Task completion.
567
587
 
568
- For integrated bounded work, the Leader owns one roleless WorkItem and may
569
- execute it directly or create a native subagent through the current Agent
570
- conversation. Direct delivery operates on Task main without creating this
571
- WorkItem:
588
+ For one substantial feature requirement, the Leader may create a WorkItem and
589
+ give it to a native subagent or Task Role Worker. A small Task or bugfix stays
590
+ on Task main. Do not create a WorkItem merely to record implementation steps,
591
+ tests, review, or follow-up fixes:
572
592
 
573
593
  ```sh
574
- yui task work create <task-id> "Review the implementation" \
575
- --objective "Return source-backed findings" \
576
- --accept "Every finding identifies an affected path"
594
+ yui task work create <task-id> "Implement the export API" \
595
+ --objective "Deliver the independently acceptable export API requirement" \
596
+ --accept "The API contract and focused validation are complete"
577
597
  yui task work update <task-id>/<work-item-id> running
578
598
  yui config profile show reviewer
579
599
  ```
@@ -703,17 +723,18 @@ yui task complete <task-id> --summary-file delivery.txt \
703
723
  --accept-published-tree <publication-id>
704
724
  ```
705
725
 
706
- This does not weaken normal freshness checks. Yui requires the current,
726
+ This is an independent exact-tree authorization. Yui requires the current,
707
727
  unsuperseded Publication to be merged and verified, its local commit to equal
708
728
  the physical Task head, its remote commit to be ancestry-divergent, and both
709
- commits to resolve to the same exact tree. Integrated delivery also requires
710
- the latest completed Task-final Review; direct delivery does not invent one.
729
+ commits to resolve to the same exact tree. When a Task-final Review obligation
730
+ exists, completion also requires the latest semantic Round to attest the
731
+ accepted Task head; otherwise completion does not invent a ReviewRound.
711
732
  `--refresh-remote` fetches
712
733
  the remote object graph before resolving that Publication commit. For a Task
713
- governed by a durable exact final-review contract, the user/Operator command
714
- persists the exact authorization tuple and wakes the Task Leader; only that
715
- contract-capable Leader may consume it and complete the Task. Tasks without
716
- that contract retain the one-step explicit completion path. The Task event
734
+ governed by a durable final-review contract, the stored contract continues to
735
+ require its Reviewer policy, but compatible CLI and Controller updates do not
736
+ need to reproduce its historical control-plane digest. Tasks without that
737
+ contract retain the one-step explicit completion path. The Task event
717
738
  audit records the authorization and, on completion, the accepted Project,
718
739
  Publication, optional ReviewRound, both commits, and tree.
719
740
 
@@ -762,14 +783,22 @@ Global Operator and global Role sessions remain native interactive CLIs:
762
783
  yui session enter <global-role>
763
784
  ```
764
785
 
786
+ An offline Home migration requires a short maintenance window with no managed
787
+ Agent Session running. Once current Turns and Runs have finished, use
788
+ `yui session stop --all` from a normal shell. The command stops Task and global
789
+ Role Sessions only after every one is idle, and leaves all Sessions untouched
790
+ when any Role is still busy. On success it also leaves the Controller stopped;
791
+ run `yui update` next rather than resuming ordinary Task work.
792
+
765
793
  tmux fixes a pane's history capacity when that pane is created. Existing panes
766
794
  retain their configured capacity; managed runtime output remains observable in
767
795
  the Agent Host pane without becoming lifecycle or acknowledgement evidence.
768
796
 
769
- Each Role, including a Task-bound Worker instance, can bind multiple configured Agents, has one active Agent, and keeps
770
- a separate native session per Agent binding. Operator narrows this to at most
771
- one Agent per adapter—for example, one Codex and one Claude—so its bindings are
772
- ready-to-switch configurations rather than parallel identities. Operator can
797
+ Each Role, including Operator and a Task-bound Worker instance, can bind multiple
798
+ configured Agents, has one active Agent, and keeps a separate native session per
799
+ Agent binding. Multiple bindings may use the same adapter for different accounts,
800
+ models, profiles, or environment sources. They are ready-to-switch configurations,
801
+ not parallel writers: the active binding remains the unique authority. Operator can
773
802
  keep multiple conversations for each binding. `operator new` and
774
803
  `operator resume` reuse the single Operator tmux pane: when a process is
775
804
  running, Yui asks before stopping it and switching the conversation. On a
@@ -811,7 +840,7 @@ movement cannot conceal a workflow that is not advancing.
811
840
 
812
841
  Stable Role context is also launch metadata, never a bootstrap turn. Yui passes Role policy and `systemPrompt` through the Agent's native system/developer-instruction channel. Task execution Runs receive the generic Leader or Worker Skill, while review Runs receive the generic Reviewer Skill based on durable Run purpose rather than a configured Role name. These Yui-owned Role Skills define portable orchestration only. Project Skills remain ordinary versioned files in the Project and are discovered, selected, and loaded by the Agent through its native project mechanism; Yui does not scan, parse, copy, or inject them.
813
842
 
814
- Native Codex developer instructions carry compact absolute references only for Yui-owned Role Skills, which Codex reads on demand. Because `developer_instructions` is one scalar setting, Yui inspects every supported Linux Codex layer—`/etc/codex/config.toml`, the user config, the selected `$CODEX_HOME/<name>.config.toml`, project configs, and `/etc/codex/managed_config.toml`—and refuses to replace a value found in any of them. Codex sessions opened without a managed Run use Yui's structured `notify` callback for session presentation and therefore require exclusive ownership of that setting. Managed Runs instead use invocation-local Agent Driver Hooks as their sole lifecycle authority. `skills.config` is not misused because it only enables or disables already-discovered Skills. Claude receives the same Yui-owned Role Skill content from a private `0600` managed context file rather than a large or sensitive argv value; retries and resumes reuse the purpose-specific Role path. Non-Operator global Roles stay neutral and receive no Task orchestration Skill. Operator therefore opens at an empty native composer, so the user's text remains its first user message. Leader wakeups and Worker or Reviewer Run assignments remain real mailbox-delivered work messages. An adapter without a native instruction channel must reject this context rather than silently converting it into a first user prompt.
843
+ Native Codex developer instructions carry compact absolute references only for Yui-owned Role Skills, which Codex reads on demand. Yui applies this scalar as an invocation-local override, so existing user, profile, project, and system values do not make the Session unusable and the underlying config file is never mutated. A higher-precedence managed `developer_instructions` value remains a bounded launch blocker because Codex will not let invocation flags replace it. Interactive Codex Sessions apply the same rule to Yui's structured `notify` callback; Doctor reports ordinary overridden sources as context and rejects an effective managed conflict. Managed Runs instead use invocation-local Agent Driver Hooks as their sole lifecycle authority and do not claim `notify`. `skills.config` is not misused because it only enables or disables already-discovered Skills. Claude receives the same Yui-owned Role Skill content from a private `0600` managed context file rather than a large or sensitive argv value; retries and resumes reuse the purpose-specific Role path. Non-Operator global Roles stay neutral and receive no Task orchestration Skill. Operator therefore opens at an empty native composer, so the user's text remains its first user message. Leader wakeups and Worker or Reviewer Run assignments remain real mailbox-delivered work messages. An adapter without a native instruction channel must reject this context rather than silently converting it into a first user prompt.
815
844
 
816
845
  ## Controller and failure handling
817
846
 
@@ -927,6 +956,7 @@ yui config role add|list|show|update|remove|bind|unbind
927
956
  yui config profile add|list|show|update|remove|reset
928
957
  yui config completion [bash|zsh|fish]
929
958
  yui session enter|record|replace|reconcile
959
+ yui session stop --all
930
960
  yui project add|clone|refresh|update|discover|list|show|knowledge
931
961
  ```
932
962
 
@@ -199,6 +199,12 @@ const globalSessionChildren = [
199
199
  usage: "yui session replace <role> --native-id <id> --reason <text>",
200
200
  options: ["--native-id", "--reason"]
201
201
  },
202
+ {
203
+ name: "stop",
204
+ summary: "Stop all idle managed Sessions and the Controller before an offline update.",
205
+ usage: "yui session stop --all",
206
+ options: ["--all"]
207
+ },
202
208
  {
203
209
  name: "reconcile",
204
210
  summary: "Reconcile durable Session owners with native sessions.",
@@ -241,9 +247,9 @@ const taskChildren = [
241
247
  {
242
248
  name: "create",
243
249
  summary: "Create a Draft Task.",
244
- usage: "yui task create <title> [--project <project> ...] [--base <project>=<ref> ...] [--delivery <direct|integrated>] [--require-integration]",
245
- options: ["--project", "--base", "--delivery", "--require-integration"],
246
- optionValues: { "--delivery": ["direct", "integrated"] }
250
+ usage: "yui task create <title> [--type <project-defined-type>] [--project <project> ...] [--base <project>=<ref> ...]",
251
+ options: ["--type", "--project", "--base"],
252
+ optionValues: { "--type": ["feature", "bugfix"] }
247
253
  },
248
254
  {
249
255
  name: "project",
@@ -266,15 +272,15 @@ const taskChildren = [
266
272
  {
267
273
  name: "update",
268
274
  summary: "Update Task metadata.",
269
- usage: "yui task update <id> [--title <text>] [--description <text>|--clear-description] [--priority <low|medium|high|urgent>|--clear-priority] [--tags <comma-separated>|--clear-tags] [--due-at <RFC3339>|--clear-due-at] [--delivery <direct|integrated>] [--require-integration]",
275
+ usage: "yui task update <id> [--title <text>] [--type <project-defined-type>|--clear-type] [--description <text>|--clear-description] [--priority <low|medium|high|urgent>|--clear-priority] [--tags <comma-separated>|--clear-tags] [--due-at <RFC3339>|--clear-due-at]",
270
276
  options: [
271
- "--title", "--description", "--priority", "--tags", "--due-at",
277
+ "--title", "--type", "--description", "--priority", "--tags", "--due-at",
278
+ "--clear-type",
272
279
  "--clear-description", "--clear-priority", "--clear-tags", "--clear-due-at",
273
- "--delivery", "--require-integration"
274
280
  ],
275
281
  optionValues: {
276
282
  "--priority": ["low", "medium", "high", "urgent"],
277
- "--delivery": ["direct", "integrated"]
283
+ "--type": ["feature", "bugfix"]
278
284
  }
279
285
  },
280
286
  { name: "activate", summary: "Activate a Draft Task.", usage: "yui task activate <id>" },
@@ -1357,14 +1363,16 @@ export const ROOT_COMMAND = buildNode({
1357
1363
  },
1358
1364
  {
1359
1365
  name: "session",
1360
- summary: "Load and enter global Role sessions, and reconcile their durable identities.",
1366
+ summary: "Enter, stop, and reconcile managed Role sessions.",
1361
1367
  examples: [
1362
1368
  "yui session context operator --json",
1363
1369
  "yui session enter operator",
1370
+ "yui session stop --all",
1364
1371
  "yui session reconcile --report"
1365
1372
  ],
1366
1373
  sections: [
1367
1374
  { id: "global", title: "Global Role sessions", entries: ["context", "enter", "record", "replace"] },
1375
+ { id: "maintenance", title: "Maintenance", entries: ["stop"] },
1368
1376
  { id: "recovery", title: "Recovery", entries: ["reconcile"] }
1369
1377
  ],
1370
1378
  children: globalSessionChildren
package/dist/cli.js CHANGED
@@ -28,7 +28,7 @@ import { CONFIG_DOMAINS } from "./config/configCatalog.js";
28
28
  import { runConfigOverview } from "./commands/configOverview.js";
29
29
  import { parseControllerCleanupOptions, parseControllerStatusOptions, parseControllerRuntimeSnapshot, renderControllerResourceStatus, renderRuntimeIdentitySection, summarizeDurablePhysicalMismatch, runInteractiveControllerCleanup } from "./commands/controllerCommands.js";
30
30
  import { parseExecutionAuditOptions, runExecutionAuditCommand } from "./commands/executionAuditCommands.js";
31
- import { parseSessionReconcileOptions, runSessionReconcileCommand } from "./commands/sessionCommands.js";
31
+ import { parseSessionReconcileOptions, parseSessionStopOptions, runSessionReconcileCommand, runSessionStopCommand } from "./commands/sessionCommands.js";
32
32
  import { SessionOwnerReconciliation } from "./controller/sessionOwnerReconciliation.js";
33
33
  import { runJobCommand } from "./commands/jobCommands.js";
34
34
  import { runDurableJobCommand } from "./commands/durableJobCommands.js";
@@ -667,6 +667,31 @@ export async function main() {
667
667
  return;
668
668
  }
669
669
  if (resolved[0] === "session") {
670
+ if (resolved[1] === "stop") {
671
+ const options = parseSessionStopOptions(resolved.slice(2));
672
+ const result = await runSessionStopCommand({
673
+ options,
674
+ runtime: {
675
+ beginMaintenance: () => acquireHandoverLock(home),
676
+ snapshot: () => ({
677
+ candidates: schedulerStore.listRuntimeSessionCandidates(),
678
+ dormant: schedulerStore.listDormantRuntimeOwners()
679
+ }),
680
+ drainController: () => runtime.drainController(),
681
+ stopController: () => stopFileTaskController(home, {
682
+ environment: process.env
683
+ }),
684
+ startController: async () => {
685
+ await ensureFileTaskController(home, { environment: process.env });
686
+ },
687
+ stopDormantSession: (candidate) => runtime.stopDormantSession(candidate)
688
+ },
689
+ environment: process.env
690
+ });
691
+ process.exitCode = result.exitCode;
692
+ emit(result.output, false, result.data);
693
+ return;
694
+ }
670
695
  const roleOptions = {
671
696
  yuiHome: home,
672
697
  env: process.env,
@@ -1844,36 +1869,6 @@ async function prepareReviewLaneWorkspaces(taskId, reviewRoundId, store, prepare
1844
1869
  return map;
1845
1870
  }
1846
1871
  async function directTaskMainSnapshotForTaskCommand(args, store, preparer, environment, taskFinalReviewContract) {
1847
- const deliveryOption = args.indexOf("--delivery", 3);
1848
- const deliveryPromotion = args[0] === "task"
1849
- && args[1] === "update"
1850
- && args[2] !== undefined
1851
- && (args.includes("--require-integration", 3)
1852
- || (deliveryOption >= 0 && args[deliveryOption + 1] === "integrated"));
1853
- if (deliveryPromotion) {
1854
- const task = store.getTask(args[2]);
1855
- if (task === null
1856
- || (task.status !== "active" && task.status !== "draft")
1857
- || task.requireIntegration === true
1858
- || task.projectBindings.length === 0) {
1859
- return undefined;
1860
- }
1861
- const workspace = store.getTaskWorkspace(task.id);
1862
- if (task.status === "draft" && workspace === null)
1863
- return undefined;
1864
- if (workspace === null
1865
- || workspace.owner.type !== "task"
1866
- || workspace.owner.taskId !== task.id) {
1867
- throw usageError(`Task has no authoritative main workspace: ${task.id}.`);
1868
- }
1869
- try {
1870
- return await preparer.snapshotDirectTaskMain(workspace, task.projectBindings.map(({ projectId }) => projectId));
1871
- }
1872
- catch (error) {
1873
- throw usageError(`Delivery promotion Task-main verification failed for ${task.id}: `
1874
- + `${error instanceof Error ? error.message : String(error)}`);
1875
- }
1876
- }
1877
1872
  if (taskFinalReviewContract === undefined
1878
1873
  || args[0] !== "task"
1879
1874
  || args[1] !== "work"
@@ -1913,7 +1908,7 @@ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, en
1913
1908
  if (task === null || task.status !== "active" || task.projectBindings.length === 0) {
1914
1909
  return undefined;
1915
1910
  }
1916
- // Every Project-backed completion, including direct delivery, must freeze
1911
+ // Every Project-backed completion must freeze
1917
1912
  // a clean committed Task-main snapshot. Review policy only decides whether
1918
1913
  // that head also needs an independent ReviewRound.
1919
1914
  taskId = task.id;
@@ -219,7 +219,7 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
219
219
  if (orchestration.tasks.length > 0) {
220
220
  lines.push(renderTable("Task orchestration metrics", [
221
221
  { header: "Task", minWidth: 7, maxWidth: 14 },
222
- { header: "Delivery", minWidth: 8, maxWidth: 10 },
222
+ { header: "Type", minWidth: 8, maxWidth: 12 },
223
223
  { header: "Runs", minWidth: 4, maxWidth: 6 },
224
224
  { header: "WIs", minWidth: 3, maxWidth: 5 },
225
225
  { header: "Review F/D/N", minWidth: 12, maxWidth: 16 },
@@ -229,7 +229,7 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
229
229
  { header: "Advice", minWidth: 6, maxWidth: 8 }
230
230
  ], orchestration.tasks.map((task) => [
231
231
  task.taskId,
232
- task.deliveryPath,
232
+ task.taskType ?? "unspecified",
233
233
  String(task.runs.total),
234
234
  String(task.workItems),
235
235
  `${task.reviews.full}/${task.reviews.delta}/${task.reviews.nonSemantic}`,
@@ -198,7 +198,6 @@ function updateRole(args, store, options) {
198
198
  if (changesAgentConfig) {
199
199
  const agentId = parsed.one("--agent")?.trim() || role.activeAgentId;
200
200
  const agent = requireAgent(agentId, tx);
201
- assertOperatorAdapterAvailable(role, agent);
202
201
  const binding = role.agentBindings[agentId] ?? createRoleAgentBinding(definition(agent));
203
202
  bindings = { ...role.agentBindings, [agentId]: patchRoleAgentBinding(binding, parsed) };
204
203
  }
@@ -231,7 +230,6 @@ function bindRole(args, store) {
231
230
  roleName: role.name
232
231
  }, "desired Agent binding update");
233
232
  const agent = requireAgent(agentId, tx);
234
- assertOperatorAdapterAvailable(role, agent);
235
233
  const binding = role.agentBindings[agentId] ?? createRoleAgentBinding(definition(agent));
236
234
  const withBinding = updateGlobalRole(role, {
237
235
  agentBindings: { ...role.agentBindings, [agentId]: binding }
@@ -260,16 +258,6 @@ function bindRole(args, store) {
260
258
  });
261
259
  return presentRole(result.message, result.role, store);
262
260
  }
263
- function assertOperatorAdapterAvailable(role, agent) {
264
- if (role.name !== "operator" || Object.hasOwn(role.agentBindings, agent.id))
265
- return;
266
- const existing = Object.values(role.agentBindings).find((binding) => binding.adapterId === agent.adapterId);
267
- if (existing !== undefined) {
268
- throw usageError(`Operator already has a ${agent.adapterId} Agent: ${existing.agentId}. `
269
- + "Update that Agent's configuration, or activate another adapter and "
270
- + "unbind it before binding this Agent.");
271
- }
272
- }
273
261
  function removeRole(args, store) {
274
262
  const [rawName, ...rest] = args;
275
263
  const name = roleName(rawName);