@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.
- package/ARCHITECTURE.md +23 -4
- package/README.md +36 -13
- package/dist/cli/commandCatalog.js +2 -4
- 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/taskCommands.js +154 -171
- package/dist/commands/taskCompletionGate.js +29 -54
- package/dist/commands/taskContextCommand.js +55 -11
- package/dist/commands/taskInputCommands.js +8 -5
- 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/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 +379 -0
- package/dist/storage/sqliteSchema.js +18 -8
- package/dist/storage/sqliteStore.js +24 -20
- package/dist/storage/storeRpc.js +0 -1
- package/dist/storage/taskStore.js +15 -30
- 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
|
|
@@ -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
|
|
394
|
-
Leader-owned Task with no WorkItem can be reviewed
|
|
395
|
-
|
|
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
|
|
400
|
-
|
|
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:
|
|
598
|
-
wall-clock budgets; quorum and deadline; a
|
|
599
|
-
marginal value for more Lane spend. Omitted
|
|
600
|
-
budget and runtime-health windows; stage
|
|
601
|
-
|
|
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
|
|
617
|
-
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.
|
|
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
|
|
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,
|
|
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
|
}
|
|
@@ -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 {
|
|
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 {
|
|
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>]
|
|
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: "
|
|
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 `
|
|
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
|
|
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
|
-
+
|
|
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"
|
|
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}
|
|
158
|
+
+ `${reviews.deltaRechecks.requiresFullReview} requiring Leader decision`
|
|
159
159
|
]));
|
|
160
160
|
}
|
|
161
161
|
else {
|