@zq-silk/yui 0.11.3 → 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.
Files changed (86) hide show
  1. package/ARCHITECTURE.md +23 -4
  2. package/README.md +36 -13
  3. package/dist/cli/commandCatalog.js +2 -4
  4. package/dist/cli.js +105 -43
  5. package/dist/commands/configCommands.js +12 -45
  6. package/dist/commands/executionAuditCommands.js +1 -1
  7. package/dist/commands/jobCommands.js +4 -10
  8. package/dist/commands/taskCommands.js +154 -171
  9. package/dist/commands/taskCompletionGate.js +29 -54
  10. package/dist/commands/taskContextCommand.js +55 -11
  11. package/dist/commands/taskInputCommands.js +8 -5
  12. package/dist/commands/taskNextActionCommand.js +56 -6
  13. package/dist/commands/taskOverviewCommand.js +17 -31
  14. package/dist/commands/taskRoleRuntimeStatus.js +24 -0
  15. package/dist/commands/taskWorkspaceCommands.js +5 -2
  16. package/dist/config/configCatalog.js +2 -2
  17. package/dist/config/yuiConfig.js +3 -7
  18. package/dist/context/wakeNotification.js +20 -5
  19. package/dist/controller/agentRuntimeObserver.js +247 -51
  20. package/dist/controller/clientRuntime.js +1 -1
  21. package/dist/controller/controller.js +8 -12
  22. package/dist/controller/fileSchedulerStoreAdapter.js +146 -198
  23. package/dist/controller/runtime.js +3 -3
  24. package/dist/controller/runtimeEventInbox.js +17 -7
  25. package/dist/controller/runtimeEventProcessor.js +12 -19
  26. package/dist/controller/runtimeObservationHook.js +45 -0
  27. package/dist/execution/resourceBroker.js +5 -4
  28. package/dist/executor/agentExecutor.js +4 -4
  29. package/dist/executor/effectiveLaunch.js +8 -48
  30. package/dist/executor/fileRoleLaunchPlanner.js +36 -16
  31. package/dist/executor/workspacePreflightClassification.js +23 -2
  32. package/dist/interaction/operatorPresentation.js +33 -89
  33. package/dist/lifecycle/exactRunTerminalization.js +1 -2
  34. package/dist/lifecycle/taskRoleSessionReset.js +5 -13
  35. package/dist/observability/orchestrationMetrics.js +8 -3
  36. package/dist/profile/agentProfile.js +1 -1
  37. package/dist/release/cliHomeReleaseFence.js +123 -0
  38. package/dist/repository/taskWorkspacePreparer.js +176 -60
  39. package/dist/resources/sqliteResourceRegistry.js +1 -1
  40. package/dist/review/deltaRecheck.js +12 -51
  41. package/dist/review/reviewAcceptance.js +26 -0
  42. package/dist/review/reviewConfig.js +0 -31
  43. package/dist/review/reviewDecision.js +113 -0
  44. package/dist/review/reviewOutcomeClassifier.js +1 -1
  45. package/dist/review/reviewRound.js +1 -1
  46. package/dist/review/reviewerAvailability.js +69 -0
  47. package/dist/runtime/agentDriverObservation.js +24 -10
  48. package/dist/runtime/builtinAgentDrivers.js +4 -3
  49. package/dist/runtime/builtinTranscriptObserver.js +301 -64
  50. package/dist/runtime/builtinTranscriptUsage.js +9 -7
  51. package/dist/runtime/index.js +1 -0
  52. package/dist/runtime/runtimeObservation.js +13 -0
  53. package/dist/runtime/runtimeProjection.js +16 -25
  54. package/dist/runtime/sessionTokenMetrics.js +181 -0
  55. package/dist/runtime/structuredProviderHost.js +7 -1
  56. package/dist/scheduler/activeRoleRunDelivery.js +4 -4
  57. package/dist/scheduler/activeTaskProgress.js +7 -6
  58. package/dist/scheduler/leaderWakeupProcessor.js +3 -5
  59. package/dist/scheduler/operatorEvent.js +34 -0
  60. package/dist/scheduler/operatorInputNotificationProcessor.js +54 -94
  61. package/dist/scheduler/roleRunStall.js +0 -14
  62. package/dist/scheduler/taskExecutionProjection.js +97 -76
  63. package/dist/scheduler/taskObservabilityProjection.js +10 -11
  64. package/dist/storage/migration/productionRegistry.js +379 -0
  65. package/dist/storage/sqliteSchema.js +18 -8
  66. package/dist/storage/sqliteStore.js +24 -20
  67. package/dist/storage/storeRpc.js +0 -1
  68. package/dist/storage/taskStore.js +15 -30
  69. package/dist/storage/upgrade/recordVersions.js +3 -2
  70. package/dist/storage/upgrade/sqliteMigrationTarget.js +30 -8
  71. package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +23 -11
  72. package/dist/storage/upgrade/sqliteStateMigration.js +66 -8
  73. package/dist/task/completionReadiness.js +10 -6
  74. package/dist/task/nextAction.js +82 -24
  75. package/dist/telemetry/sqliteTelemetryStore.js +1 -1
  76. package/dist/web/assets/client/components.js +13 -3
  77. package/dist/web/assets/client/i18n.js +6 -2
  78. package/dist/web/webSnapshot.js +4 -0
  79. package/i18n/README.zh-CN.md +2 -2
  80. package/package.json +1 -1
  81. package/skills/yui-leader/SKILL.md +53 -16
  82. package/skills/yui-operator/SKILL.md +9 -0
  83. package/skills/yui-reviewer/SKILL.md +28 -7
  84. package/dist/context/sessionContextBudget.js +0 -71
  85. package/dist/lifecycle/contextBudgetRollover.js +0 -81
  86. 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 owns one workspace root
