@zq-silk/yui 0.2.0 → 0.4.2

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 (208) hide show
  1. package/ARCHITECTURE.md +603 -133
  2. package/README.md +806 -31
  3. package/dist/agent/agent.js +2 -1
  4. package/dist/agent/argumentPolicy.js +3 -1
  5. package/dist/agent/launchEnvironment.js +106 -0
  6. package/dist/agent/managedRuntimeEnvironment.js +34 -0
  7. package/dist/brief/taskBrief.js +11 -1
  8. package/dist/cli/agentConfigurationPicker.js +287 -0
  9. package/dist/cli/commandCatalog.js +488 -60
  10. package/dist/cli/completion.js +146 -22
  11. package/dist/cli/helpRenderer.js +3 -1
  12. package/dist/cli/interactionCandidates.js +53 -15
  13. package/dist/cli/interactionPolicy.js +267 -30
  14. package/dist/cli/interactiveSelection.js +6 -2
  15. package/dist/cli/invocationRouter.js +5 -1
  16. package/dist/cli/operatorWizard.js +87 -0
  17. package/dist/cli/roleOptionCatalog.js +1 -0
  18. package/dist/cli/roleWizard.js +185 -21
  19. package/dist/cli/updateCommand.js +62 -19
  20. package/dist/cli/updateOrchestrator.js +539 -0
  21. package/dist/cli/updatePorts.js +1119 -0
  22. package/dist/cli/upgradeCommand.js +112 -0
  23. package/dist/cli.js +1420 -86
  24. package/dist/commands/agentCommands.js +146 -3
  25. package/dist/commands/configCommands.js +126 -0
  26. package/dist/commands/controllerCommands.js +365 -0
  27. package/dist/commands/globalRoleCommands.js +168 -126
  28. package/dist/commands/jobCommands.js +18 -8
  29. package/dist/commands/operatorCommands.js +159 -9
  30. package/dist/commands/profileCommands.js +203 -0
  31. package/dist/commands/projectCommands.js +650 -0
  32. package/dist/commands/roleConfiguration.js +85 -24
  33. package/dist/commands/roleRuntimeGuard.js +12 -0
  34. package/dist/commands/roleSkillValidation.js +47 -0
  35. package/dist/commands/taskActor.js +127 -0
  36. package/dist/commands/taskCommands.js +4201 -313
  37. package/dist/commands/taskCompletionGate.js +131 -0
  38. package/dist/commands/taskContextCommand.js +244 -30
  39. package/dist/commands/taskInputCommands.js +177 -59
  40. package/dist/commands/taskIntegrationCommands.js +303 -0
  41. package/dist/commands/taskOverviewCommand.js +363 -0
  42. package/dist/commands/taskRoleRuntimeStatus.js +125 -19
  43. package/dist/commands/textInput.js +15 -0
  44. package/dist/completion/completionInstaller.js +26 -22
  45. package/dist/config/yuiConfig.js +4 -3
  46. package/dist/context/dispatchContext.js +90 -38
  47. package/dist/context/roleSessionContext.js +119 -0
  48. package/dist/controller/claudeLifecycleHook.js +203 -0
  49. package/dist/controller/clientRuntime.js +408 -56
  50. package/dist/controller/codexLifecycleHook.js +108 -0
  51. package/dist/controller/controller.js +1089 -32
  52. package/dist/controller/domainIdentity.js +505 -0
  53. package/dist/controller/ephemeralResourceReaper.js +131 -0
  54. package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
  55. package/dist/controller/providerHookRunFence.js +127 -0
  56. package/dist/controller/resourceCleanupLinux.js +286 -0
  57. package/dist/controller/resourceInventory.js +531 -0
  58. package/dist/controller/resourceInventoryLinux.js +610 -0
  59. package/dist/controller/runtime.js +629 -10
  60. package/dist/controller/runtimeEventInbox.js +564 -0
  61. package/dist/controller/runtimeEventProcessor.js +248 -0
  62. package/dist/controller/runtimeLaunchCoordinator.js +477 -0
  63. package/dist/controller/sessionNotify.js +121 -78
  64. package/dist/coordination/deadlineScheduler.js +15 -0
  65. package/dist/coordination/mailboxScheduler.js +108 -0
  66. package/dist/coordination/workMailbox.js +329 -0
  67. package/dist/coordination/workMailboxQueue.js +86 -0
  68. package/dist/core/controllerClient.js +19 -5
  69. package/dist/core/controllerEndpoint.js +37 -0
  70. package/dist/core/controllerServer.js +218 -10
  71. package/dist/core/protocol.js +6 -2
  72. package/dist/decision/decision.js +2 -1
  73. package/dist/doctor/doctor.js +681 -32
  74. package/dist/domain/validation.js +53 -0
  75. package/dist/errors/cliError.js +5 -3
  76. package/dist/event/taskEvent.js +7 -3
  77. package/dist/execution/codexThreadNaming.js +160 -0
  78. package/dist/execution/executionGroup.js +579 -0
  79. package/dist/executor/agentAdapter.js +255 -40
  80. package/dist/executor/agentConfigurationCatalog.js +326 -0
  81. package/dist/executor/agentConfigurationProbe.js +506 -0
  82. package/dist/executor/agentExecutor.js +625 -10
  83. package/dist/executor/codexConfigConflict.js +290 -0
  84. package/dist/executor/effectiveLaunch.js +340 -0
  85. package/dist/executor/executorRegistry.js +238 -36
  86. package/dist/executor/fileRoleLaunchPlanner.js +550 -40
  87. package/dist/executor/turnCompletion.js +126 -0
  88. package/dist/input/inputRequest.js +30 -9
  89. package/dist/integration/changeSet.js +36 -0
  90. package/dist/integration/checkResult.js +24 -0
  91. package/dist/integration/gitIntegrationService.js +695 -0
  92. package/dist/integration/integrationAttempt.js +142 -0
  93. package/dist/interaction/operatorPresentation.js +96 -0
  94. package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
  95. package/dist/lifecycle/exactRunTerminalization.js +572 -0
  96. package/dist/lifecycle/providerLifecycleMapping.js +190 -0
  97. package/dist/lifecycle/taskRoleSessionReset.js +124 -0
  98. package/dist/message/message.js +23 -7
  99. package/dist/milestone/milestone.js +2 -1
  100. package/dist/operator/operatorSessionHistory.js +124 -0
  101. package/dist/output/agentConfigurationPresentation.js +43 -0
  102. package/dist/output/rolePresentation.js +34 -10
  103. package/dist/output/terminal.js +8 -0
  104. package/dist/output/timePresentation.js +55 -0
  105. package/dist/profile/agentProfile.js +128 -0
  106. package/dist/repository/gitWorkspace.js +578 -24
  107. package/dist/repository/project.js +213 -0
  108. package/dist/repository/taskWorkspaceCoordinator.js +392 -0
  109. package/dist/repository/taskWorkspacePreparer.js +1688 -191
  110. package/dist/review/reviewConfig.js +11 -0
  111. package/dist/review/reviewRound.js +399 -0
  112. package/dist/review/taskFinalReviewContract.js +90 -0
  113. package/dist/role/role.js +124 -23
  114. package/dist/run/agentRun.js +155 -12
  115. package/dist/run/runIdentity.js +82 -0
  116. package/dist/runtime/exactControlPlane.js +472 -0
  117. package/dist/runtime/index.js +8 -0
  118. package/dist/runtime/lifecycleReservation.js +38 -0
  119. package/dist/runtime/ports.js +11 -0
  120. package/dist/runtime/preallocatedNativeSession.js +13 -0
  121. package/dist/runtime/promptEnvelope.js +30 -0
  122. package/dist/runtime/runtimeBinding.js +31 -0
  123. package/dist/runtime/runtimeOwner.js +14 -0
  124. package/dist/runtime/sessionLaunchRequest.js +62 -0
  125. package/dist/runtime/sessionTitle.js +54 -0
  126. package/dist/runtime/taskRuntimeIsolation.js +643 -0
  127. package/dist/runtime/tmuxAdapters.js +315 -0
  128. package/dist/runtime/turnCompletion.js +3 -0
  129. package/dist/runtime/validation.js +23 -0
  130. package/dist/scheduler/activeRoleRunDelivery.js +342 -32
  131. package/dist/scheduler/activeTaskProgress.js +63 -0
  132. package/dist/scheduler/leaderFailure.js +2 -1
  133. package/dist/scheduler/leaderWakeupProcessor.js +307 -66
  134. package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
  135. package/dist/scheduler/operatorNotification.js +44 -2
  136. package/dist/scheduler/ports.js +28 -1
  137. package/dist/scheduler/roleRunLiveness.js +131 -25
  138. package/dist/scheduler/roleRunStall.js +951 -0
  139. package/dist/scheduler/taskExecutionProjection.js +544 -0
  140. package/dist/scheduler/wakeupQueue.js +3 -0
  141. package/dist/setup/setupCommand.js +302 -52
  142. package/dist/storage/compatibleTaskStore.js +102 -0
  143. package/dist/storage/migration/baseline.js +78 -0
  144. package/dist/storage/migration/classifier.js +51 -0
  145. package/dist/storage/migration/compatibleCodec.js +53 -0
  146. package/dist/storage/migration/engine.js +147 -0
  147. package/dist/storage/migration/index.js +33 -0
  148. package/dist/storage/migration/planner.js +154 -0
  149. package/dist/storage/migration/productionRegistry.js +486 -0
  150. package/dist/storage/migration/registry.js +169 -0
  151. package/dist/storage/migration/report.js +54 -0
  152. package/dist/storage/migration/types.js +31 -0
  153. package/dist/storage/storageSchema.js +147 -123
  154. package/dist/storage/storageVersions.js +11 -0
  155. package/dist/storage/taskStore.js +1793 -197
  156. package/dist/storage/upgrade/homeClassification.js +156 -0
  157. package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
  158. package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
  159. package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
  160. package/dist/storage/upgrade/recordVersionScan.js +176 -0
  161. package/dist/storage/upgrade/recordVersions.js +159 -0
  162. package/dist/storage/upgrade/switchProgress.js +80 -0
  163. package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
  164. package/dist/storage/upgrade/upgradeReceipt.js +161 -0
  165. package/dist/storage/upgradeCoordination.js +186 -0
  166. package/dist/storage/upgradeFence.js +366 -0
  167. package/dist/task/task.js +132 -26
  168. package/dist/task/taskRecordReference.js +66 -0
  169. package/dist/tmux/commandExecutor.js +75 -2
  170. package/dist/tmux/tmuxManager.js +747 -49
  171. package/dist/version.js +23 -0
  172. package/dist/web/assets/assetManifest.js +62 -0
  173. package/dist/web/assets/client/app.js +631 -0
  174. package/dist/web/assets/client/components.js +605 -0
  175. package/dist/web/assets/client/dom.js +14 -0
  176. package/dist/web/assets/client/format.js +28 -0
  177. package/dist/web/assets/client/i18n.js +494 -0
  178. package/dist/web/assets/client/markdown.js +114 -0
  179. package/dist/web/assets/client/theme.js +32 -0
  180. package/dist/web/assets/client/view.js +458 -0
  181. package/dist/web/assets/fontData.js +12 -0
  182. package/dist/web/assets/fonts.js +12 -0
  183. package/dist/web/assets/shell.js +114 -0
  184. package/dist/web/assets/styles/cards.js +135 -0
  185. package/dist/web/assets/styles/layout.js +47 -0
  186. package/dist/web/assets/styles/markdown.js +29 -0
  187. package/dist/web/assets/styles/responsive.js +39 -0
  188. package/dist/web/assets/styles/tokens.js +101 -0
  189. package/dist/web/assets/styles/widgets.js +147 -0
  190. package/dist/web/tmuxWebTerminal.js +158 -0
  191. package/dist/web/webServer.js +463 -0
  192. package/dist/web/webSnapshot.js +148 -0
  193. package/dist/workItem/workItem.js +642 -23
  194. package/dist/workspace/gitChangeSetCapture.js +86 -0
  195. package/dist/workspace/workItemChangeSetManager.js +445 -0
  196. package/dist/worktree/managedWorkspace.js +202 -0
  197. package/docs/task-local-identity.md +62 -0
  198. package/i18n/README.zh-CN.md +406 -31
  199. package/package.json +10 -2
  200. package/skills/yui-leader/SKILL.md +601 -39
  201. package/skills/yui-operator/SKILL.md +255 -34
  202. package/skills/yui-reviewer/SKILL.md +57 -0
  203. package/skills/yui-worker/SKILL.md +214 -17
  204. package/dist/commands/repositoryCommands.js +0 -86
  205. package/dist/operator/operatorContext.js +0 -66
  206. package/dist/repository/repository.js +0 -55
  207. package/dist/scheduler/archivedTaskRuntime.js +0 -12
  208. package/dist/worktree/roleWorkspace.js +0 -62
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Yui
4
4
 
