@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.
- package/ARCHITECTURE.md +603 -133
- package/README.md +806 -31
- package/dist/agent/agent.js +2 -1
- package/dist/agent/argumentPolicy.js +3 -1
- package/dist/agent/launchEnvironment.js +106 -0
- package/dist/agent/managedRuntimeEnvironment.js +34 -0
- package/dist/brief/taskBrief.js +11 -1
- package/dist/cli/agentConfigurationPicker.js +287 -0
- package/dist/cli/commandCatalog.js +488 -60
- package/dist/cli/completion.js +146 -22
- package/dist/cli/helpRenderer.js +3 -1
- package/dist/cli/interactionCandidates.js +53 -15
- package/dist/cli/interactionPolicy.js +267 -30
- package/dist/cli/interactiveSelection.js +6 -2
- package/dist/cli/invocationRouter.js +5 -1
- package/dist/cli/operatorWizard.js +87 -0
- package/dist/cli/roleOptionCatalog.js +1 -0
- package/dist/cli/roleWizard.js +185 -21
- package/dist/cli/updateCommand.js +62 -19
- package/dist/cli/updateOrchestrator.js +539 -0
- package/dist/cli/updatePorts.js +1119 -0
- package/dist/cli/upgradeCommand.js +112 -0
- package/dist/cli.js +1420 -86
- package/dist/commands/agentCommands.js +146 -3
- package/dist/commands/configCommands.js +126 -0
- package/dist/commands/controllerCommands.js +365 -0
- package/dist/commands/globalRoleCommands.js +168 -126
- package/dist/commands/jobCommands.js +18 -8
- package/dist/commands/operatorCommands.js +159 -9
- package/dist/commands/profileCommands.js +203 -0
- package/dist/commands/projectCommands.js +650 -0
- package/dist/commands/roleConfiguration.js +85 -24
- package/dist/commands/roleRuntimeGuard.js +12 -0
- package/dist/commands/roleSkillValidation.js +47 -0
- package/dist/commands/taskActor.js +127 -0
- package/dist/commands/taskCommands.js +4201 -313
- package/dist/commands/taskCompletionGate.js +131 -0
- package/dist/commands/taskContextCommand.js +244 -30
- package/dist/commands/taskInputCommands.js +177 -59
- package/dist/commands/taskIntegrationCommands.js +303 -0
- package/dist/commands/taskOverviewCommand.js +363 -0
- package/dist/commands/taskRoleRuntimeStatus.js +125 -19
- package/dist/commands/textInput.js +15 -0
- package/dist/completion/completionInstaller.js +26 -22
- package/dist/config/yuiConfig.js +4 -3
- package/dist/context/dispatchContext.js +90 -38
- package/dist/context/roleSessionContext.js +119 -0
- package/dist/controller/claudeLifecycleHook.js +203 -0
- package/dist/controller/clientRuntime.js +408 -56
- package/dist/controller/codexLifecycleHook.js +108 -0
- package/dist/controller/controller.js +1089 -32
- package/dist/controller/domainIdentity.js +505 -0
- package/dist/controller/ephemeralResourceReaper.js +131 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
- package/dist/controller/providerHookRunFence.js +127 -0
- package/dist/controller/resourceCleanupLinux.js +286 -0
- package/dist/controller/resourceInventory.js +531 -0
- package/dist/controller/resourceInventoryLinux.js +610 -0
- package/dist/controller/runtime.js +629 -10
- package/dist/controller/runtimeEventInbox.js +564 -0
- package/dist/controller/runtimeEventProcessor.js +248 -0
- package/dist/controller/runtimeLaunchCoordinator.js +477 -0
- package/dist/controller/sessionNotify.js +121 -78
- package/dist/coordination/deadlineScheduler.js +15 -0
- package/dist/coordination/mailboxScheduler.js +108 -0
- package/dist/coordination/workMailbox.js +329 -0
- package/dist/coordination/workMailboxQueue.js +86 -0
- package/dist/core/controllerClient.js +19 -5
- package/dist/core/controllerEndpoint.js +37 -0
- package/dist/core/controllerServer.js +218 -10
- package/dist/core/protocol.js +6 -2
- package/dist/decision/decision.js +2 -1
- package/dist/doctor/doctor.js +681 -32
- package/dist/domain/validation.js +53 -0
- package/dist/errors/cliError.js +5 -3
- package/dist/event/taskEvent.js +7 -3
- package/dist/execution/codexThreadNaming.js +160 -0
- package/dist/execution/executionGroup.js +579 -0
- package/dist/executor/agentAdapter.js +255 -40
- package/dist/executor/agentConfigurationCatalog.js +326 -0
- package/dist/executor/agentConfigurationProbe.js +506 -0
- package/dist/executor/agentExecutor.js +625 -10
- package/dist/executor/codexConfigConflict.js +290 -0
- package/dist/executor/effectiveLaunch.js +340 -0
- package/dist/executor/executorRegistry.js +238 -36
- package/dist/executor/fileRoleLaunchPlanner.js +550 -40
- package/dist/executor/turnCompletion.js +126 -0
- package/dist/input/inputRequest.js +30 -9
- package/dist/integration/changeSet.js +36 -0
- package/dist/integration/checkResult.js +24 -0
- package/dist/integration/gitIntegrationService.js +695 -0
- package/dist/integration/integrationAttempt.js +142 -0
- package/dist/interaction/operatorPresentation.js +96 -0
- package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
- package/dist/lifecycle/exactRunTerminalization.js +572 -0
- package/dist/lifecycle/providerLifecycleMapping.js +190 -0
- package/dist/lifecycle/taskRoleSessionReset.js +124 -0
- package/dist/message/message.js +23 -7
- package/dist/milestone/milestone.js +2 -1
- package/dist/operator/operatorSessionHistory.js +124 -0
- package/dist/output/agentConfigurationPresentation.js +43 -0
- package/dist/output/rolePresentation.js +34 -10
- package/dist/output/terminal.js +8 -0
- package/dist/output/timePresentation.js +55 -0
- package/dist/profile/agentProfile.js +128 -0
- package/dist/repository/gitWorkspace.js +578 -24
- package/dist/repository/project.js +213 -0
- package/dist/repository/taskWorkspaceCoordinator.js +392 -0
- package/dist/repository/taskWorkspacePreparer.js +1688 -191
- package/dist/review/reviewConfig.js +11 -0
- package/dist/review/reviewRound.js +399 -0
- package/dist/review/taskFinalReviewContract.js +90 -0
- package/dist/role/role.js +124 -23
- package/dist/run/agentRun.js +155 -12
- package/dist/run/runIdentity.js +82 -0
- package/dist/runtime/exactControlPlane.js +472 -0
- package/dist/runtime/index.js +8 -0
- package/dist/runtime/lifecycleReservation.js +38 -0
- package/dist/runtime/ports.js +11 -0
- package/dist/runtime/preallocatedNativeSession.js +13 -0
- package/dist/runtime/promptEnvelope.js +30 -0
- package/dist/runtime/runtimeBinding.js +31 -0
- package/dist/runtime/runtimeOwner.js +14 -0
- package/dist/runtime/sessionLaunchRequest.js +62 -0
- package/dist/runtime/sessionTitle.js +54 -0
- package/dist/runtime/taskRuntimeIsolation.js +643 -0
- package/dist/runtime/tmuxAdapters.js +315 -0
- package/dist/runtime/turnCompletion.js +3 -0
- package/dist/runtime/validation.js +23 -0
- package/dist/scheduler/activeRoleRunDelivery.js +342 -32
- package/dist/scheduler/activeTaskProgress.js +63 -0
- package/dist/scheduler/leaderFailure.js +2 -1
- package/dist/scheduler/leaderWakeupProcessor.js +307 -66
- package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
- package/dist/scheduler/operatorNotification.js +44 -2
- package/dist/scheduler/ports.js +28 -1
- package/dist/scheduler/roleRunLiveness.js +131 -25
- package/dist/scheduler/roleRunStall.js +951 -0
- package/dist/scheduler/taskExecutionProjection.js +544 -0
- package/dist/scheduler/wakeupQueue.js +3 -0
- package/dist/setup/setupCommand.js +302 -52
- package/dist/storage/compatibleTaskStore.js +102 -0
- package/dist/storage/migration/baseline.js +78 -0
- package/dist/storage/migration/classifier.js +51 -0
- package/dist/storage/migration/compatibleCodec.js +53 -0
- package/dist/storage/migration/engine.js +147 -0
- package/dist/storage/migration/index.js +33 -0
- package/dist/storage/migration/planner.js +154 -0
- package/dist/storage/migration/productionRegistry.js +486 -0
- package/dist/storage/migration/registry.js +169 -0
- package/dist/storage/migration/report.js +54 -0
- package/dist/storage/migration/types.js +31 -0
- package/dist/storage/storageSchema.js +147 -123
- package/dist/storage/storageVersions.js +11 -0
- package/dist/storage/taskStore.js +1793 -197
- package/dist/storage/upgrade/homeClassification.js +156 -0
- package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
- package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
- package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
- package/dist/storage/upgrade/recordVersionScan.js +176 -0
- package/dist/storage/upgrade/recordVersions.js +159 -0
- package/dist/storage/upgrade/switchProgress.js +80 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
- package/dist/storage/upgrade/upgradeReceipt.js +161 -0
- package/dist/storage/upgradeCoordination.js +186 -0
- package/dist/storage/upgradeFence.js +366 -0
- package/dist/task/task.js +132 -26
- package/dist/task/taskRecordReference.js +66 -0
- package/dist/tmux/commandExecutor.js +75 -2
- package/dist/tmux/tmuxManager.js +747 -49
- package/dist/version.js +23 -0
- package/dist/web/assets/assetManifest.js +62 -0
- package/dist/web/assets/client/app.js +631 -0
- package/dist/web/assets/client/components.js +605 -0
- package/dist/web/assets/client/dom.js +14 -0
- package/dist/web/assets/client/format.js +28 -0
- package/dist/web/assets/client/i18n.js +494 -0
- package/dist/web/assets/client/markdown.js +114 -0
- package/dist/web/assets/client/theme.js +32 -0
- package/dist/web/assets/client/view.js +458 -0
- package/dist/web/assets/fontData.js +12 -0
- package/dist/web/assets/fonts.js +12 -0
- package/dist/web/assets/shell.js +114 -0
- package/dist/web/assets/styles/cards.js +135 -0
- package/dist/web/assets/styles/layout.js +47 -0
- package/dist/web/assets/styles/markdown.js +29 -0
- package/dist/web/assets/styles/responsive.js +39 -0
- package/dist/web/assets/styles/tokens.js +101 -0
- package/dist/web/assets/styles/widgets.js +147 -0
- package/dist/web/tmuxWebTerminal.js +158 -0
- package/dist/web/webServer.js +463 -0
- package/dist/web/webSnapshot.js +148 -0
- package/dist/workItem/workItem.js +642 -23
- package/dist/workspace/gitChangeSetCapture.js +86 -0
- package/dist/workspace/workItemChangeSetManager.js +445 -0
- package/dist/worktree/managedWorkspace.js +202 -0
- package/docs/task-local-identity.md +62 -0
- package/i18n/README.zh-CN.md +406 -31
- package/package.json +10 -2
- package/skills/yui-leader/SKILL.md +601 -39
- package/skills/yui-operator/SKILL.md +255 -34
- package/skills/yui-reviewer/SKILL.md +57 -0
- package/skills/yui-worker/SKILL.md +214 -17
- package/dist/commands/repositoryCommands.js +0 -86
- package/dist/operator/operatorContext.js +0 -66
- package/dist/repository/repository.js +0 -55
- package/dist/scheduler/archivedTaskRuntime.js +0 -12
- 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
|
|
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,
|
|
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
|
|
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
|
-
|
|
227
|
+
Bind a Project and create a Draft Task:
|
|
47
228
|
|
|
48
229
|
```sh
|
|
49
|
-
yui
|
|
50
|
-
|
|
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" --
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
78
|
-
|
|
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
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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)
|