132
- containing a managed main worktree for each binding. The
133
- `<workspace>/tasks/<task>/main` root is a logical multi-Project container, not a
134
- Git repository. Each Project child (for example
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
@@ -390,14 +396,16 @@ acceptance so the Leader can accept it directly or run
390
396
  Leader-managed candidates awaiting a decision instead of marking them done.
391
397
  `final` does not create WorkItem ReviewRounds or decide Task topology. The
392
398
  Leader explicitly requests a Task-scoped Review, unless an immutable Task
393
- contract requires one. The Round freezes Task main directly, so even a
394
- Leader-owned Task with no WorkItem can be reviewed. A changed frozen head needs
395
- a new semantic Round; the same compatible Reviewer Session may continue in its
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
396
403
  stable workspace, while every Run remains bound to its exact Round and head.
397
404
  The Reviewer follows Project Policy/Knowledge and reports reachable, material,
398
405
  actionable findings across the complete Task.
399
- A ReviewRound freezes the Candidate's exact Git commit and creates a fresh,
400
- ReviewRound-owned writable worktree on a unique branch. Its AgentRun may edit,
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,
401
409
  test, and optionally commit diagnostic evidence there, but never changes the
402
410
  Candidate or Worker workspace and never creates another WorkItem, Candidate,
403
411
  ChangeSet, or recursive review. The result wakes the Leader, who decides whether
@@ -405,6 +413,17 @@ to route evidence to the original Worker, accept, reject and redispatch that
405
413
  Worker in its existing Session, review again, or request user input.
406
414
  A failed review remains visible evidence and wakes the Leader, but does not
407
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.
408
427
  Candidate history, every ReviewRound, and the Leader decision remain grouped
409
428
  under the original WorkItem. A rejected result creates a new Candidate on the
410
429
  next dispatch while reusing the original execution Role, Session, and
@@ -594,11 +613,14 @@ Every stage is a new immutable ExecutionGroup. Its ContextSnapshot and selected
594
613
  parent Lane results are durable references; `retry` repeats a stage within its
595
614
  attempt budget, while `retry` at Resolve begins the next bounded round.
596
615
 
597
- Each new stage also freezes one Resource Broker contract: token, tool-call and
598
- wall-clock budgets; quorum and deadline; a straggler window; and the minimum
599
- marginal value for more Lane spend. Omitted values reuse the existing context
600
- budget and runtime-health windows; stage retries share the original cumulative
601
- spend and absolute deadline. Execution, Lane retry, and Reviewer-panel
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
602
624
  admission all count active Lanes at Home, Task, WorkItem, Group, Provider,
603
625
  Agent, and model scopes. Capacity pressure keeps the excess Lane durably
604
626
  pending instead of failing the Group. Capacity release or deadline arrival
@@ -613,8 +635,9 @@ The Leader may add `--early-stop <0-100>` to an accepting Group resolution.
613
635
  Yui permits it only after quorum and T5's passed Verify/Resolve evidence prove
614
636
  sufficiency. It may skip Lanes that never started; active stragglers are
615
637
  reported and retained, never killed automatically for cost. If evidence is
616
- insufficient, budget or deadline exhaustion blocks the stage for Leader
617
- judgment instead of turning thin evidence into success.
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.
618
641
 
619
642
  New exploration histories also freeze the structured candidate-convergence
620
643
  contract. Yui appends the exact stage-local JSON shape to every Lane assignment
@@ -792,7 +815,7 @@ yui task input request <task-id> --question "Which format should be the default?
792
815
 
793
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.
794
817
 
795
- `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.
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.
796
819
 
797
820
  Inspect the result:
798
821
 
@@ -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
  },
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, RESUMED_PENDING_FINAL_REVIEW, TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW, TaskFinalReviewDispatchDriftError, preserveReviewRoundWorkspace, parseTaskCompletionRequest, parseTaskFinalReviewContractRebindRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, resolvedExecutionStageRetryGroup, validateTaskArchiveRequest } from "./commands/taskCommands.js";
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, ReviewRoundWorkspaceEvidenceError } from "./repository/taskWorkspacePreparer.js";
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 { deltaRecheckEnabled } from "./review/reviewConfig.js";
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 = error instanceof Error ? error.message : String(error);
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
- // RFC Phase 3: prepare the Task workspace before activation so a
1127
- // preparation failure keeps the Task in Draft instead of leaving it
1128
- // Active without a workspace.
1129
- if (resolved[1] === "activate") {
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.prepareTaskWorkspace(task.id);
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
- : { deltaRecheckDiff: deltaRecheckPreflight.diffByProject })
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, { runtime, environment: process.env, yuiHome: home });
1245
- reviewOutput = `Review could not start: ${message}\n`;
1246
- reviewData = { reviewRound: failed };
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
- return snapshotActualTaskReviewCandidate(taskId, store, preparer);
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. The assessment's deterministic gates (contiguous base,
2006
- * attempt thresholds, evidence-file overlap) fail closed here; the Reviewer
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) => ((round.scope ?? "work-item") === "task"
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 not allowed: ${assessment.reason} Request a full Review instead.`);
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
- status: value.status,
2123
- resumedPendingFinalReview: data[RESUMED_PENDING_FINAL_REVIEW] === true,
2124
- terminalizedLeaderRun: data[TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW] === true
2185
+ reviewerRoleName: value.reviewerRoleName,
2186
+ status: value.status
2125
2187
  }