5
- Yui is a local orchestrator for long-running Codex and Claude work. It keeps its control state in inspectable JSON files, lets tmux own every Agent terminal, and creates deterministic Git worktrees for repository-backed Tasks.
5
+ Yui is a local control plane for durable Codex and Claude work. It keeps control state and Project knowledge in inspectable JSON, lets tmux own native Agent terminals, and combines reusable Worker Profiles, Leader-owned delegation, explicit acceptance, and isolated Git worktrees for Project-backed Tasks.
6
6
 
7
7
  The current implementation restores the useful Role/Agent/session and CLI framework without restoring the later data-maintenance, lease, schedule, and recovery-ledger systems.
8
8
 
@@ -21,7 +21,15 @@ yui setup
21
21
  yui doctor
22
22
  ```
23
23
 
24
- `setup` is interactive. It detects installed Agent CLIs, asks which Agents to configure, selects the default and Operator Agent, confirms the Operator workspace, and offers shell-completion setup. Running it again preserves existing Tasks and Roles while allowing configuration changes.
24
+ `setup` is interactive. It detects installed Agent CLIs, asks which Agents to configure, selects the default and Operator Agent, and probes each selected CLI for its current models. It configures the Leader and Operator, then explains that the global Worker configuration is copied into new Task Roles and asks whether Worker should reuse Leader or be configured separately. Model selection is followed by that model's supported reasoning efforts. Setup also confirms the Project workspace outside Yui home and offers shell-completion setup. The picker includes the native CLI default and a custom-value option. Running setup again preserves existing Tasks, Roles, and the installation's Project workspace while allowing safe configuration changes.
25
+
26
+ Model and effort are per-Agent Role settings, so Operator, Leader, and the global Worker can use different values even when they share an Agent CLI. Interactive Role flows validate those settings against the selected Agent runtime. Worker Profile model and effort fields are provider-neutral child-execution hints and therefore remain explicit, scriptable values rather than Agent capability selections.
27
+
28
+ Setup gives every managed Agent binding the explicit `bypass` permission
29
+ strategy. Later Role updates may select `default`, `bypass`, or `configured`;
30
+ the last choice exposes that adapter's native permission enums and tool rules.
31
+
32
+ Runtime catalogs are refreshed per command and cached under Yui home. If a live probe times out or fails, Yui shows the last cache for the same Agent launch context and clearly marks it as potentially stale; without a matching cache, it offers CLI defaults and custom values. `yui agent capabilities <id>` exposes the same one-pass catalog, including models, model-specific efforts, and other runtime choices such as permissions, search availability, profiles, settings sources, and service tiers.
25
33
 
26
34
  `completion` is also interactive, with or without an explicit shell:
27
35
 
@@ -39,17 +47,193 @@ export YUI_HOME=/absolute/path/to/yui-home
39
47
  yui setup
40
48
  ```
41
49
 
