@zq-silk/yui 0.11.2 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +23 -4
- package/README.md +80 -13
- package/dist/cli/commandCatalog.js +30 -5
- package/dist/cli.js +105 -43
- package/dist/commands/configCommands.js +12 -45
- package/dist/commands/executionAuditCommands.js +1 -1
- package/dist/commands/jobCommands.js +4 -10
- package/dist/commands/projectCommands.js +581 -2
- package/dist/commands/taskCommands.js +159 -172
- package/dist/commands/taskCompletionGate.js +29 -54
- package/dist/commands/taskContextCommand.js +55 -11
- package/dist/commands/taskInputCommands.js +8 -5
- package/dist/commands/taskIntegrationCommands.js +2 -1
- package/dist/commands/taskNextActionCommand.js +56 -6
- package/dist/commands/taskOverviewCommand.js +17 -31
- package/dist/commands/taskRoleRuntimeStatus.js +24 -0
- package/dist/commands/taskWorkspaceCommands.js +5 -2
- package/dist/config/configCatalog.js +2 -2
- package/dist/config/yuiConfig.js +3 -7
- package/dist/context/wakeNotification.js +20 -5
- package/dist/controller/agentRuntimeObserver.js +247 -51
- package/dist/controller/clientRuntime.js +1 -1
- package/dist/controller/controller.js +8 -12
- package/dist/controller/fileSchedulerStoreAdapter.js +146 -198
- package/dist/controller/runtime.js +3 -3
- package/dist/controller/runtimeEventInbox.js +17 -7
- package/dist/controller/runtimeEventProcessor.js +12 -19
- package/dist/controller/runtimeObservationHook.js +45 -0
- package/dist/execution/resourceBroker.js +5 -4
- package/dist/executor/agentExecutor.js +4 -4
- package/dist/executor/effectiveLaunch.js +8 -48
- package/dist/executor/fileRoleLaunchPlanner.js +36 -16
- package/dist/executor/workspacePreflightClassification.js +23 -2
- package/dist/interaction/operatorPresentation.js +33 -89
- package/dist/lifecycle/exactRunTerminalization.js +1 -2
- package/dist/lifecycle/taskRoleSessionReset.js +5 -13
- package/dist/observability/orchestrationMetrics.js +8 -3
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +123 -0
- package/dist/repository/checkoutSwap.js +61 -0
- package/dist/repository/gitWorkspace.js +36 -0
- package/dist/repository/project.js +53 -3
- package/dist/repository/taskWorkspacePreparer.js +176 -60
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/deltaRecheck.js +12 -51
- package/dist/review/reviewAcceptance.js +26 -0
- package/dist/review/reviewConfig.js +0 -31
- package/dist/review/reviewDecision.js +113 -0
- package/dist/review/reviewOutcomeClassifier.js +1 -1
- package/dist/review/reviewRound.js +1 -1
- package/dist/review/reviewerAvailability.js +69 -0
- package/dist/runtime/agentDriverObservation.js +24 -10
- package/dist/runtime/builtinAgentDrivers.js +4 -3
- package/dist/runtime/builtinTranscriptObserver.js +301 -64
- package/dist/runtime/builtinTranscriptUsage.js +9 -7
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/runtimeObservation.js +13 -0
- package/dist/runtime/runtimeProjection.js +16 -25
- package/dist/runtime/sessionTokenMetrics.js +181 -0
- package/dist/runtime/structuredProviderHost.js +7 -1
- package/dist/scheduler/activeRoleRunDelivery.js +4 -4
- package/dist/scheduler/activeTaskProgress.js +7 -6
- package/dist/scheduler/leaderWakeupProcessor.js +3 -5
- package/dist/scheduler/operatorEvent.js +34 -0
- package/dist/scheduler/operatorInputNotificationProcessor.js +54 -94
- package/dist/scheduler/roleRunStall.js +0 -14
- package/dist/scheduler/taskExecutionProjection.js +97 -76
- package/dist/scheduler/taskObservabilityProjection.js +10 -11
- package/dist/storage/migration/productionRegistry.js +467 -0
- package/dist/storage/sqliteSchema.js +18 -8
- package/dist/storage/sqliteStore.js +61 -21
- package/dist/storage/storeRpc.js +0 -1
- package/dist/storage/taskStore.js +47 -31
- package/dist/storage/upgrade/recordVersions.js +3 -2
- package/dist/storage/upgrade/sqliteMigrationTarget.js +30 -8
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +23 -11
- package/dist/storage/upgrade/sqliteStateMigration.js +66 -8
- package/dist/task/completionReadiness.js +10 -6
- package/dist/task/nextAction.js +82 -24
- package/dist/telemetry/sqliteTelemetryStore.js +1 -1
- package/dist/web/assets/client/components.js +13 -3
- package/dist/web/assets/client/i18n.js +6 -2
- package/dist/web/webSnapshot.js +4 -0
- package/i18n/README.zh-CN.md +2 -2
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +53 -16
- package/skills/yui-operator/SKILL.md +9 -0
- package/skills/yui-reviewer/SKILL.md +28 -7
- package/dist/context/sessionContextBudget.js +0 -71
- package/dist/lifecycle/contextBudgetRollover.js +0 -81
- package/dist/scheduler/operatorNotification.js +0 -59
package/ARCHITECTURE.md
CHANGED
|
@@ -19,6 +19,16 @@ units. For a feature, the Leader judges whether the whole result is small
|
|
|
19
19
|
enough to own in the same way or large enough to need independently owned
|
|
20
20
|
delivery units.
|
|
21
21
|
|
|
22
|
+
Operator updates have one durable path. Domain and runtime transitions first
|
|
23
|
+
append an immutable TaskEvent; user-owned questions append an InputRequest.
|
|
24
|
+
Only those record references enter the global Operator mailbox. The Controller
|
|
25
|
+
batches pending references into one receipt-backed synthetic user message for
|
|
26
|
+
the existing interactive Operator and defers the complete batch while that
|
|
27
|
+
Operator is busy or unavailable. The message carries CLI read pointers rather
|
|
28
|
+
than copied Task or Provider narrative. No lower layer calls the Operator, no
|
|
29
|
+
mutable notification projection duplicates the event history, and no Goal or
|
|
30
|
+
polling protocol is required to follow work.
|
|
31
|
+
|
|
22
32
|
`WorkItem` means one substantial, independently acceptable requirement with a
|
|
23
33
|
clear owner. Create multiple WorkItems only when multiple Workers can own and
|
|
24
34
|
advance those requirements independently, normally in parallel. Internal
|
|
@@ -128,10 +138,14 @@ late results until explicitly reopened.
|
|
|
128
138
|
|
|
129
139
|
Stable Project checkouts are read-only references. Task identity follows one
|
|
130
140
|
bounded outcome rather than Project count. A Task binds zero or more Projects,
|
|
131
|
-
records an independent base ref for each binding, and
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
141
|
+
records an independent base ref for each binding, and adopts one workspace root
|
|
142
|
+
only when it becomes active. A Draft owns planning state and Project bindings,
|
|
143
|
+
not a writable Workspace. Activation prepares physical worktrees first and then
|
|
144
|
+
commits status, workspace identity, cwd, and durable Workspace ownership in one
|
|
145
|
+
TaskStore transaction; failure discards unadopted resources and leaves the Task
|
|
146
|
+
Draft. The active Workspace contains a managed main worktree for each binding.
|
|
147
|
+
The `<workspace>/tasks/<task>/main` root is a logical multi-Project container,
|
|
148
|
+
not a Git repository. Each Project child (for example
|
|
135
149
|
`<workspace>/tasks/<task>/main/yui`) is the supported Git cwd and points to
|
|
136
150
|
`<workspace>/worktree/<project>/<task>/main`; Git commands run in that child.
|
|
137
151
|
For a single-Project workspace, the native Agent starts in that Project's
|
|
@@ -142,6 +156,11 @@ additional-directory mechanism. The active Leader may append a Project when the
|
|
|
142
156
|
same outcome expands; replacing an existing binding is not a scope-repair
|
|
143
157
|
mechanism.
|
|
144
158
|
|
|
159
|
+
Before a Role launch, Yui verifies that every physical Project HEAD still
|
|
160
|
+
descends from its Workspace's recorded base. Normal committed progress is
|
|
161
|
+
allowed; a reset or repoint outside that lineage is reported as
|
|
162
|
+
`physical-drift` and fails closed before Provider launch.
|
|
163
|
+
|
|
145
164
|
A WorkItem can read the full Task workspace but has an explicit Project write
|
|
146
165
|
scope. Isolation creates a second root with independent worktrees for writable
|
|
147
166
|
Projects and Task-main context for the rest. The managed dispatch and
|
package/README.md
CHANGED
|
@@ -274,6 +274,12 @@ yui task context <task-id>
|
|
|
274
274
|
yui task activate <task-id>
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
+
A Draft stores planning state and Project bindings only; it does not adopt a
|
|
278
|
+
writable managed Workspace. `task activate` prepares every bound Project first,
|
|
279
|
+
then commits the Task's `active` status and Task-owned Workspace together. A
|
|
280
|
+
preparation or consistency failure leaves the Task Draft and reports the
|
|
281
|
+
workspace diagnosis instead of exposing a partially adopted execution root.
|
|
282
|
+
|
|
277
283
|
Task type describes intent rather than selecting an execution protocol.
|
|
278
284
|
Software Projects use `bugfix` or `feature`: a bugfix is Leader-owned; if it
|
|
279
285
|
grows into independently owned delivery requirements, reclassify it as a
|
|
@@ -294,6 +300,49 @@ files as dirty, preserves ignored files, and refuses missing remotes or refs and
|
|
|
294
300
|
When the configured branch is `HEAD`, refresh resolves the remote's symbolic default branch for that
|
|
295
301
|
operation and requires the checkout to be on that branch; detached or mismatched checkouts fail.
|
|
296
302
|
|
|
303
|
+
### Project lifecycle
|
|
304
|
+
|
|
305
|
+
Divergence and end-of-life are explicit, Operator-authority operations with fail-closed gates.
|
|
306
|
+
Every destructive command refuses a managed Task Session (run it from an Operator or user
|
|
307
|
+
terminal), an active Task binding, a dirty checkout, and an unreachable or unverified remote.
|
|
308
|
+
|
|
309
|
+
```sh
|
|
310
|
+
yui project diagnose app
|
|
311
|
+
yui project reset app
|
|
312
|
+
yui project reset app --discard-local
|
|
313
|
+
yui project replace app --discard-local
|
|
314
|
+
yui project retire app --reason "superseded by app-ng"
|
|
315
|
+
yui project delete app --confirm app
|
|
316
|
+
yui project delete app --checkout --confirm app
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
`project reset` handles the divergence `project refresh` refuses. Without `--discard-local` it is
|
|
320
|
+
a dry run: it fetches and verifies the remote baseline, and when the checkout has diverged it
|
|
321
|
+
refuses while listing the exact local commits that would be discarded. With `--discard-local` it
|
|
322
|
+
hard-resets the clean checkout to the verified remote commit (a plain fast-forward when the
|
|
323
|
+
checkout is merely behind). `project replace` goes further for Home-managed checkouts: it clones
|
|
324
|
+
the remote into a staging directory, verifies both branches, copies the Yui-local refs
|
|
325
|
+
(`refs/heads/yui/`, `refs/yui/archive/`) so historical evidence keeps resolving, then swaps the
|
|
326
|
+
checkout on disk while the catalog record keeps its path. Replace refuses linked worktrees (Task
|
|
327
|
+
or Integration workspaces) and dirty checkouts, and requires `--discard-local`. The swap is
|
|
328
|
+
recoverable: the previous checkout is parked at a backup path and restored on any failure, a
|
|
329
|
+
catalog refusal rolls the swap back, and a crash mid-swap is healed on the next run (a crash
|
|
330
|
+
before the swap leaves only a removable staging clone).
|
|
331
|
+
|
|
332
|
+
`project retire` is the auditable soft deprecation: it records who retired the Project, when, and
|
|
333
|
+
why, while retaining the catalog record, checkout, and every historical
|
|
334
|
+
Task/Run/Review/Integration/Publication reference. A retired Project cannot be refreshed,
|
|
335
|
+
updated, migrated, reset, replaced, maintained through Knowledge writes (add/retire/propose/
|
|
336
|
+
accept/reject), or bound to new Tasks, WorkItems, or Integrations; Knowledge reads (`list`,
|
|
337
|
+
`show`, `proposals list/show`) stay open so the evidence stays auditable.
|
|
338
|
+
`project delete` is the separate hard-removal decision: it requires a retired Project, an exact
|
|
339
|
+
`--confirm <project-id>` acknowledgment, and fails closed while any Task record references the
|
|
340
|
+
Project. `--checkout` additionally removes the Home-managed checkout (external checkouts are
|
|
341
|
+
user-owned and must be removed manually): it first refuses linked worktrees and dirty checkouts,
|
|
342
|
+
then moves the checkout to a tombstone before removing the catalog record, restoring it on any
|
|
343
|
+
failure so the catalog and checkout never disagree unrecoverably. `project show` and
|
|
344
|
+
`project list` display the lifecycle status and retirement record.
|
|
345
|
+
|
|
297
346
|
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.
|
|
298
347
|
|
|
299
348
|
Leader wakeups stay deliberately small: the wake envelope carries only the
|
|
@@ -347,14 +396,16 @@ acceptance so the Leader can accept it directly or run
|
|
|
347
396
|
Leader-managed candidates awaiting a decision instead of marking them done.
|
|
348
397
|
`final` does not create WorkItem ReviewRounds or decide Task topology. The
|
|
349
398
|
Leader explicitly requests a Task-scoped Review, unless an immutable Task
|
|
350
|
-
contract requires one. The Round
|
|
351
|
-
Leader-owned Task with no WorkItem can be reviewed
|
|
352
|
-
|
|
399
|
+
contract requires one. The Round snapshots the exact Task-main Project heads
|
|
400
|
+
directly, so even a Leader-owned Task with no WorkItem can be reviewed without
|
|
401
|
+
locking the mutable Task workspace. A changed frozen head needs a new semantic
|
|
402
|
+
Round; the same Reviewer Session continues in its
|
|
353
403
|
stable workspace, while every Run remains bound to its exact Round and head.
|
|
354
404
|
The Reviewer follows Project Policy/Knowledge and reports reachable, material,
|
|
355
405
|
actionable findings across the complete Task.
|
|
356
|
-
A ReviewRound freezes the Candidate's exact Git commit and
|
|
357
|
-
|
|
406
|
+
A ReviewRound freezes the Candidate's exact Git commit and updates the
|
|
407
|
+
Reviewer Role's stable writable workspace to that head while recording exact
|
|
408
|
+
Round-owned workspace evidence. Its AgentRun may edit,
|
|
358
409
|
test, and optionally commit diagnostic evidence there, but never changes the
|
|
359
410
|
Candidate or Worker workspace and never creates another WorkItem, Candidate,
|
|
360
411
|
ChangeSet, or recursive review. The result wakes the Leader, who decides whether
|
|
@@ -362,6 +413,17 @@ to route evidence to the original Worker, accept, reject and redispatch that
|
|
|
362
413
|
Worker in its existing Session, review again, or request user input.
|
|
363
414
|
A failed review remains visible evidence and wakes the Leader, but does not
|
|
364
415
|
take that decision away from the Leader.
|
|
416
|
+
Task context and next-action expose direct Review facts: every frozen Project
|
|
417
|
+
commit, its relation to the current candidate, the active Run, and the Reviewer
|
|
418
|
+
workspace. A request that fails after Round creation retains the ReviewRound
|
|
419
|
+
and reports its exact reason; the Leader opens that Round and decides whether
|
|
420
|
+
to retry, inspect or clean the workspace, use another Reviewer, or continue
|
|
421
|
+
other work.
|
|
422
|
+
An active Task-final Review freezes only its own candidate; it does not prevent
|
|
423
|
+
the Leader from processing new input or advancing a later candidate. Delta
|
|
424
|
+
Recheck is always available when Yui can prove an accepted contiguous baseline
|
|
425
|
+
and exact diff. Yui does not select a mode from generic size thresholds;
|
|
426
|
+
`requires-full-review` returns to the Leader without creating another Round.
|
|
365
427
|
Candidate history, every ReviewRound, and the Leader decision remain grouped
|
|
366
428
|
under the original WorkItem. A rejected result creates a new Candidate on the
|
|
367
429
|
next dispatch while reusing the original execution Role, Session, and
|
|
@@ -551,11 +613,14 @@ Every stage is a new immutable ExecutionGroup. Its ContextSnapshot and selected
|
|
|
551
613
|
parent Lane results are durable references; `retry` repeats a stage within its
|
|
552
614
|
attempt budget, while `retry` at Resolve begins the next bounded round.
|
|
553
615
|
|
|
554
|
-
Each new stage also freezes one Resource Broker contract:
|
|
555
|
-
wall-clock budgets; quorum and deadline; a
|
|
556
|
-
marginal value for more Lane spend. Omitted
|
|
557
|
-
budget and runtime-health windows; stage
|
|
558
|
-
|
|
616
|
+
Each new stage also freezes one Resource Broker contract: behavioral tool-call
|
|
617
|
+
and wall-clock budgets; a display-only token threshold; quorum and deadline; a
|
|
618
|
+
straggler window; and the minimum marginal value for more Lane spend. Omitted
|
|
619
|
+
values reuse the existing context budget and runtime-health windows; stage
|
|
620
|
+
retries share the original cumulative observations and absolute deadline.
|
|
621
|
+
Observed token totals and the configured token threshold are cost context only:
|
|
622
|
+
they never close spend, block admission, suppress scheduling, or stop a Lane.
|
|
623
|
+
Execution, Lane retry, and Reviewer-panel
|
|
559
624
|
admission all count active Lanes at Home, Task, WorkItem, Group, Provider,
|
|
560
625
|
Agent, and model scopes. Capacity pressure keeps the excess Lane durably
|
|
561
626
|
pending instead of failing the Group. Capacity release or deadline arrival
|
|
@@ -570,8 +635,9 @@ The Leader may add `--early-stop <0-100>` to an accepting Group resolution.
|
|
|
570
635
|
Yui permits it only after quorum and T5's passed Verify/Resolve evidence prove
|
|
571
636
|
sufficiency. It may skip Lanes that never started; active stragglers are
|
|
572
637
|
reported and retained, never killed automatically for cost. If evidence is
|
|
573
|
-
insufficient, budget or deadline exhaustion
|
|
574
|
-
judgment instead of turning thin evidence into
|
|
638
|
+
insufficient, behavioral tool-call/wall-clock budget or deadline exhaustion
|
|
639
|
+
blocks the stage for Leader judgment instead of turning thin evidence into
|
|
640
|
+
success.
|
|
575
641
|
|
|
576
642
|
New exploration histories also freeze the structured candidate-convergence
|
|
577
643
|
contract. Yui appends the exact stage-local JSON shape to every Lane assignment
|
|
@@ -749,7 +815,7 @@ yui task input request <task-id> --question "Which format should be the default?
|
|
|
749
815
|
|
|
750
816
|
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.
|
|
751
817
|
|
|
752
|
-
`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
|
|
818
|
+
`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. Task completion, retirement, Leader attention, stalls, and open input are queued to the global Operator mailbox only as immutable TaskEvent or InputRequest references. The Controller merges one pending mailbox batch into one receipt-backed `[Yui updates]` user message for an existing ready Operator; the Operator reads the referenced records through the CLI and decides what is worth presenting. A running or unavailable Operator is never started or interrupted: the whole batch remains durable and is retried after native turn completion or a later Controller pass. This path is a user message, not a tool call, and it never inspects or classifies 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.
|
|
753
819
|
|
|
754
820
|
Inspect the result:
|
|
755
821
|
|
|
@@ -1011,6 +1077,7 @@ yui config completion [bash|zsh|fish]
|
|
|
1011
1077
|
yui session enter|record|replace|reconcile
|
|
1012
1078
|
yui session stop --all
|
|
1013
1079
|
yui project add|clone|refresh|update|discover|list|show|knowledge
|
|
1080
|
+
yui project reset|replace|retire|delete
|
|
1014
1081
|
```
|
|
1015
1082
|
|
|
1016
1083
|
`yui update` stages the newly published package **side by side** — it never
|
|
@@ -59,8 +59,7 @@ function durableConfigDomainNode(domain) {
|
|
|
59
59
|
const values = CONFIG_KEY_VALUES.filter(({ name }) => keys.includes(name));
|
|
60
60
|
const options = domain === "workflow"
|
|
61
61
|
? [
|
|
62
|
-
"--soft-tokens", "--hard-tokens", "--role", "--trigger", "--finding-ledger"
|
|
63
|
-
"--delta-recheck", "--delta-recheck-max-lines", "--delta-recheck-max-files"
|
|
62
|
+
"--soft-tokens", "--hard-tokens", "--role", "--trigger", "--finding-ledger"
|
|
64
63
|
]
|
|
65
64
|
: domain === "runtime"
|
|
66
65
|
? ["--quiet-after-seconds", "--diagnostic-after-seconds", "--stall-after-seconds"]
|
|
@@ -86,8 +85,7 @@ function durableConfigDomainNode(domain) {
|
|
|
86
85
|
optionValues: domain === "workflow"
|
|
87
86
|
? {
|
|
88
87
|
"--trigger": ["always", "leader", "final"],
|
|
89
|
-
"--finding-ledger": ["shadow", "enforce"]
|
|
90
|
-
"--delta-recheck": ["enabled", "disabled"]
|
|
88
|
+
"--finding-ledger": ["shadow", "enforce"]
|
|
91
89
|
}
|
|
92
90
|
: {}
|
|
93
91
|
},
|
|
@@ -1260,7 +1258,10 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1260
1258
|
{
|
|
1261
1259
|
name: "project",
|
|
1262
1260
|
summary: "Manage Projects, stable checkouts, branches, and Yui knowledge.",
|
|
1263
|
-
sections: [
|
|
1261
|
+
sections: [
|
|
1262
|
+
{ id: "manage", title: "Commands", entries: ["add", "clone", "refresh", "diagnose", "migrate", "update", "discover", "list", "show", "knowledge"] },
|
|
1263
|
+
{ id: "lifecycle", title: "Lifecycle (Operator authority)", entries: ["reset", "replace", "retire", "delete"] }
|
|
1264
|
+
],
|
|
1264
1265
|
children: [
|
|
1265
1266
|
{
|
|
1266
1267
|
name: "add",
|
|
@@ -1291,6 +1292,30 @@ export const ROOT_COMMAND = buildNode({
|
|
|
1291
1292
|
usage: "yui project migrate <project> [--preflight]",
|
|
1292
1293
|
options: ["--preflight"]
|
|
1293
1294
|
},
|
|
1295
|
+
{
|
|
1296
|
+
name: "reset",
|
|
1297
|
+
summary: "Hard-reset a canonical checkout to its verified remote baseline (Operator authority).",
|
|
1298
|
+
usage: "yui project reset <project> [--discard-local]",
|
|
1299
|
+
options: ["--discard-local"]
|
|
1300
|
+
},
|
|
1301
|
+
{
|
|
1302
|
+
name: "replace",
|
|
1303
|
+
summary: "Re-clone a Home-managed checkout from its remote, preserving Yui refs (Operator authority).",
|
|
1304
|
+
usage: "yui project replace <project> --discard-local",
|
|
1305
|
+
options: ["--discard-local"]
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
name: "retire",
|
|
1309
|
+
summary: "Soft-deprecate a Project; record and evidence are retained (Operator authority).",
|
|
1310
|
+
usage: "yui project retire <project> --reason <text>",
|
|
1311
|
+
options: ["--reason"]
|
|
1312
|
+
},
|
|
1313
|
+
{
|
|
1314
|
+
name: "delete",
|
|
1315
|
+
summary: "Remove a retired Project's catalog record and optionally its checkout (Operator authority).",
|
|
1316
|
+
usage: "yui project delete <project> [--checkout] --confirm <project-id>",
|
|
1317
|
+
options: ["--checkout", "--confirm"]
|
|
1318
|
+
},
|
|
1294
1319
|
{
|
|
1295
1320
|
name: "update",
|
|
1296
1321
|
summary: "Update a bound Project's aliases, remote, or branch refs.",
|
package/dist/cli.js
CHANGED
|
@@ -37,7 +37,7 @@ import { runResourcesCommand } from "./commands/resourcesCommands.js";
|
|
|
37
37
|
import { applyOperatorSessionControl, runOperatorCommand } from "./commands/operatorCommands.js";
|
|
38
38
|
import { runProjectCommand } from "./commands/projectCommands.js";
|
|
39
39
|
import { runProfileCommand } from "./commands/profileCommands.js";
|
|
40
|
-
import { dispatchPreparedReviewRound, failPendingReviewRound,
|
|
40
|
+
import { dispatchPreparedReviewRound, failPendingReviewRound, preserveReviewRoundWorkspace, parseTaskCompletionRequest, parseTaskFinalReviewContractRebindRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, resolvedExecutionStageRetryGroup, validateTaskArchiveRequest } from "./commands/taskCommands.js";
|
|
41
41
|
import { taskActor } from "./commands/taskActor.js";
|
|
42
42
|
import { isCurrentGlobalOperator } from "./commands/taskInputCommands.js";
|
|
43
43
|
import { runTaskIntegrationCommand } from "./commands/taskIntegrationCommands.js";
|
|
@@ -49,6 +49,7 @@ import { runWorkflowCommandAsync } from "./commands/workflowCommands.js";
|
|
|
49
49
|
import { createUpdatePorts } from "./cli/updatePorts.js";
|
|
50
50
|
import { createReleaseWorkflowPorts } from "./release/releaseWorkflowPorts.js";
|
|
51
51
|
import { acquireHandoverLock, readRuntimeIdentity } from "./release/runtimeRelease.js";
|
|
52
|
+
import { assertCliHomeReleaseFence, describeCliHomeInvocation } from "./release/cliHomeReleaseFence.js";
|
|
52
53
|
import { renderReleaseActivateResult, renderReleaseInstallResult, renderReleaseList, resolveReleaseActivationDriver, runReleaseActivate, runReleaseInstall, runReleaseList } from "./commands/releaseCommands.js";
|
|
53
54
|
import { reconcileTaskRemoteBaselines, verifyTaskCompletionPublishedTree } from "./commands/taskCompletionGate.js";
|
|
54
55
|
import { runTaskBaseStatusCommand } from "./commands/taskBaseCommands.js";
|
|
@@ -68,7 +69,7 @@ import { agentNotFound, CliError, runtimeError, usageError } from "./errors/cliE
|
|
|
68
69
|
import { FileRoleLaunchPlanner } from "./executor/fileRoleLaunchPlanner.js";
|
|
69
70
|
import { AGENT_HOST_CONTROL_PROTOCOL, runAgentHost, sendAgentHostAuthorityControl } from "./runtime/agentHost.js";
|
|
70
71
|
import { TaskWorkspaceCoordinator, WorkspaceCleanupBlockedError } from "./repository/taskWorkspaceCoordinator.js";
|
|
71
|
-
import { FileTaskWorkspacePreparer
|
|
72
|
+
import { FileTaskWorkspacePreparer } from "./repository/taskWorkspacePreparer.js";
|
|
72
73
|
import { inspectStorageSchema } from "./storage/storageSchema.js";
|
|
73
74
|
import { collectRuntimeBuildIdentity, collectStorageIdentity, countDroppedInboxEvents, createProductionRuntimeIdentityPorts, evaluateStorageHealth, resolveStatusIdentityEnabled } from "./observability/runtimeIdentity.js";
|
|
74
75
|
import { resolveYuiHome } from "./storage/taskStore.js";
|
|
@@ -83,13 +84,14 @@ import { AgentConfigurationCatalogService } from "./executor/agentConfigurationC
|
|
|
83
84
|
import { TmuxWebTerminalService } from "./web/tmuxWebTerminal.js";
|
|
84
85
|
import { listOperatorSessions, operatorSessionRef } from "./operator/operatorSessionHistory.js";
|
|
85
86
|
import { YUI_VERSION, yuiVersionIdentity } from "./version.js";
|
|
87
|
+
import { SqliteSchemaMigrationError } from "./storage/sqliteSchema.js";
|
|
86
88
|
import { YUI_CONTROL_PLANE_DESCRIPTOR, YUI_TASK_RUNTIME_DESCRIPTOR, assertCompatibleControlPlanePreflight, assertExactControlPlanePreflight, assertExactTaskRuntimeEnvironment, assertExactTaskRuntimeState, exactControlPlaneDigest, extractExactControlArgument, createExactControlPlaneDescriptor, parseExactControlPlaneDescriptor } from "./runtime/exactControlPlane.js";
|
|
87
89
|
import { readSessionBootstrapManifest, refreshManagedSessionCliWrappers } from "./context/sessionBootstrapManifest.js";
|
|
88
90
|
import { builtinAgentDriverRegistry } from "./runtime/builtinAgentDrivers.js";
|
|
89
91
|
import { createTaskFinalReviewContract, extractTaskFinalReviewRequest } from "./review/taskFinalReviewContract.js";
|
|
90
92
|
import { prepareTaskFinalReviewContractRebindProof, resolveRecordedTaskFinalReviewContract } from "./review/taskFinalReviewContractRebind.js";
|
|
91
93
|
import { assessDeltaRecheck } from "./review/deltaRecheck.js";
|
|
92
|
-
import {
|
|
94
|
+
import { isAcceptedTaskReviewBaseline } from "./review/reviewAcceptance.js";
|
|
93
95
|
import { NodeGitWorkspace } from "./repository/gitWorkspace.js";
|
|
94
96
|
import { currentWorkItemExecutionGroup, workItemExecutionGroupById } from "./workItem/workItem.js";
|
|
95
97
|
const VERSION = YUI_VERSION;
|
|
@@ -107,13 +109,47 @@ void main().catch((error) => {
|
|
|
107
109
|
process.exitCode = error.exitCode;
|
|
108
110
|
return;
|
|
109
111
|
}
|
|
110
|
-
const message =
|
|
112
|
+
const message = runtimeFailureMessage(error);
|
|
111
113
|
process.stderr.write(`${jsonOutput
|
|
112
114
|
? JSON.stringify({ ok: false, code: "RUNTIME_ERROR", message, details: {} })
|
|
113
115
|
: `RUNTIME_ERROR: ${message}`}\n`);
|
|
114
116
|
process.exitCode = 5;
|
|
115
117
|
});
|
|
118
|
+
function runtimeFailureMessage(error) {
|
|
119
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
120
|
+
if (!(error instanceof SqliteSchemaMigrationError))
|
|
121
|
+
return message;
|
|
122
|
+
try {
|
|
123
|
+
return `${message}\n${describeCliHomeInvocation({
|
|
124
|
+
home: resolveYuiHome(process.env),
|
|
125
|
+
packageRoot: fileURLToPath(new URL("../", import.meta.url)),
|
|
126
|
+
entryPath: fileURLToPath(import.meta.url)
|
|
127
|
+
})}`;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return message;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
116
133
|
export async function main() {
|
|
134
|
+
const home = resolveYuiHome(process.env);
|
|
135
|
+
const routedForFence = args.length === 0 ? undefined : routeInvocation(args);
|
|
136
|
+
const managedInvocation = process.env.YUI_SESSION_SCOPE === "task"
|
|
137
|
+
|| process.env.YUI_SESSION_SCOPE === "global"
|
|
138
|
+
|| process.env[YUI_CONTROL_PLANE_DESCRIPTOR] !== undefined
|
|
139
|
+
|| process.env[YUI_TASK_RUNTIME_DESCRIPTOR] !== undefined;
|
|
140
|
+
const homeFreeInvocation = args.length === 0
|
|
141
|
+
|| (args[0] === "version" && args.length === 1)
|
|
142
|
+
|| routedForFence?.kind === "help"
|
|
143
|
+
|| routedForFence?.kind === "path-error"
|
|
144
|
+
|| routedForFence?.kind === "incomplete";
|
|
145
|
+
if (managedInvocation || !homeFreeInvocation) {
|
|
146
|
+
assertCliHomeReleaseFence({
|
|
147
|
+
home,
|
|
148
|
+
packageRoot: fileURLToPath(new URL("../", import.meta.url)),
|
|
149
|
+
entryPath: fileURLToPath(import.meta.url),
|
|
150
|
+
args
|
|
151
|
+
});
|
|
152
|
+
}
|
|
117
153
|
const delegatedDriver = explicitReleaseActivationDriver();
|
|
118
154
|
if (delegatedDriver !== null) {
|
|
119
155
|
const delegated = spawnSync(process.execPath, [delegatedDriver, ...process.argv.slice(2)], {
|
|
@@ -160,7 +196,6 @@ export async function main() {
|
|
|
160
196
|
process.exitCode = runUpdateCommand();
|
|
161
197
|
return;
|
|
162
198
|
}
|
|
163
|
-
const home = resolveYuiHome(process.env);
|
|
164
199
|
if (args[0] === "release") {
|
|
165
200
|
const subcommand = args[1];
|
|
166
201
|
if (subcommand === "install" && args.length === 3) {
|
|
@@ -1123,14 +1158,16 @@ export async function main() {
|
|
|
1123
1158
|
const laneSnapshotPreflight = executionLaneGitSnapshot === undefined
|
|
1124
1159
|
? undefined
|
|
1125
1160
|
: executionLaneGitSnapshot;
|
|
1126
|
-
//
|
|
1127
|
-
//
|
|
1128
|
-
//
|
|
1129
|
-
|
|
1161
|
+
// Physical preparation may precede the durable write, but Task status,
|
|
1162
|
+
// workspace identity/cwd, and ManagedWorkspace ownership are adopted by
|
|
1163
|
+
// one transaction. A failed attempt therefore leaves the Task Draft and
|
|
1164
|
+
// owning no writable workspace.
|
|
1165
|
+
let taskWorkspaceActivation;
|
|
1166
|
+
if (resolved[1] === "activate" && resolved.length === 3) {
|
|
1130
1167
|
const taskId = resolved[2];
|
|
1131
1168
|
const task = taskId === undefined ? null : store.getTask(taskId);
|
|
1132
1169
|
if (task !== null && task.status === "draft") {
|
|
1133
|
-
await workspacePreparer.
|
|
1170
|
+
taskWorkspaceActivation = await workspacePreparer.activateTaskWorkspace(task.id);
|
|
1134
1171
|
}
|
|
1135
1172
|
}
|
|
1136
1173
|
const result = runTaskCommand(resolved.slice(1), store, {
|
|
@@ -1153,6 +1190,7 @@ export async function main() {
|
|
|
1153
1190
|
? {}
|
|
1154
1191
|
: { candidateWorkspace: candidateMaterialization.workspace ?? null }),
|
|
1155
1192
|
...(executionLaneWorkspaces === undefined ? {} : { executionLaneWorkspaces }),
|
|
1193
|
+
...(taskWorkspaceActivation === undefined ? {} : { taskWorkspaceActivation }),
|
|
1156
1194
|
...(laneDispatchProjectPaths === undefined ? {} : { laneDispatchProjectPaths }),
|
|
1157
1195
|
...(directTaskMainSnapshot === undefined ? {} : { directTaskMainSnapshot }),
|
|
1158
1196
|
...(actualTaskReviewCandidate === undefined
|
|
@@ -1219,31 +1257,54 @@ export async function main() {
|
|
|
1219
1257
|
...(storedRound?.deltaRecheck === undefined
|
|
1220
1258
|
|| deltaRecheckPreflight === undefined
|
|
1221
1259
|
? {}
|
|
1222
|
-
: {
|
|
1260
|
+
: {
|
|
1261
|
+
deltaRecheckDiff: deltaRecheckPreflight.diffByProject
|
|
1262
|
+
})
|
|
1223
1263
|
});
|
|
1224
1264
|
reviewOutput = run === null
|
|
1225
1265
|
? `Review ${requestedRound.id} retained pending by Resource Broker\n`
|
|
1226
1266
|
: `Review queued as ${requestedRound.id} (${run.id})\n`;
|
|
1227
1267
|
reviewData = {
|
|
1268
|
+
reviewRequest: run === null
|
|
1269
|
+
? {
|
|
1270
|
+
kind: "busy",
|
|
1271
|
+
reviewerRoleName: requestedRound.reviewerRoleName,
|
|
1272
|
+
phase: "resource-broker",
|
|
1273
|
+
activeReviewRoundId: requestedRound.id,
|
|
1274
|
+
retryable: true,
|
|
1275
|
+
retryAfterSeconds: 5
|
|
1276
|
+
}
|
|
1277
|
+
: {
|
|
1278
|
+
kind: "started",
|
|
1279
|
+
reviewerRoleName: requestedRound.reviewerRoleName,
|
|
1280
|
+
reviewRoundId: requestedRound.id,
|
|
1281
|
+
runId: run.id
|
|
1282
|
+
},
|
|
1228
1283
|
reviewRound: store.getReviewRound(requestedRound.taskId, requestedRound.id),
|
|
1229
1284
|
...(run === null ? {} : { reviewRun: run }),
|
|
1230
1285
|
workspace
|
|
1231
1286
|
};
|
|
1232
1287
|
}
|
|
1233
1288
|
catch (error) {
|
|
1234
|
-
const currentRound = store.getReviewRound(requestedRound.taskId, requestedRound.id);
|
|
1235
|
-
if (requestedRound.resumedPendingFinalReview
|
|
1236
|
-
&& !requestedRound.terminalizedLeaderRun
|
|
1237
|
-
&& (error instanceof ReviewRoundWorkspaceEvidenceError
|
|
1238
|
-
|| error instanceof TaskFinalReviewDispatchDriftError)
|
|
1239
|
-
&& (currentRound?.scope ?? "work-item") === "task") {
|
|
1240
|
-
throw error;
|
|
1241
|
-
}
|
|
1242
1289
|
const message = error instanceof Error ? error.message : String(error);
|
|
1243
1290
|
await workspacePreparer.discardUnadoptedExecutionLaneWorkspaces(executionLaneWorkspaces);
|
|
1244
|
-
const failed = failPendingReviewRound(requestedRound.taskId, requestedRound.id, message, store, {
|
|
1245
|
-
|
|
1246
|
-
|
|
1291
|
+
const failed = failPendingReviewRound(requestedRound.taskId, requestedRound.id, message, store, {
|
|
1292
|
+
runtime,
|
|
1293
|
+
environment: process.env,
|
|
1294
|
+
yuiHome: home
|
|
1295
|
+
});
|
|
1296
|
+
reviewOutput = `Review could not start: ${message}\n`
|
|
1297
|
+
+ "The failed ReviewRound was retained for Leader routing.\n";
|
|
1298
|
+
reviewData = {
|
|
1299
|
+
reviewRequest: {
|
|
1300
|
+
kind: "unavailable",
|
|
1301
|
+
reviewerRoleName: requestedRound.reviewerRoleName,
|
|
1302
|
+
reviewRoundId: failed.id,
|
|
1303
|
+
reason: message,
|
|
1304
|
+
retryable: true
|
|
1305
|
+
},
|
|
1306
|
+
reviewRound: failed
|
|
1307
|
+
};
|
|
1247
1308
|
}
|
|
1248
1309
|
}
|
|
1249
1310
|
if (resolved[1] === "project" && resolved[2] === "add") {
|
|
@@ -1906,6 +1967,7 @@ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, en
|
|
|
1906
1967
|
if (args[0] !== "task")
|
|
1907
1968
|
return undefined;
|
|
1908
1969
|
let taskId;
|
|
1970
|
+
let decisionSupportRead = false;
|
|
1909
1971
|
if (args[1] === "complete" && args[2] !== undefined) {
|
|
1910
1972
|
const task = store.getTask(args[2]);
|
|
1911
1973
|
if (task === null || task.status !== "active" || task.projectBindings.length === 0) {
|
|
@@ -1963,9 +2025,21 @@ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, en
|
|
|
1963
2025
|
taskId = reference.taskId;
|
|
1964
2026
|
}
|
|
1965
2027
|
}
|
|
2028
|
+
else if ((args[1] === "context" || args[1] === "next-action")
|
|
2029
|
+
&& args[2] !== undefined) {
|
|
2030
|
+
taskId = store.getTask(args[2])?.id;
|
|
2031
|
+
decisionSupportRead = true;
|
|
2032
|
+
}
|
|
1966
2033
|
if (taskId === undefined || store.getTask(taskId)?.status !== "active")
|
|
1967
2034
|
return undefined;
|
|
1968
|
-
|
|
2035
|
+
try {
|
|
2036
|
+
return await snapshotActualTaskReviewCandidate(taskId, store, preparer);
|
|
2037
|
+
}
|
|
2038
|
+
catch (error) {
|
|
2039
|
+
if (decisionSupportRead && error instanceof CliError)
|
|
2040
|
+
return undefined;
|
|
2041
|
+
throw error;
|
|
2042
|
+
}
|
|
1969
2043
|
}
|
|
1970
2044
|
async function snapshotActualTaskReviewCandidate(taskId, store, preparer) {
|
|
1971
2045
|
const task = store.getTask(taskId);
|
|
@@ -2002,9 +2076,8 @@ async function snapshotActualTaskReviewCandidate(taskId, store, preparer) {
|
|
|
2002
2076
|
/**
|
|
2003
2077
|
* Issue 07: computes the delta-recheck assessment for `task review request
|
|
2004
2078
|
* --delta-recheck`. Runs only for that exact command; every other command
|
|
2005
|
-
* returns undefined.
|
|
2006
|
-
*
|
|
2007
|
-
* still owns the semantic equivalence decision.
|
|
2079
|
+
* returns undefined. Technical evidence boundaries fail closed here; semantic
|
|
2080
|
+
* risk remains a Leader and Project-policy decision.
|
|
2008
2081
|
*/
|
|
2009
2082
|
async function deltaRecheckPreflightForTaskCommand(args, store, actualTaskReviewCandidate) {
|
|
2010
2083
|
if (args[0] !== "task" || args[1] !== "review" || args[2] !== "request") {
|
|
@@ -2018,23 +2091,13 @@ async function deltaRecheckPreflightForTaskCommand(args, store, actualTaskReview
|
|
|
2018
2091
|
const task = store.getTask(taskId);
|
|
2019
2092
|
if (task === null || task.status !== "active")
|
|
2020
2093
|
return undefined;
|
|
2021
|
-
const config = store.getReviewConfig();
|
|
2022
|
-
if (!deltaRecheckEnabled(config)) {
|
|
2023
|
-
throw usageError("Delta-recheck is not enabled for this Project's review policy. "
|
|
2024
|
-
+ "Set `yui config workflow set review --role <role> --trigger final --delta-recheck enabled` "
|
|
2025
|
-
+ "or request a full Review.");
|
|
2026
|
-
}
|
|
2027
|
-
const reviewConfig = config;
|
|
2028
2094
|
if (actualTaskReviewCandidate === undefined)
|
|
2029
2095
|
return undefined;
|
|
2030
2096
|
// The previous Round is the latest completed Task-final Round that accepted
|
|
2031
2097
|
// a head (a full Review or an equivalent-and-accepted delta). A
|
|
2032
2098
|
// non-accepted delta cannot be the base for a new delta.
|
|
2033
2099
|
const previous = [...store.listReviewRounds(task.id)]
|
|
2034
|
-
.filter((round) => (
|
|
2035
|
-
&& round.status === "completed"
|
|
2036
|
-
&& (round.deltaRecheck === undefined
|
|
2037
|
-
|| round.deltaRecheck.disposition === "equivalent-and-accepted")))
|
|
2100
|
+
.filter((round) => isAcceptedTaskReviewBaseline(store, round))
|
|
2038
2101
|
.sort((left, right) => left.id.localeCompare(right.id, undefined, { numeric: true }))
|
|
2039
2102
|
.at(-1);
|
|
2040
2103
|
if (previous === undefined) {
|
|
@@ -2052,11 +2115,10 @@ async function deltaRecheckPreflightForTaskCommand(args, store, actualTaskReview
|
|
|
2052
2115
|
repositoryPaths,
|
|
2053
2116
|
previousRound: previous,
|
|
2054
2117
|
candidate: actualTaskReviewCandidate,
|
|
2055
|
-
git: new NodeGitWorkspace()
|
|
2056
|
-
config: reviewConfig
|
|
2118
|
+
git: new NodeGitWorkspace()
|
|
2057
2119
|
});
|
|
2058
2120
|
if (assessment.kind === "ineligible") {
|
|
2059
|
-
throw usageError(`Delta-recheck is
|
|
2121
|
+
throw usageError(`Delta-recheck is technically unavailable: ${assessment.reason}`);
|
|
2060
2122
|
}
|
|
2061
2123
|
return assessment.preflight;
|
|
2062
2124
|
}
|
|
@@ -2115,13 +2177,13 @@ function reviewRoundFromCommandData(data) {
|
|
|
2115
2177
|
const value = round;
|
|
2116
2178
|
return typeof value.id === "string"
|
|
2117
2179
|
&& typeof value.taskId === "string"
|
|
2180
|
+
&& typeof value.reviewerRoleName === "string"
|
|
2118
2181
|
&& typeof value.status === "string"
|
|
2119
2182
|
? {
|
|
2120
2183
|
id: value.id,
|
|
2121
2184
|
taskId: value.taskId,
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
terminalizedLeaderRun: data[TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW] === true
|
|
2185
|
+
reviewerRoleName: value.reviewerRoleName,
|
|
2186
|
+
status: value.status
|
|
2125
2187
|
}
|
|
2126
2188
|
: undefined;
|
|
2127
2189
|
}
|