2126
2188
  : undefined;
2127
2189
  }
@@ -1,11 +1,11 @@
1
1
  import { mkdirSync, realpathSync } from "node:fs";
2
2
  import { isAbsolute, relative, resolve } from "node:path";
3
3
  import { usageError } from "../errors/cliError.js";
4
- import { DEFAULT_CONTEXT_HARD_TOKENS, DEFAULT_CONTEXT_SOFT_TOKENS, DEFAULT_AGENT_LAUNCH_INACTIVITY_TIMEOUT_SECONDS, DEFAULT_CONTROLLER_TASK_CONCURRENCY, DEFAULT_DELIVERY_TIMEOUT_SECONDS, DEFAULT_LEADER_NEXT_ACTION_MODE, DEFAULT_LEADER_SEMANTIC_BUDGET_TURNS, DEFAULT_PROVIDER_RETRY_DELAYS_SECONDS, DEFAULT_PROVIDER_RETRY_MAX_WINDOW_SECONDS, DEFAULT_PROVIDER_RETRY_MODE, DEFAULT_RECONCILIATION_INTERVAL_SECONDS, DEFAULT_RESOURCES_GC_MODE, DEFAULT_RESOURCES_QUARANTINE_TTL_HOURS, DEFAULT_TMUX_HISTORY_LIMIT, LEADER_NEXT_ACTION_MODES, PROVIDER_RETRY_MODES, reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveContextBudget, resolveDeliveryTimeoutSeconds, resolveLeaderNextActionMode, resolveLeaderSemanticBudgetTurns, resolveProviderRetryAdapters, resolveProviderRetryDelaysSeconds, resolveProviderRetryMaxWindowSeconds, resolveProviderRetryMode, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode, resolveResourcesQuarantineTtlHours, resolveRuntimeHealth, resolveTelemetryEnabled, resolveTelemetryRunCap, resolveTelemetryTerminalKeep, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
4
+ import { DEFAULT_AGENT_LAUNCH_INACTIVITY_TIMEOUT_SECONDS, DEFAULT_CONTROLLER_TASK_CONCURRENCY, DEFAULT_DELIVERY_TIMEOUT_SECONDS, DEFAULT_LEADER_NEXT_ACTION_MODE, DEFAULT_LEADER_SEMANTIC_BUDGET_TURNS, DEFAULT_PROVIDER_RETRY_DELAYS_SECONDS, DEFAULT_PROVIDER_RETRY_MAX_WINDOW_SECONDS, DEFAULT_PROVIDER_RETRY_MODE, DEFAULT_RECONCILIATION_INTERVAL_SECONDS, DEFAULT_RESOURCES_GC_MODE, DEFAULT_RESOURCES_QUARANTINE_TTL_HOURS, DEFAULT_TMUX_HISTORY_LIMIT, LEADER_NEXT_ACTION_MODES, PROVIDER_RETRY_MODES, reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveContextBudget, resolveDeliveryTimeoutSeconds, resolveLeaderNextActionMode, resolveLeaderSemanticBudgetTurns, resolveProviderRetryAdapters, resolveProviderRetryDelaysSeconds, resolveProviderRetryMaxWindowSeconds, resolveProviderRetryMode, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode, resolveResourcesQuarantineTtlHours, resolveRuntimeHealth, resolveTelemetryEnabled, resolveTelemetryRunCap, resolveTelemetryTerminalKeep, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
5
5
  import { CONFIG_DEFINITIONS, CONFIG_KEYS, configDefinition, configDefinitionsForDomain } from "../config/configCatalog.js";
6
6
  import { resolveTimeZone } from "../output/timePresentation.js";
7
7
  import { defaultTableWidth, renderTable } from "../output/table.js";
8
- import { DEFAULT_DELTA_RECHECK_MAX_CHANGED_FILES, DEFAULT_DELTA_RECHECK_MAX_CHANGED_LINES, REVIEW_FINDING_LEDGER_MODES, REVIEW_DELTA_RECHECK_MODES, REVIEW_TRIGGERS } from "../review/reviewConfig.js";
8
+ import { REVIEW_FINDING_LEDGER_MODES, REVIEW_TRIGGERS } from "../review/reviewConfig.js";
9
9
  /**
10
10
  * One uniform key model for every durable setting. Each catalog domain uses
11
11
  * the same show/set/clear contract; strategy settings such as `review` and
@@ -23,8 +23,7 @@ const RESOURCES_GC_AUTO_QUARANTINE_SET_USAGE = "Resources config set usage: yui
23
23
  const LEADER_NEXT_ACTION_SET_USAGE = `Workflow config set usage: yui config workflow set leader-next-action <${LEADER_NEXT_ACTION_MODES.join("|")}>.`;
24
24
  const CONTEXT_BUDGET_SET_USAGE = "Workflow config set usage: yui config workflow set context-budget [--soft-tokens <n>] [--hard-tokens <n>].";
25
25
  const REVIEW_SET_USAGE = "Workflow config set usage: yui config workflow set review --role <global-role> --trigger <always|leader|final> "
26
- + "[--finding-ledger <shadow|enforce>] [--delta-recheck <enabled|disabled>] "
27
- + "[--delta-recheck-max-lines <n>] [--delta-recheck-max-files <n>].";
26
+ + "[--finding-ledger <shadow|enforce>].";
28
27
  export function runConfigCommand(domain, args, store) {
29
28
  const keys = configDefinitionsForDomain(domain).map(({ key }) => key);
30
29
  const usage = `${capitalize(domain)} config usage: yui config ${domain} show | yui config ${domain} set <key> <value...> | yui config ${domain} clear <key>.`;
@@ -97,12 +96,7 @@ export function effectiveConfigData(config, domain) {
97
96
  : {
98
97
  roleName: config.review.roleName,
99
98
  trigger: config.review.trigger,
100
- findingLedger: config.review.findingLedger ?? "shadow",
101
- deltaRecheck: config.review.deltaRecheck ?? "disabled",
102
- deltaRecheckMaxChangedLines: config.review.deltaRecheckMaxChangedLines
103
- ?? DEFAULT_DELTA_RECHECK_MAX_CHANGED_LINES,
104
- deltaRecheckMaxChangedFiles: config.review.deltaRecheckMaxChangedFiles
105
- ?? DEFAULT_DELTA_RECHECK_MAX_CHANGED_FILES
99
+ findingLedger: config.review.findingLedger ?? "shadow"
106
100
  },
107
101
  completionInstallations: config.completionInstallations ?? {}
108
102
  };
@@ -175,14 +169,6 @@ function parseReconciliationIntervalSeconds(value) {
175
169
  function parseBooleanConfigValue(value) {
176
170
  return value === "true" ? true : value === "false" ? false : value;
177
171
  }
178
- function parsePositiveIntegerOption(value, usage) {
179
- if (value === undefined)
180
- return undefined;
181
- if (!/^\d+$/.test(value) || Number(value) < 1) {
182
- throw usageError(usage);
183
- }
184
- return Number(value);
185
- }
186
172
  const CONFIG_KEY_HANDLERS = [
187
173
  {
188
174
  key: "default-agent",
@@ -313,10 +299,10 @@ const CONFIG_KEY_HANDLERS = [
313
299
  },
314
300
  {
315
301
  key: "context-budget",
316
- showLabel: "Context budget",
302
+ showLabel: "Legacy context budget (inactive)",
317
303
  showValue: (config) => {
318
304
  const budget = resolveContextBudget(config.contextBudget);
319
- return `soft ${budget.softTokens} / hard ${budget.hardTokens} tokens`;
305
+ return `inactive; legacy soft ${budget.softTokens} / hard ${budget.hardTokens} tokens`;
320
306
  },
321
307
  set(args, store) {
322
308
  if (args.length !== 2 && args.length !== 4)
@@ -343,14 +329,14 @@ const CONFIG_KEY_HANDLERS = [
343
329
  };
344
330
  const resolved = resolveContextBudget({ ...current, ...next });
345
331
  saveConfigKey(store, (config) => ({ ...config, contextBudget: resolved }));
346
- return `Context budget set to soft ${resolved.softTokens} / hard ${resolved.hardTokens} tokens\n`;
332
+ return `Legacy context budget stored as soft ${resolved.softTokens} / hard ${resolved.hardTokens} tokens (inactive)\n`;
347
333
  },
348
334
  clear(store) {
349
335
  saveConfigKey(store, (config) => {
350
336
  const { contextBudget: _removed, ...rest } = config;
351
337
  return rest;
352
338
  });
353
- return `Context budget reset to soft ${DEFAULT_CONTEXT_SOFT_TOKENS} / hard ${DEFAULT_CONTEXT_HARD_TOKENS} tokens\n`;
339
+ return "Legacy context budget cleared; Session Token metrics remain read-only\n";
354
340
  }
355
341
  },
356
342
  {
@@ -696,7 +682,7 @@ const CONFIG_KEY_HANDLERS = [
696
682
  showValue: (config) => (config.review === undefined
697
683
  ? "disabled"
698
684
  : `${config.review.roleName} (${config.review.trigger}; finding ledger: ${config.review.findingLedger ?? "shadow"}`
699
- + `${config.review.deltaRecheck === "enabled" ? "; delta recheck: enabled" : ""})`),
685
+ + ")"),
700
686
  set(args, store) {
701
687
  if (args.length < 4 || args.length % 2 !== 0)
702
688
  throw usageError(REVIEW_SET_USAGE);
@@ -704,8 +690,7 @@ const CONFIG_KEY_HANDLERS = [
704
690
  for (let index = 0; index < args.length; index += 2) {
705
691
  const name = args[index];
706
692
  const value = args[index + 1];
707
- if (!["--role", "--trigger", "--finding-ledger", "--delta-recheck",
708
- "--delta-recheck-max-lines", "--delta-recheck-max-files"].includes(name)
693
+ if (!["--role", "--trigger", "--finding-ledger"].includes(name)
709
694
  || value === undefined
710
695
  || options.has(name)) {
711
696
  throw usageError(REVIEW_SET_USAGE);
@@ -731,33 +716,15 @@ const CONFIG_KEY_HANDLERS = [
731
716
  }
732
717
  findingLedger = rawLedgerMode;
733
718
  }
734
- const rawDeltaRecheck = options.get("--delta-recheck")?.trim();
735
- let deltaRecheck;
736
- if (rawDeltaRecheck !== undefined) {
737
- if (!REVIEW_DELTA_RECHECK_MODES.includes(rawDeltaRecheck)) {
738
- throw usageError(REVIEW_SET_USAGE);
739
- }
740
- deltaRecheck = rawDeltaRecheck;
741
- }
742
- const deltaRecheckMaxChangedLines = parsePositiveIntegerOption(options.get("--delta-recheck-max-lines"), REVIEW_SET_USAGE);
743
- const deltaRecheckMaxChangedFiles = parsePositiveIntegerOption(options.get("--delta-recheck-max-files"), REVIEW_SET_USAGE);
744
719
  saveConfigKey(store, (config) => ({
745
720
  ...config,
746
721
  review: {
747
722
  roleName,
748
723
  trigger,
749
- ...(findingLedger === undefined ? {} : { findingLedger }),
750
- ...(deltaRecheck === undefined ? {} : { deltaRecheck }),
751
- ...(deltaRecheckMaxChangedLines === undefined
752
- ? {}
753
- : { deltaRecheckMaxChangedLines }),
754
- ...(deltaRecheckMaxChangedFiles === undefined
755
- ? {}
756
- : { deltaRecheckMaxChangedFiles })
724
+ ...(findingLedger === undefined ? {} : { findingLedger })
757
725
  }
758
726
  }));
759
- return `Review set to ${roleName} (${trigger}; finding ledger: ${findingLedger ?? "shadow"}; `
760
- + `delta recheck: ${deltaRecheck ?? "disabled"})\n`;
727
+ return `Review set to ${roleName} (${trigger}; finding ledger: ${findingLedger ?? "shadow"})\n`;
761
728
  },
762
729
  clear(store) {
763
730
  saveConfigKey(store, (config) => {
@@ -155,7 +155,7 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
155
155
  `Delta-rechecks: ${reviews.deltaRechecks.total} total · `
156
156
  + `${reviews.deltaRechecks.equivalentAndAccepted} accepted · `
157
157
  + `${reviews.deltaRechecks.finding} finding · `
158
- + `${reviews.deltaRechecks.requiresFullReview} escalated to full Review`
158
+ + `${reviews.deltaRechecks.requiresFullReview} requiring Leader decision`
159
159
  ]));
160
160
  }
161
161
  else {