42
- The home contains `schema.json`, the authoritative `state.json`, Controller discovery files, and managed worktrees. The current storage version is exact and fresh-only; the migration registry exists for future versions, but this release does not migrate older formats.
50
+ The home contains `schema.json`, the authoritative `state.json`, Project Catalog and knowledge, and Controller discovery files. Stable Project checkouts and managed worktrees live under the configured workspace, outside Yui home. Runtime storage is strict and its writer is current-only. A specifically declared, record-only older shape may be normalized into the current domain model in memory; Yui never dual-writes formats, preserves unknown old fields, or guesses an old identifier.
51
+
52
+ Every Task-owned record family allocates a monotonically increasing local ID
53
+ inside its Task. Different Tasks may therefore both contain `work-item-1`,
54
+ `agent-run-1`, or `input-1`. A managed Task session may use that short local ID
55
+ because `YUI_TASK_ID` supplies the scope. Outside a Task session, use the
56
+ qualified form `<task-id>/<local-id>`; Yui never searches every Task for a bare
57
+ ID. Commands that already take a Task explicitly, such as `task work create`
58
+ and `task integration start`, keep their subordinate IDs local to that Task.
59
+ Candidate IDs are local to their WorkItem and carry both Task and WorkItem
60
+ provenance.
61
+
62
+ Yui models three independent, monotonic storage version axes: the on-disk
63
+ `layout` (`schema.json`, `state.json`, locks), the authoritative `aggregate`
64
+ document, and the `record` axis — a `recordKind -> version` map where every
65
+ record family (WorkItem, AgentRun, ReviewRound, …) versions on its own. A
66
+ centralized compatibility framework (registry → planner → loader/engine)
67
+ covers all three. Every adjacent version transition must be declared explicitly:
68
+ a `compatible` declaration is permitted only for one record family and must
69
+ provide deterministic defaults, a strict validator for the exact old shape, and
70
+ a fresh-object normalizer into the current model; an `offline-migration`
71
+ declaration owns layout, aggregate, identity/reference, record-split, and other
72
+ semantic changes and must also have an executable migration step. A transform
73
+ without a declaration, a declaration without its required step, a future version,
74
+ or structural damage fails closed. Multi-hop is compatible only when every hop
75
+ is compatible; one offline hop selects the complete offline path. The production
76
+ registry contains the explicit aggregate `16→17` offline transition; no
77
+ historical record-family normalization is implicitly authorized. A frozen
78
+ post-baseline descriptor snapshot (versions and locators) is checked against the
79
+ current descriptor map through the same planner, so a version bump, locator
80
+ drift, or new target family cannot ship without its complete declared path.
81
+
82
+ The record axis is genuinely independent: `schema.json#/recordVersions` is the
83
+ durable per-family source of truth and raw `state.json` is structurally
84
+ cross-checked against it before the strict current loader. A target family that
85
+ the persisted manifest does not yet name is explicit version `0`; it is usable
86
+ only through a declared record-family `0->1` introduction. Absence of records in
87
+ `state.json` never promotes that family to current by itself.
88
+ `yui doctor`, staged `yui update` preflight, and `yui upgrade` therefore share
89
+ four product states:
90
+
91
+ - **current** (`USABLE`) — every axis is current.
92
+ - **compatible-old** (`COMPATIBLE`) — every older hop is an explicitly declared
93
+ record-only normalization; ordinary commands load the current domain model and
94
+ the first write emits current records plus the matching current manifest only.
95
+ - **migration-required** (`MIGRATABLE`) — at least one declared offline hop has
96
+ a complete deterministic step path.
97
+ - **unsupported** (`NEEDS_NEW_VERSION` or `CORRUPTED`) — a future version,
98
+ missing declaration/step, invalid old shape, or real structural/reference
99
+ damage. The result names the incompatible component and reason.
100
+
101
+ `yui update` stages and pins the new package side by side, then lets that exact
102
+ artifact run an internal path-specific read-only preflight against the target
103
+ Home: classification for current, strict compatible-source/current-model
104
+ validation for compatible-old, and classification plus authoritative offline
105
+ inventory for migration-required. This is deliberately distinct from user-facing
106
+ `upgrade --dry-run`: it creates no migration target or staged Home and claims no
107
+ staged-output validation, so it remains usable while the exact old Controller is
108
+ running. Current and compatible-old Homes
109
+ take the fast path: Yui captures and stops the exact old Controller, promotes the
110
+ same staged artifact, authenticates and starts the replacement Controller, and
111
+ post-verifies through the compatible loader. It does not copy, back up, rename,
112
+ or replay the Home and does not wait for Provider Sessions. Existing managed
113
+ Task Sessions keep their frozen exact executable/CLI path, Home, control digest,
114
+ Task/Run/launch/native-Session fence; replacement never retargets them through
115
+ PATH. At that managed continuity gate, package-version drift alone is accepted
116
+ because the same path now runs the activated CLI, while protocol, layout,
117
+ aggregate, path, Home, digest, and runtime-identity drift still fail closed. The
118
+ existing Session can therefore record progress and yield through the replacement
119
+ Controller without weakening the offline path's zero-Session requirement.
120
+ The first successful new write is the downgrade boundary because it is current-only.
121
+
122
+ Migration-required Homes take the offline path. Before binary activation,
123
+ Controller stop, admission fence, staging, or Home mutation, Yui re-reads an
124
+ authoritative inventory and blocks on active or in-flight Runs, live or unknown
125
+ native Sessions, pending turn completion, lifecycle mailboxes, or durable inbox
126
+ events. Stopped/history-only Sessions, a Role with no native process, and an open
127
+ Input by itself do not block. A blocker reports the total and exact available
128
+ Task/Role/Run/native-session/launch identities and reason, leaves the scene
129
+ unchanged, and tells the user to re-run `yui update` after the listed work clears;
130
+ it never kills, resets, rebinds, retries, or drains on the user's behalf.
131
+
132
+ Only that user re-run may enter the existing full migration. Once its preflight
133
+ is clear, the update parent captures and stops the exact old Controller PID. The
134
+ staged activation then runs the full migration: it places an admission fence (new
135
+ writers, CLI and Controller alike, refuse to start), waits for any writer already
136
+ admitted through `.state.lock`, and rechecks the offline inventory before staging,
137
+ validation, and switch. It then enters one shared sibling coordination boundary.
138
+ Inside it, both the runtime-lifecycle mailboxes AND the durable runtime inbox (`runtime/inbox/*`) are
139
+ checked (either non-empty blocks at `drain-incomplete`, so authoritative
140
+ not-yet-applied events are never dropped by a switch), followed by the revision
141
+ pin, complete-home copy, validation, and two-step switch.
142
+ The boundary is `<home>.upgrade-coordination.lock`, outside the Home so renames
143
+ cannot move a live lock. Inbox `publish` takes that same lock, checks the fence
144
+ and any unresolved switch-progress marker while holding it, writes the temp/link/
145
+ fsync event, and releases it. Upgrade takes the lock after the Controller drain,
146
+ proves both runtime lanes, re-pins the revision under `.state.lock`, copies the
147
+ complete Home, and keeps the coordination lock through `home -> backup` and
148
+ `staging -> home`. Thus a hook admitted before the fence either finishes before
149
+ the final snapshot (and its event is copied) or waits and receives an explicit
150
+ `UpgradeFenceError` after cutover; it can safely re-deliver and is never silently
151
+ stranded only in the backup. With no fence, the normal hook path keeps the same
152
+ durable behavior, only serialized at this boundary. **Fence acquisition is a
153
+ single atomic file create**, so two concurrent upgraders never both acquire —
154
+ exactly one wins and the other fails closed. A provably-dead owner's stale fence
155
+ is reclaimed, but the reclaim is an **atomic compare-and-delete** (it removes only
156
+ the exact stale bytes, under a lock), so a fresh live fence a racer created in the
157
+ meantime is never clobbered — two entrants can never both end up "acquired". That
158
+ reclaim lock is itself **crash-recoverable** (owner-pid + age reclaim), so a
159
+ holder that crashes mid-reclaim cannot orphan it and permanently block admission;
160
+ when a reclaim cannot be proven complete the writer fails closed rather than
161
+ falsely reporting the home writable. The coordination lock uses the same bounded
162
+ crash-recovery rule: it records an owner PID, waits only for a bounded interval,
163
+ and atomically renames aside a lock whose owner is provably dead (or whose
164
+ owner-less directory is older than the conservative acquisition window). A live
165
+ or undeterminable holder fails closed, so a crash cannot deadlock the Home and a
166
+ live writer is never evicted by a TTL. A switch-progress marker blocks hook
167
+ admission when the Home is missing or uninitialized (including a malformed
168
+ marker, because its recovery phase is unknowable); a stale marker beside an
169
+ intact Home is ignored just as the update probe ignores it after corroborating
170
+ the filesystem. The lock order is one-way — coordination
171
+ lock, then `.state.lock`; inbox writers never acquire `.state.lock` — so no
172
+ coordination cycle is introduced. An **uninitialized home** (never `yui setup`)
173
+ returns a structured "run `yui setup`" blocker rather than a silent no-op. Quiesce
174
+ **fails closed on any undeterminable signal**: a `.state.lock` that exists but
175
+ whose owner is missing/empty/non-integer/unreadable (a writer may be
176
+ mid-acquisition), or a malformed `runtime/controller.json`, is treated as an
177
+ active runtime and blocks the upgrade; only a provably-absent lock or a
178
+ clearly-dead owner is safe to proceed past.
179
+
180
+ The migration only transforms `schema.json` + `state.json`, but because the
181
+ atomic switch replaces the whole home directory, **staging carries a complete
182
+ copy of the home** — `runtime/`, `runtime/inbox/*` (authoritative), `cache/`, and
183
+ `artifacts/` are all preserved through the switch and retained in the backup, with
184
+ no silent loss (the transient `.state.lock` is the sole exception; a real home
185
+ entry that happens to share the staging directory's name is preserved too, and an
186
+ in-home staging layout is refused outright). The switch is two renames with one
187
+ non-atomic window, and **every** step after the first rename — the fsyncs and the
188
+ progress-marker writes, not just the rename — is phase-aware: a failure first
189
+ attempts an automatic rollback, and only if that rollback **also** fails is the
190
+ home reported as partially switched (a distinct `switch-ambiguous` blocker with
191
+ the exact `mv "<backup>" "<home>"` recovery) — it never falsely claims the home is
192
+ unchanged. A crash mid-switch leaves a durable marker; `yui update` recovery reads
193
+ that marker plus filesystem evidence (backup present, home missing) to name the
194
+ exact backup restore rather than a generic retry. Any other failed or blocked step
195
+ leaves the authoritative home byte-for-byte unchanged and reports the exact
196
+ blocker stage and recovery action. Only explicitly registered adjacent steps
197
+ can convert a real home; Yui never dual-reads an older schema or guesses an old
198
+ identifier. Compatible loading remains an explicit normalization contract, not
199
+ a permissive old-schema reader. See
200
+ [Task-local identity](docs/task-local-identity.md) for the current reference
201
+ contract.
202
+
203
+ Schema work across Tasks is not serialized: any Task may advance a version axis
204
+ (`layout`, `aggregate`, or a `record` family) on its own isolated branch without
205
+ waiting for another Task's schema change to land. The later-integrating branch
206
+ owns the reconciliation — rebasing onto the latest project head, resolving schema
207
+ and code conflicts, re-advancing the schema versions and record-version-map
208
+ entries the rebase requires, rebuilding and re-validating the wiring, and
209
+ re-running the isolated E2E and docs. This is a deliberate scheduling trade-off
210
+ that avoids cross-Task blocking, not an accident to repair ad hoc. The
211
+ current manifest descriptor map is re-derived against the newest head, while the
212
+ post-baseline descriptor snapshot remains frozen. If another Task later lands a
213
+ record-schema change, the integrating branch must supply the complete adjacent
214
+ path (including an explicit `0->1` introduction for a new family) and re-test to
215
+ convergence.
216
+
217
+ Setup also seeds four reusable Worker Profiles:
218
+
219
+ ```text
220
+ worker explorer implementer reviewer
221
+ ```
222
+
223
+ Profiles are versioned, provider-neutral Worker behavior templates. They hold portable prompt instructions, Skills, access expectations, and optional model and effort hints, but do not bind an Agent or own a Session or workspace. A Task Role is the Task-bound Worker instance: applying a Profile copies its portable behavior into that mutable instance, while each Agent binding keeps its own runtime configuration.
43
224
 
44
225
  ## Quick start
45
226
 
46
- Register a repository and create a Draft Task:
227
+ Bind a Project and create a Draft Task:
47
228
 
48
229
  ```sh
49
- yui repository add app /absolute/path/to/app --base main
50
- yui repository list
230
+ yui project add app /absolute/workspace/app \
231
+ --remote git@example.com:team/app.git --stable main --development main
232
+ yui project update app --alias app-cli
233
+ yui project refresh app
234
+ yui project list
51
235
 
52
- yui task create "Ship CSV export" --repository <repository-id> --base main
236
+ yui task create "Ship CSV export" --project app
53
237
  yui task update <task-id> --priority high --tags release,csv --due-at 2026-08-01T00:00:00Z
54
238
  yui task update <task-id> --clear-priority --clear-tags --clear-due-at
55
239
  yui task show <task-id>
@@ -57,37 +241,340 @@ yui task context <task-id>
57
241
  yui task activate <task-id>
58
242
  ```
59
243
 
244
+ `project refresh` is the explicit network operation for a stable Project checkout. It fetches the
245
+ configured stable branch directly from the Project remote URL and advances only through a clean,
246
+ verified fast-forward. Refresh requires matching stable and development branches, treats untracked
247
+ files as dirty, preserves ignored files, and refuses missing remotes or refs and diverged checkouts.
248
+ When the configured branch is `HEAD`, refresh resolves the remote's symbolic default branch for that
249
+ operation and requires the checkout to be on that branch; detached or mismatched checkouts fail.
250
+
60
251
  Use `task context` as the first detailed read of an existing Task. It combines the Task, Brief, active Decisions, recent Milestones, Roles, current and recent WorkItems with their Runs, recent Messages, open and resolved InputRequests, and recent Events. Terminal output keeps histories and long text compact; `yui --json task context <task-id>` returns the complete records in the top-level `data` field.
61
252
 
62
- Activation queues the first durable Leader wake. For a repository-backed Task, the Controller first creates one worktree per Role at `<YUI_HOME>/worktrees/<task-id>/<role-name>` on `yui/<task-id>/<role-name>`, then starts the Leader. Roles added later receive their own worktree before delivery.
253
+ Human-facing timestamps default to Beijing time (`Asia/Shanghai`) while durable
254
+ records and `--json` data remain UTC/RFC 3339. Inspect or change the IANA
255
+ timezone with:
256
+
257
+ ```sh
258
+ yui config show
259
+ yui config set --time-zone Europe/London
260
+ ```
261
+
262
+ WorkItem review is one global, optional rule that reuses an existing Global
263
+ Role's Agent, model, permissions, prompt, and Skills:
264
+
265
+ ```sh
266
+ yui config review set --role reviewer --trigger always
267
+ yui config review show
268
+ yui config review clear
269
+ ```
270
+
271
+ For Project-backed software delivery, use `--trigger final` to keep WorkItem
272
+ acceptance and Integration independent and run one fresh ReviewRound over the
273
+ complete frozen integrated Task candidate before completion:
274
+
275
+ ```sh
276
+ yui config review set --role reviewer --trigger final
277
+ ```
278
+
279
+ Every result entering Leader acceptance is one explicit candidate on its
280
+ existing WorkItem. The current global rule applies to the next candidate in
281
+ every existing or new Task; that candidate snapshots the rule, so later
282
+ `set`/`clear` changes do not rewrite an in-flight decision.
283
+ `always` starts a ReviewRound for every candidate, including a yielded Role Run
284
+ or a Leader-managed direct result; `leader` leaves the candidate awaiting
285
+ acceptance so the Leader can accept it directly or run
286
+ `yui task work review <task-id>/<work-item-id>`. A configured review rule therefore keeps
287
+ Leader-managed candidates awaiting a decision instead of marking them done.
288
+ `final` does not create WorkItem ReviewRounds; `task complete` queues one
289
+ Task-scoped ReviewRound after every bound Project has a committed Integration,
290
+ and re-queues a new round only when those frozen heads change. The final
291
+ Reviewer follows Project Policy/Knowledge and reports reachable, material,
292
+ actionable findings across the complete Task.
293
+ A ReviewRound freezes the Candidate's exact Git commit and creates a fresh,
294
+ ReviewRound-owned writable worktree on a unique branch. Its AgentRun may edit,
295
+ test, and optionally commit diagnostic evidence there, but never changes the
296
+ Candidate or Worker workspace and never creates another WorkItem, Candidate,
297
+ ChangeSet, or recursive review. The result wakes the Leader, who decides whether
298
+ to route evidence to the original Worker, accept, reject and redispatch that
299
+ Worker in its existing Session, review again, or request user input.
300
+ A failed review remains visible evidence and wakes the Leader, but does not
301
+ take that decision away from the Leader.
302
+ Candidate history, every ReviewRound, and the Leader decision remain grouped
303
+ under the original WorkItem. A rejected result creates a new Candidate on the
304
+ next dispatch while reusing the original execution Role, Session, and
305
+ workspace.
306
+
307
+ Yui Core supplies lifecycle and exact-scope safety; generic role Skills supply
308
+ portable collaboration behavior; Project Policy/Knowledge supplies
309
+ project-specific build, test, migration, release, and review rules; the Task
310
+ Contract supplies the current objective and acceptance. Project-backed Workers
311
+ commit and leave the Develop workspace clean before
312
+ yielding a Candidate. Yui freezes each writable Project's HEAD in the Candidate
313
+ snapshot; ReviewRound worktrees are recreated from those exact commits even if
314
+ Develop later advances during repair.
315
+
316
+ Task identity follows one bounded outcome, not the number of repositories
317
+ involved. A repository-backed Task may bind multiple Projects, each with its
318
+ own base ref. Yui exposes them under one Task workspace root:
319
+
320
+ ```text
321
+ <workspace>/tasks/<task-id>/main/
322
+ ├── backend/
323
+ ├── frontend/
324
+ └── shared-sdk/
325
+ ```
326
+
327
+ `<workspace>/tasks/<task-id>/main` is a logical multi-Project container, not a
328
+ Git repository. Each Project child is the supported Git cwd (for example
329
+ `<workspace>/tasks/<task-id>/main/yui`) and points to that Project's managed
330
+ worktree at `<workspace>/worktree/<project>/<task-id>/main`. Run Git commands
331
+ inside the relevant Project child. With one bound Project, the native Agent
332
+ starts in its managed worktree so Agent-native project configuration and Skills
333
+ are discovered normally. With multiple Projects, it starts at the logical root
334
+ and receives every Project worktree through the provider's native
335
+ additional-directory mechanism. Create all known
336
+ bindings together, or let the active Task Leader add one when the same outcome
337
+ expands:
338
+
339
+ ```sh
340
+ yui task create "Update authentication" \
341
+ --project backend --project frontend \
342
+ --base backend=develop --base frontend=main
343
+ yui task project add <task-id> shared-sdk --base main
344
+ ```
345
+
346
+ Implementation WorkItems declare the Projects they may modify. Their workspace
347
+ keeps the same relative layout, creates isolated worktrees only for that write
348
+ scope, and exposes the other Task Projects as context from Task main. Yui puts
349
+ the exact writable and context-only Project lists into the managed dispatch and
350
+ the `yui-worker` Skill requires the Agent to honor that boundary. Native Agent
351
+ permissions remain session-wide, while Profile `access` is a behavior hint,
352
+ not a provider sandbox or write grant. Every managed Role binding defaults to
353
+ `permission.strategy=bypass`, including `explorer`, so provider prompts do not
354
+ block normal work. Profiles and Skills constrain behavior; exact WorkItem or
355
+ ReviewRound scope and the matching managed workspace are the only authority to
356
+ modify Project files. A Role may instead choose `default` or `configured` and
357
+ retain any supported subset of the provider's native permission options.
358
+
359
+ Workspace ownership is independent from the executor Role. Yui persists one
360
+ owner-keyed `ManagedWorkspace` for Task main, each WorkItem Develop checkout,
361
+ each ReviewRound, and each IntegrationAttempt; dispatch attaches a snapshot.
362
+ The delivery chain is `isolate -> Candidate -> ReviewRound -> ChangeSet capture
363
+ -> Integration -> accept -> cleanup`. Review worktrees start at the frozen
364
+ Candidate commit and never become a Develop ChangeSet source.
365
+
366
+ Write scope may only expand. The Leader supplies the complete old-plus-new set
367
+ after a Worker yields and reports that another repository is required; an
368
+ existing writable Project cannot be removed:
369
+
370
+ ```sh
371
+ yui task work create <task-id> "Update contract" \
372
+ --project backend --project frontend --role implementer
373
+ yui task work scope <task-id>/<work-item-id> \
374
+ --project backend --project frontend --project shared-sdk
375
+ yui task work isolate <task-id>/<work-item-id>
376
+ yui task work reject <task-id>/<work-item-id> \
377
+ --summary "Write scope expanded; continue in the refreshed workspace."
378
+ yui task work dispatch <task-id>/<work-item-id>
379
+ yui task work capture <task-id>/<work-item-id>
380
+ yui task integration start <task-id> --project backend \
381
+ --change-set <backend-change-set-id> --check "<validation command>"
382
+ yui task integration cleanup <task-id>/<integration-id>
383
+ yui task work cleanup <task-id>/<work-item-id> --integrated
384
+ ```
385
+
386
+ `capture` records one immutable ChangeSet per modified Project. Repeat capture
387
+ at the same HEAD reuses the record; a repaired HEAD produces a new candidate.
388
+ Integration remains a single-Project Git transaction, so the Leader integrates
389
+ each Project independently. Acceptance succeeds only after every modified
390
+ Project's latest candidate is integrated. Yui refuses integrated cleanup while
391
+ any result remains unintegrated. Use `--abandon` only for deliberate discard.
392
+ Dirty worktrees are retained. Native Agent Sessions may be scoped to their
393
+ launch directory, so Yui retires a stopped Role Session whenever the Role moves
394
+ between Task main and an isolated WorkItem worktree. The next dispatch starts a
395
+ Session in the new workspace while durable Yui records preserve context.
63
396
 
64
397
  Submit information through Operator:
65
398
 
66
399
  ```sh
67
400
  yui operator submit "Compare CSV and JSON compatibility" --task <task-id>
68
401
  yui operator submit "Investigate a smaller cache design"
402
+ yui operator list
403
+ yui operator resume
404
+ yui operator resume --last
405
+ yui operator new
69
406
  yui operator enter
70
407
  ```
71
408
 
409
+ When a Task Role's current native Session cannot continue, reset it by intent:
410
+
411
+ ```sh
412
+ yui task role reset <task-id> <role> --reason "<why this generation cannot continue>"
413
+ ```
414
+
415
+ Yui derives the current Run, Agent, launch, receipt, and native Session from its
416
+ own records. It fails only that exact active Run (and its execution WorkItem),
417
+ stores the current Session as broken history, and asks the Controller to stop
418
+ only the Role-owned runtime. The command never creates a Candidate, accepts
419
+ work, or completes the Task. While cleanup is pending, `task role status` and
420
+ `task context` block a fresh launch. Existing messages, reviews, and delivery
421
+ history remain durable.
422
+
72
423
  Without `--task`, `operator submit` creates a new Draft. Drafts accept planning changes but must be activated before Agent execution.
424
+ Operator resolves every request against the Project catalog and existing Task
425
+ context. Follow-up requirements, fixes, reviews, and questions for the same
426
+ bounded outcome stay in that Task even when they involve multiple Projects.
427
+ A distinct outcome, ownership boundary, or lifecycle creates a separate Task.
428
+ Features, bugs, and questions use the same
429
+ Task/WorkItem model rather than separate workflow types.
430
+ `operator list` shows recent conversations in fixed most-recently-updated order using
431
+ their Agent and readable title or preview; native provider session IDs remain
432
+ internal. Until an adapter supplies that metadata, Yui shows the provider plus
433
+ a stable short Yui reference so untitled conversations remain distinguishable.
434
+ `operator resume` opens the same lightweight numbered list, while
435
+ `--last` resumes the newest entry directly. `operator new` starts a clean
436
+ conversation and preserves the previous one in history.
437
+
438
+ Create a Task-bound Worker instance from the configured global Worker, apply a
439
+ Profile, and dispatch a WorkItem:
440
+
441
+ ```sh
442
+ yui role show worker
443
+ yui task role add <task-id> implementer --profile implementer
444
+ yui task role show <task-id> implementer
445
+
446
+ yui task work create <task-id> "Implement the exporter" \
447
+ --project app --role implementer
448
+ yui task work isolate <task-id>/<work-item-id>
449
+ yui task work dispatch <task-id>/<work-item-id> --input "Implement and run focused tests"
450
+ ```
73
451
 
74
- Add a Worker and dispatch a WorkItem:
452
+ Permission is one adapter-specific enum configuration on each Agent binding:
453
+ `default` follows the provider, `bypass` compiles the provider's supported
454
+ bypass flag, and `configured` retains whichever native options are explicitly
455
+ set. Codex options are `sandbox` and `approval`; Claude options are `mode`,
456
+ `allowedTools`, and `disallowedTools`. Provider permission is independent from
457
+ Profile behavior and Project write authority: only an exact WorkItem scope and
458
+ matching managed workspace grant normal Project writes. A ReviewRound is the only non-WorkItem write
459
+ purpose and must match its Run, reviewRoundId, frozen base, and
460
+ ReviewRound-owned workspace; every mismatch fails closed. Its diagnostic commit
461
+ is visible history but is
462
+ explicitly rejected by capture, ChangeSet, Integration, and acceptance paths.
463
+ Review yield keeps the same exact `--summary-file -` command, but its stdin is
464
+ the Reviewer's complete free-form Markdown or JSON report. If a JSON report
465
+ includes known `checks` or `evidenceCommit` fields, Yui records them as
466
+ structured evidence and verifies the reported commit against the managed
467
+ Review branch HEAD; unknown fields remain part of the report. Dirty uncommitted
468
+ diagnosis may yield without a commit; the worktree is retained and cleanup
469
+ refuses it until it is clean.
470
+
471
+ Every Role desired launch change increments its revision and applies only to a
472
+ future launch. Each AgentRun and native Role Session stores the complete actual
473
+ agent, adapter, model, effort, Profile access intent, exact writable Projects,
474
+ permission strategy and native options, workspace, context, and source desired revision. Updating,
475
+ switching, or clearing Role overrides never
476
+ hot-mutates an existing process. `task context`, Role views, Run history,
477
+ Events, and Web show desired/effective revisions, Profile intent, permission, and
478
+ pending next-launch drift.
479
+
480
+ Both Codex and Claude deliver a managed Run only through its exact injected
481
+ stdin-yield command. A final assistant message alone is not a durable handoff;
482
+ permission denial, a missing or wrong Run yield, and StopFailure fail closed.
483
+
484
+ The Worker delivers its current Run explicitly:
75
485
 
76
486
  ```sh
77
- yui task role add <task-id> implementer --agent codex
78
- yui task role list <task-id>
487
+ yui task run yield <task-id>/<run-id> --summary-file - <<'YUI_SUMMARY'
488
+ Implemented the exporter; focused tests pass
489
+ YUI_SUMMARY
490
+ ```
491
+
492
+ Yield completes the AgentRun, submits the WorkItem for Leader review, appends
493
+ the result message, and queues the Leader. It does not accept the WorkItem. A
494
+ Leader never wakes itself; any pending Operator or Worker wake remains durable
495
+ until the Leader is idle.
496
+
497
+ If the outcome cannot be determined, label the handoff `uncertain`,
498
+ `incomplete`, `blocked`, or `requiring Leader judgment` and submit the most
499
+ complete truthful identities, actions, repository state, checks and errors,
500
+ lifecycle boundary, unfinished work, open decisions, risks, confidence, and
501
+ bounded next options. Yield records immutable Run/Candidate or Review evidence
502
+ only; it does not imply acceptance, WorkItem completion, ChangeSet capture,
503
+ Integration, or Task completion.
79
504
 
80
- yui task work create <task-id> "Implement the exporter" --role implementer
81
- yui task work dispatch <work-item-id> --input "Implement and run focused tests"
505
+ For bounded work, the Leader owns a roleless WorkItem and may execute it
506
+ directly or create a native subagent through the current Agent conversation:
507
+
508
+ ```sh
509
+ yui task work create <task-id> "Review the implementation" \
510
+ --objective "Return source-backed findings" \
511
+ --accept "Every finding identifies an affected path"
512
+ yui task work update <task-id>/<work-item-id> running
513
+ yui profile show reviewer
82
514
  ```
83
515
 
84
- The Worker completes its current Run explicitly:
516
+ Subagent creation and result delivery happen inside the Leader's native Agent
517
+ runtime; there is no Yui subagent launch command and Yui does not manage the
518
+ child Session. The Leader must select and read an explicit Worker Profile,
519
+ using `worker` when no specialist fits, and include its revision, instructions,
520
+ Skills, access expectations, validation, and supported model/effort hints in
521
+ the child brief. Agent bindings on Task Roles are ignored: the child inherits
522
+ the Leader Agent, credentials, and conversation context. The Leader reviews the
523
+ returned result and records the actual execution facts:
85
524
 
86
525
  ```sh
87
- yui task run yield <run-id> --summary "Implemented the exporter; focused tests pass"
526
+ yui task work update <task-id>/<work-item-id> done \
527
+ --summary "executor=subagent; profile=reviewer@3; model=inherited; round=1; result=reviewed; checks=npm test passed"
88
528
  ```
89
529
 
90
- Yield atomically completes the Run and WorkItem, appends the result message, and queues the Leader. A Leader never wakes itself; any already-pending Operator or Worker wake remains durable until the Leader is idle.
530
+ Use `inherited` or `unknown` when the native runtime does not expose an actual
531
+ model or effort; do not guess. The three supported paths remain deliberately
532
+ small: Leader direct execution, a conversation-native subagent, or a Task Role
533
+ AgentRun when work needs its own provider, credentials, interaction, or durable
534
+ Session.
535
+
536
+ For an isolated Task Role result, the Leader first reviews the yielded result.
537
+ An insufficient result is rejected with feedback and redispatched in the same
538
+ workspace. An acceptable result is captured and integrated in a candidate
539
+ worktree. Checks run there, and the target advances only if its recorded HEAD
540
+ still matches:
541
+
542
+ ```sh
543
+ yui task integration start <task-id> \
544
+ --change-set <change-set-id> \
545
+ --check "npm test"
546
+ ```
547
+
548
+ Integration state stores compact check outcomes and failure diagnoses. Full stdout and stderr are streamed without truncation to `YUI_HOME/artifacts/integration-checks/...`; `task integration show` exposes the relative log path, and `task integration cleanup` removes both the candidate worktree and those logs.
549
+
550
+ Code or semantic conflicts remain blocked until that Task's Leader records a decision:
551
+
552
+ ```sh
553
+ yui task integration resolve <task-id>/<integration-id> \
554
+ --option manual-resolution \
555
+ --rationale "Preserve the public contract while combining both implementations"
556
+ yui task integration continue <task-id>/<integration-id>
557
+ ```
558
+
559
+ Worker yield is not WorkItem completion. The Leader accepts only after reviewing
560
+ the result, validations, and the latest ChangeSet integration:
561
+
562
+ ```sh
563
+ yui task work accept <task-id>/<work-item-id> --summary "Acceptance criteria met."
564
+ ```
565
+
566
+ Use `task work reject` to return an awaiting result for repair and redispatch,
567
+ and `task work retire <task>/<work> --summary "..."` to retire obsolete work,
568
+ optionally naming a replacement. WorkItem and Integration
569
+ worktrees and check logs remain available as evidence until explicit cleanup.
570
+
571
+ For long-running Tasks, the Leader keeps Yui—not a native transcript—as the
572
+ recovery authority. The Task Brief owns the overall technical approach,
573
+ including how coordinated Project changes fit together. WorkItems own the
574
+ executable per-Project modifications and acceptance checks. The Leader updates
575
+ Brief focus and Leader summary before every yield, records material choices as
576
+ Decisions, adds phase outcomes as Milestones, and promotes only cross-Task
577
+ stable facts to Project Knowledge.
91
578
 
92
579
  When an active Leader Run cannot continue without a user decision, it can create a durable InputRequest and yield its Run:
93
580
 
@@ -95,8 +582,8 @@ When an active Leader Run cannot continue without a user decision, it can create
95
582
  yui task input request <task-id> --question "Which format should be the default?" \
96
583
  --choice csv="CSV" --choice json="JSON" --blocks work-item:<work-item-id>
97
584
  yui task input list
98
- yui task input show <input-id>
99
- yui task input answer <input-id> --choice csv
585
+ yui task input show <task-id>/<input-id>
586
+ yui task input answer <task-id>/<input-id> --choice csv
100
587
  ```
101
588
 
102
589
  Requests are user-required by default and remain open until answered or cancelled. When the Agent has a safe recommendation, it may attach a choice fallback and explicit timeout:
@@ -107,9 +594,9 @@ yui task input request <task-id> --question "Which format should be the default?
107
594
  --recommend csv --timeout-seconds 300
108
595
  ```
109
596
 
110
- The recommendation is shown to the user. If no answer arrives, the first Controller scan at or after the deadline atomically applies that exact choice and queues the fixed Leader session to resume. Free-text and user-required requests never auto-resolve.
597
+ The recommendation is shown to the user. If no answer arrives, the nearest-deadline timer wakes the Controller to atomically apply that exact choice and queue the fixed Leader session to resume. Free-text and user-required requests never auto-resolve.
111
598
 
112
- `task input list` is the authoritative global open-input Inbox; add a Task ID to scope it, or `--all` to include answered and cancelled requests. The Controller also makes one receipt-backed, best-effort delivery to an already-running Operator composer. It never starts or interrupts an Operator for this notification; an absent or busy Operator falls back to the durable Inbox and is reconsidered on a later Controller scan. Answers may be submitted by the user or Operator. An open request prevents unrelated pending wakes and Task completion or archival. The originating Leader may instead run `yui task input cancel <task-id> <input-id> --reason "..."`; cancellation does not self-wake it.
599
+ `task input list` is the authoritative global open-input Inbox; add a Task ID to scope it, or `--all` to include answered and cancelled requests. The Controller also makes one receipt-backed, best-effort delivery to an already-running Operator process. It never starts or interrupts an Operator for this notification; unavailable process state or a changed pane fence falls back to the durable Inbox and is reconsidered on a later Controller pass. It does not inspect or classify Agent terminal text. Answers may be submitted by the user or Operator. An open request prevents unrelated pending wakes and Task completion or archival. The originating Leader may instead run `yui task input cancel <task-id> <input-id> --reason "..."`; cancellation queues that fixed Leader session to resume.
113
600
 
114
601
  Inspect the result:
115
602
 
@@ -119,19 +606,35 @@ yui task context <task-id>
119
606
 
120
607
  Use the narrower `task work`, `task message`, `task run`, and Task Knowledge commands when you need one collection or record.
121
608
 
122
- When the requested outcome is finished, complete the Task to stop automatic Leader wakes without deleting its sessions or Role worktrees:
609
+ When the requested outcome is finished, complete the Task to stop automatic Leader wakes without deleting its sessions or Task main worktree:
123
610
 
124
611
  ```sh
125
612
  yui task complete <task-id> --summary "CSV export shipped and verified"
126
613
  yui task reopen <task-id>
127
614
  ```
128
615
 
129
- Completed Tasks reject messages, dispatch, enter, retry, and late yields until explicitly reopened. Archive remains terminal and performs tmux/worktree cleanup.
616
+ Completed Tasks reject messages, dispatch, enter, retry, and late yields until explicitly reopened, while retaining Task main for inspection or integration. Every isolated WorkItem worktree must be explicitly cleaned as integrated or abandoned before archive; that cleanup also removes its managed branch. Archive requires `--integrated` or `--abandon` to state the Task main outcome and is allowed only after Task main is clean. It removes managed worktrees but retains Task and WorkItem records. The Task main branch is retained as a recovery artifact instead of being silently deleted.
130
617
  Task lifecycle completion/selection only suggests valid source states: Draft for activate, active for complete, and completed for reopen.
131
618
 
132
619
  ## Sessions and tmux
133
620
 
134
- Yui never proxies an interactive Agent terminal. Before `operator enter`, `role enter`, or `task enter` attaches, Yui closes readline, leaves raw mode, pauses its stdin, and synchronously hands the terminal to tmux. As a result, native Codex features such as `/model`, slash-command suggestions, full-screen rendering, and key handling remain available.
621
+ tmux owns every long-lived interactive Agent process. Before `operator enter`,
622
+ `role enter`, or `task enter` attaches, Yui closes readline, leaves raw mode,
623
+ pauses its stdin, and synchronously hands the terminal to tmux. The attach uses
624
+ the real outer terminal capabilities and a clean alternate screen; mouse
625
+ scrolling stays in the Agent pane's 100,000-line tmux history instead of mixing
626
+ with the shell or IDE terminal history that preceded the attach. Native Agent
627
+ features such as `/model`, slash-command suggestions, full-screen rendering,
628
+ and key handling remain available.
629
+
630
+ tmux fixes a pane's history capacity when that pane is created. Roles created
631
+ before this limit was configured keep their earlier capacity; Yui warns on
632
+ Terminal attach and in Web so the user can exit and re-enter that Role once to
633
+ create a 100,000-line pane while retaining the native Agent conversation.
634
+
635
+ The first terminal attached to one Operator or Task tmux session is writable.
636
+ Additional Terminal or Web viewers attach read-only, preventing two surfaces
637
+ from typing into the same Agent at once.
135
638
 
136
639
  ```sh
137
640
  yui role enter <global-role>
@@ -139,31 +642,76 @@ yui task enter <task-id> [role]
139
642
  yui task role enter <task-id> <role>
140
643
  ```
141
644
 
142
- Each Role can bind multiple configured Agents, has one active Agent, and keeps a separate native session per Agent binding. Switching Agents preserves dormant sessions; switching is blocked while that Role has an active Run or native process.
645
+ Each Role, including a Task-bound Worker instance, can bind multiple configured Agents, has one active Agent, and keeps
646
+ a separate native session per Agent binding. Operator narrows this to at most
647
+ one Agent per adapter—for example, one Codex and one Claude—so its bindings are
648
+ ready-to-switch configurations rather than parallel identities. Operator can
649
+ keep multiple conversations for each binding. `operator new` and
650
+ `operator resume` reuse the single Operator tmux pane: when a process is
651
+ running, Yui asks before stopping it and switching the conversation. On a
652
+ cross-Agent switch, the saved model and effort are reused unless the user
653
+ explicitly chooses to update them.
654
+
655
+ The Role's active binding is desired state for the next compatible launch. A
656
+ running AgentRun and its native Session continue under their immutable
657
+ effective snapshot even if the Role is edited or switched. Resume is allowed
658
+ only when the complete effective snapshot and workspace remain compatible;
659
+ otherwise Yui starts a new Session after the old process has stopped and keeps
660
+ the terminal Session's immutable effective snapshot in history. Until that
661
+ process terminates, exact control-plane wakes continue through its actual
662
+ snapshot instead of applying desired drift as a hot change.
663
+
664
+ Use `yui role unbind <global-role> <agent-id>` or `yui task role unbind <task-id> <role> <agent-id>` to retire a dormant binding. The active binding and any non-stopped native session are rejected; a stopped session record is removed atomically with the binding.
143
665
 
144
666
  Claude session IDs are preallocated at launch. Managed Codex launches use Codex's structured `notify` callback; after a completed turn, the callback records the native thread ID without injecting a session-binding prompt into the model conversation.
145
667
 
668
+ Automated lifecycle and delivery decisions use structured Hook payloads,
669
+ persisted identities, tmux process state, receipts, and pane fences. Yui never
670
+ parses prompt glyphs, progress text, trust dialogs, or other Agent terminal
671
+ output to infer readiness or success. `captureRole()` remains an explicit
672
+ human-facing transcript read and has no lifecycle authority.
673
+
674
+ 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.
675
+
676
+ 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. Managed Codex sessions also require exclusive ownership of the structured `notify` callback that records native Turn completion; Yui refuses launch when any inspected layer already defines `notify`, so neither callback can silently replace the other. `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.
677
+
146
678
  ## Controller and failure handling
147
679
 
148
680
  One background Controller runs per `YUI_HOME`:
149
681
 
150
682
  ```sh
151
683
  yui controller status
684
+ yui controller status --all
685
+ yui controller status --all --verbose
686
+ yui controller cleanup
687
+ yui controller cleanup --all
152
688
  yui controller stop
153
689
  yui controller restart
154
690
  ```
155
691
 
692
+ `controller status` scans the current `YUI_HOME` without starting a Controller. It
693
+ shows a bounded summary of the current Controller, owned Agent sessions, residual
694
+ resources, and live anomalies. `--all` also discovers other same-user Yui homes
695
+ from running processes; `--verbose` expands the resource details. `--json`
696
+ returns the complete typed snapshot even when the human view is abbreviated.
697
+
698
+ `controller cleanup` is interactive and never selects active Task or Role
699
+ resources. It separates safe and review-required candidates, confirms live
700
+ process cleanup explicitly, and revalidates process, tmux pane, and socket
701
+ identity immediately before acting. Partial failures are reported without
702
+ hiding the resources that remain. Use `--all` to include discovered Yui homes.
703
+
156
704
  `controller restart` replaces the Controller process and its scheduler/socket services with the currently installed Yui version. It does not stop or restart managed tmux/Agent sessions.
157
705
 
158
- Its full reconciliation pass runs every 30 seconds by default; durable state changes still request an immediate pass. The retained loop is:
706
+ Its recovery reconciliation runs every 120 seconds by default. Normal durable state changes enqueue a Task, Role, or Operator key and return immediately; keys received in the same fixed 100 ms window trigger one non-overlapping targeted pass. Operator presentation has an independent lane, so a blocked Task workspace operation cannot delay a user question. Periodic Git/worktree work is limited to Tasks with durable Task-mailbox work, while active Role liveness uses one tmux inventory. A Codex turn-complete Hook writes directly to storage without starting or waiting for the Controller, then gives a legal yield/input/completion two seconds to win before closing a forgotten Run. Durable mailboxes freeze the current batch while new signals merge into the next batch; failures release the current batch for recovery. Recommended InputRequest and pending Turn deadlines share one nearest-deadline selector and therefore do not wait for the recovery interval. Explicit `task reconcile` still requests an immediate recovery pass. The retained loop is:
159
707
 
160
- 1. prepare active repository workspaces;
708
+ 1. prepare active Project Task main worktrees;
161
709
  2. stop archived Task tmux sessions and clean only clean worktrees;
162
710
  3. deliver queued Worker Runs;
163
711
  4. detect exited active Role processes;
164
712
  5. dispatch pending Leader wakes when the Leader is idle.
165
713
 
166
- Automated input is sent only through tmux, after an Agent-specific readiness check. A pane-local receipt prevents the same Run from being typed twice after a Controller retry.
714
+ Automated input is sent only through tmux. Each pass performs one non-blocking process-state readiness check; a busy startup is retried through a small bounded mailbox timer, while later busy sessions are normally woken by Codex turn-complete events. A pane-local receipt prevents the same Run from being typed twice after a Controller retry.
167
715
 
168
716
  If a Role process exits before yielding, the Controller fails that Run and running WorkItem and queues the Leader. Recovery failures are exposed through the small compatibility Jobs view:
169
717
 
@@ -172,11 +720,49 @@ yui jobs list
172
720
  yui jobs retry leader-recovery:<task-id>
173
721
  yui task reconcile <task-id>
174
722
  yui task run retry <failed-run-id>
723
+ yui task run settle <obsolete-failed-review-run-id>
175
724
  ```
176
725
 
177
726
  `jobs` is not a restored generic queue: it presents durable pending Leader wakes and Leader recovery failures only.
178
727
 
179
- Completion is the reversible execution fence. Archiving is terminal: it fails active Runs, stops the Task's tmux session, and removes each clean Role worktree. Dirty Role worktrees are preserved for deliberate cleanup.
728
+ `task run settle` is a Leader-only repair for one exact failed Reviewer Run whose
729
+ matching Task-final ReviewRound was stranded running by an older lifecycle. It
730
+ closes only an obsolete frozen candidate, preserves the Run, Round, workspace,
731
+ and evidence, and never creates a retry Round.
732
+
733
+ Completion is the reversible execution fence. Archiving is terminal and is accepted only after active work is settled: it stops the Task's tmux session and removes clean managed worktrees. Dirty worktrees keep the Task completed and are preserved for deliberate resolution.
734
+
735
+ ## Local web control room
736
+
737
+ Run the local control room on the default loopback address:
738
+
739
+ ```sh
740
+ yui web
741
+ # Yui web control room: http://127.0.0.1:4173
742
+ ```
743
+
744
+ Use `--port <port>` or `--host 127.0.0.1|::1|localhost` to change the
745
+ listener. Yui rejects non-loopback hosts because the control room exposes Task
746
+ metadata, Briefs, Roles, WorkItems, Runs, messages, Decisions, Milestones, and
747
+ InputRequests. A random token embedded in the served page protects its write
748
+ and terminal endpoints.
749
+
750
+ The Web surface can answer an open InputRequest through the same durable CLI
751
+ mutation used by Terminal users. It can also attach to the existing Operator,
752
+ Leader, or Worker tmux pane through a native xterm client. Closing the browser
753
+ terminal detaches only that tmux client; the Agent process and conversation
754
+ continue running in tmux. The Web surface does not duplicate transcripts or
755
+ maintain another session state.
756
+
757
+ The dashboard opens on an overview cockpit: four operational metrics (active
758
+ tasks, open inputs waiting on you, completed tasks, and the total), a
759
+ cross-task attention inbox that surfaces every open InputRequest with its
760
+ question and urgency so you can answer without drilling in, and the list of
761
+ currently active tasks. Selecting a task opens an anchored detail view
762
+ (Summary, Focus, Work items, Runs, Roles, History, Messages) with a sticky
763
+ tab bar that tracks the visible section.
764
+
765
+ The control room supports English and Simplified Chinese, selecting an initial locale from the browser and remembering manual changes. The theme selector switches between the dark Control Room, the light Paper Ledger, and the dark-blue Atlas themes. Both choices are stored only in browser `localStorage`; they do not modify `YUI_HOME`.
180
766
 
181
767
  ## Management commands
182
768
 
@@ -184,28 +770,217 @@ The restored management surface includes:
184
770
 
185
771
  ```sh
186
772
  yui update
187
- yui agent add|list|show|update|remove
773
+ yui upgrade [--dry-run]
774
+ yui agent add|list|show|capabilities|update|remove
188
775
  yui role add|list|show|update|remove|bind|enter
189
776
  yui role session record|replace
190
- yui repository add|list
777
+ yui project add|clone|refresh|update|discover|list|show|knowledge
191
778
  ```
192
779
 
780
+ `yui update` stages the newly published package **side by side** — it never
781
+ replaces the current global install first — then uses the staged binary to run an
782
+ internal path-specific read-only preflight against the Home. It classifies current
783
+ Homes, validates compatible-old sources into the current model in memory, and
784
+ checks the authoritative offline inventory for migration-required Homes. This
785
+ preflight does not create or validate a staged Home and is not
786
+ `upgrade --dry-run`; a full stage/validation/switch occurs only after the parent
787
+ stops the exact old Controller PID. Current and compatible-old Homes use the
788
+ no-Home-mutation fast path; migration-required Homes first require a clear
789
+ offline Run/Session/lifecycle inventory, then use the timestamped-backup switch.
790
+ Both paths promote the binary only after an exact PID-fenced old-Controller stop
791
+ and run a new-binary health check before the authenticated replacement starts.
792
+ Yui promotes the **same artifact it staged**
793
+ (binary activation pins the exact staged version, never a second bare `@latest`);
794
+ the staged version must be a **concrete semver** — a `latest`/dist-tag sentinel,
795
+ a malformed value, or a probe without a valid `{ ok:true }` envelope at exit 0 is
796
+ rejected and the stage **fails** rather than falling back to `@latest`. The health
797
+ check runs the **actually-activated** global binary and **requires** its version
798
+ to be concrete and equal to the staged one — a missing, unparseable, or mismatched
799
+ version fails closed (never skipped). Every spawned-child result must be a valid
800
+ `{ ok:true, data }` **success envelope** before its outcome is trusted (else
801
+ preflight blocks / activation is ambiguous), and a success-class outcome is
802
+ trusted **only when the process also exited 0**. The post-update health check
803
+ **parses and validates the machine-readable `yui --json doctor` envelope before
804
+ interpreting the exit status** — because `--json doctor` exits non-zero on
805
+ unhealthy storage — requires **every** expected storage check present-and-`ok`
806
+ (a missing/duplicated/malformed check fails closed), and rejects an unparseable or
807
+ self-contradictory envelope; only a valid success envelope with all storage checks
808
+ `ok` and exit 0 is healthy. On any failure it reports the exact phase and a
809
+ recovery action.
810
+
811
+ On the offline path, if storage activation cannot be resolved — the spawned staged binary was
812
+ killed or crashed after switching but before reporting — `yui update` reports a
813
+ distinct **ambiguous** result (a dedicated non-zero exit), never a false
814
+ "unchanged/recoverable". A durable completion receipt written the instant the
815
+ switch commits (a `<home>.upgrade-receipt.json` sibling, cleared on clean
816
+ success) lets it resolve the true state from receipt + backup + current schema
817
+ and print precise manual-verification steps. The receipt is trusted only when it
818
+ genuinely corresponds: it must carry this home's `homePath` and a `backupPath`
819
+ that is the expected `<home>.backup-*` real directory, so a **legacy receipt
820
+ without those fields, or one whose backup is unrelated / missing / not a
821
+ directory** is not trusted as proof of this attempt's switch — the
822
+ tool re-probes the real on-disk state instead.
823
+
824
+ Rollback boundary (precise): the managed Session launcher is an in-place
825
+ forwarder, not a versioned package pointer, so Yui does **not** claim binary+Home
826
+ dual-resource atomicity. It guarantees: (1) staging is isolated — a
827
+ stage/preflight failure leaves the old binary and Home byte-for-byte unchanged;
828
+ (2) the compatible fast path does not switch the Home; (3) the offline storage
829
+ switch is atomic with a timestamped backup and is recoverable by restoring that
830
+ backup until the new version resumes writes; (4) no auto-downgrade once the new
831
+ version has written. The offline path's non-atomic window — storage already
832
+ switched, binary promotion then fails — is reported with the exact
833
+ `mv <backup> <home>` recovery, and because the axes are version-gated the old
834
+ binary fail-closes on the new home rather than misreading it.
835
+
193
836
  Agent environment bindings store process-environment variable names, never secret values. Adapter-owned lifecycle arguments cannot be overridden through raw arguments.
194
837
 
195
838
  ## Scope
196
839
 
197
- Yui targets one trusted local user on one machine. It intentionally omits Web/API surfaces, distributed coordination, backup/import/export commands, trash/restore, derived indexes, recovery journals, runtime leases, inactivity TTLs, cooldowns, and recurring schedules.
840
+ Yui targets one trusted local user on one machine. Its Web/API surface is
841
+ loopback-only and intentionally omits remote or multi-user Web access,
842
+ distributed coordination, backup/import/export commands, trash/restore,
843
+ derived indexes, recovery journals, runtime leases, inactivity TTLs,
844
+ cooldowns, and recurring schedules. (The one internal exception is the
845
+ timestamped home backup `yui upgrade`/`yui update` takes immediately before an
846
+ atomic storage switch, purely to make that single switch recoverable — it is not
847
+ a general backup/restore facility.)
198
848
 
199
849
  See [ARCHITECTURE.md](./ARCHITECTURE.md) for persistence and scheduling details.
850
+ The reusable, user-driven acceptance plan is documented in
851
+ [Operator routing and long-running Task E2E testing](./docs/testing/operator-routing-e2e-plan.md).
200
852
 
201
853
  ## Development
202
854
 
203
855
  ```sh
856
+ npm ci
204
857
  npm run build
205
858
  npm test
206
859
  npm run lint
207
860
  ```
208
861
 
862
+ `npm test` (and `make test` / `make check`) runs the full **deterministic** test
863
+ suite: it never launches a real model and never touches the global `yui`
864
+ binary, a shared `YUI_HOME`, or a running production Session. It stays
865
+ deterministic even when launched from inside a managed Yui Session, because it
866
+ preloads `test/helpers/scrubSessionEnv.js` to strip every Yui-owned managed
867
+ runtime value from the test process, including shared `YUI_HOME`, exact Leader
868
+ action assertions, workspace projections, and Agent launch descriptors. Tests
869
+ that touch Home/CLI/Controller/tmux explicitly supply a test-created isolated
870
+ Home. The same preamble puts local refusal shims for bare `codex` and `claude`
871
+ ahead of the caller's `PATH`; Session fixtures install observable Mock Agents
872
+ inside their owned Home instead. Only a dedicated managed-identity child may
873
+ opt out. The Provider E2E tier is exempt from the shims only after its explicit
874
+ opt-in and mandatory isolation preflight path has been selected.
875
+
876
+ ### Test tiers
877
+
878
+ Yui's tests are classified into five explicit, executable tiers so a reader
879
+ never has to guess what a test actually exercised. Each tier declares whether it
880
+ creates a Session, whether it calls a real model, and whether it stands up a
881
+ disposable real runtime. Agent workflow for applying these tiers while developing
882
+ Yui lives in [`.agents/skills/develop-yui/SKILL.md`](.agents/skills/develop-yui/SKILL.md); it is
883
+ not part of the generic Leader, Worker, or Reviewer workflow:
884
+
885
+ | Tier | Session | Real model | Disposable runtime | Preflight | Opt-in |
886
+ | --- | --- | --- | --- | --- | --- |
887
+ | Unit | no | no | no | no | — |
888
+ | Isolated Integration | yes | no | yes | no | — |
889
+ | Mock Agent Session | yes | no | yes | no | — |
890
+ | Provider E2E | yes | **yes** | yes | **required** | `YUI_ALLOW_PROVIDER_E2E=1` |
891
+ | Release E2E | **no** | **no** | yes | **required** | `YUI_ALLOW_RELEASE_E2E=1` |
892
+
893
+ ```sh
894
+ make test-tier T=unit # or: npm run test:tier -- unit
895
+ npm run test:tier -- unit -- --test-name-pattern "test name"
896
+ node scripts/run-test-tier.mjs list
897
+ ```
898
+
899
+ The supported tier entrypoint always runs the canonical `npm run build` first.
900
+ It therefore works on a fresh checkout and cannot mistake a present but stale
901
+ `dist/cli.js` for current code. The raw `node --test dist/...` path remains an
902
+ unsupported bypass of that freshness boundary.
903
+
904
+ **Provider E2E is the only tier that calls a real model.** Release E2E, on its
905
+ normal path, creates no Session and calls no model — it exercises
906
+ binary/install/update/upgrade release flows against real npm/home/namespace
907
+ resources. Both tiers are **privileged and fail-closed**: they live only in
908
+ nested privileged manifests excluded from the default test glob, refuse to run
909
+ without their opt-in env var, and execute through one wrapper that registers
910
+ cleanup before observation and does not even evaluate the scenario module until
911
+ the blocking isolation preflight (`assertIsolationReady`) passes. Active-Session
912
+ observation is runner-owned and uses an all-scope Yui runtime inventory;
913
+ scenario code cannot replace it or manufacture an empty result. The preflight
914
+ requires an absolute
915
+ checkout-local launcher; a run root proven **temporary and creator-bound owned by
916
+ this run** — created via `createOwnedRunRoot` (mkdtemp + a random-token
917
+ ownership receipt) and re-proven by that exact token, with a symlink run root
918
+ refused and every path canonicalized so a symlink escape cannot pass a lexical
919
+ check; the disposable `YUI_HOME`, workspace, isolated npm prefix, and unique
920
+ runtime namespace all derived *inside that exact owned root* and **physically
921
+ fenced** against symlink escape; and an **explicit** observation that zero
922
+ production Sessions are active (missing evidence fails closed — it is never
923
+ assumed empty). No bare `yui`, `make link` symlink, shared home, arbitrary or
924
+ pre-existing foreign run root, symlinked path, or unproven Session state is
925
+ tolerated. Real-runtime teardown scans and cleans only the creator-owned Home,
926
+ uses Yui's exact process/pane/artifact identity fences, verifies the Home-derived
927
+ tmux server is absent, and refuses environment overrides that redirect
928
+ `YUI_HOME`. The reusable annotated-resource selector separately requires an
929
+ exact non-empty creator token plus matching `ephemeral-test` marker; a missing
930
+ token touches nothing and is a failed cleanup outcome. **Mock Agent Session
931
+ transport success does not prove
932
+ provider-native acceptance** — only the Provider E2E tier can record that. See
933
+ [docs/testing/test-tiers.md](./docs/testing/test-tiers.md) for the full contract.
934
+
935
+ To make user terminals use this checkout, reversibly link the user-level `yui` command:
936
+
937
+ ```sh
938
+ make link
939
+ command -v yui
940
+ yui doctor
941
+ ```
942
+
943
+ The first `make link` saves the original `yui` entry in the same user-level bin directory and replaces it with a managed symlink to this checkout. A later `make link` from another checkout only moves that managed symlink; the last checkout wins and development links never form a backup chain. Run `make link` and `make unlink` serially—do not invoke them concurrently from multiple environments or checkouts. The launcher defaults `YUI_HOME` to the active checkout's `output/dev/home`; an explicit `YUI_HOME` remains authoritative. Managed Agent launches do not depend on this global link: the Controller prepends a private launcher for its own Yui CLI and `YUI_HOME`. Run `yui controller restart` if an already-running Controller must load the new build. `make unlink` from any checkout using this implementation verifies the shared managed state and restores the one original `yui` entry.
944
+
945
+ ```sh
946
+ make unlink
947
+ ```
948
+
949
+ To run this checkout in isolation without changing the global `yui`, build only
950
+ its local launcher instead of linking:
951
+
952
+ ```sh
953
+ make install-local
954
+ ./output/dev/bin/yui doctor
955
+ ```
956
+
957
+ `make install-local` writes a self-contained launcher at `output/dev/bin/yui`
958
+ and never touches the user-level `yui` command. The launcher resolves its own
959
+ checkout and defaults `YUI_HOME` to this checkout's `output/dev/home`, so every
960
+ instance identity that Yui derives from `YUI_HOME`—Controller socket, tmux
961
+ server, and state—stays separate from any other checkout or the global install.
962
+ It is idempotent, so re-run it after pulling new code (then run
963
+ `./output/dev/bin/yui controller restart` if a Controller is already running).
964
+ Call the launcher by its absolute path for a stable per-checkout entry point;
965
+ exporting `output/dev/bin` onto `PATH` is a per-shell convenience only.
966
+
967
+ `make install-local` builds `dist/` and writes exactly one file—the launcher
968
+ itself. It does not modify `PATH` and does not create the data home, so run
969
+ `./output/dev/bin/yui setup` once before commands that need state. Because a
970
+ bare `yui` is resolved through `PATH` and not by the current directory, working
971
+ inside this checkout does not make a bare `yui` use the local launcher; it still
972
+ runs whatever `PATH` finds. Select this instance with the absolute launcher
973
+ path, or, for one interactive shell only, prepend it to `PATH`:
974
+
975
+ ```sh
976
+ export PATH="$PWD/output/dev/bin:$PATH" # this shell only; not for automation
977
+ ```
978
+
979
+ This is the recommended entry point for agents and scripts: run
980
+ `make install-local` once, then call `<checkout>/output/dev/bin/yui ...` by
981
+ absolute path from any working directory. Avoid relying on `export` persisting,
982
+ since each command runs in a fresh process.
983
+
209
984
  ## License
210
985
 
211
986
  [MIT](./LICENSE)