@zq-silk/yui 0.12.3 → 0.13.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/dist/cli/commandCatalog.js +2 -2
- package/dist/cli/updatePorts.js +18 -2
- package/dist/cli.js +3 -6
- package/dist/commands/durableJobCommands.js +6 -18
- package/dist/commands/executionAuditCommands.js +1 -1
- package/dist/commands/taskActor.js +35 -61
- package/dist/commands/taskCommands.js +260 -200
- package/dist/commands/taskIntegrationCommands.js +33 -24
- package/dist/commands/taskIntegrationQueueCommands.js +16 -18
- package/dist/commands/taskPublicationCommands.js +0 -4
- package/dist/commands/taskWorkspaceCommands.js +1 -1
- package/dist/context/runContextPack.js +15 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +17 -1
- package/dist/controller/jobClient.js +5 -8
- package/dist/controller/jobControl.js +59 -105
- package/dist/executor/workspacePreflightClassification.js +6 -5
- package/dist/integration/gitIntegrationService.js +3 -3
- package/dist/integration/integrationAttempt.js +13 -9
- package/dist/integration/integrationQueueService.js +7 -7
- package/dist/lifecycle/exactRunTerminalization.js +146 -6
- package/dist/milestone/milestone.js +9 -3
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewFinding.js +3 -3
- package/dist/review/reviewRound.js +11 -8
- package/dist/run/rejectedYieldAttempt.js +221 -0
- package/dist/runtime/tmuxAdapters.js +12 -3
- package/dist/scheduler/activeRoleRunDelivery.js +4 -0
- package/dist/storage/migration/productionRegistry.js +9 -0
- package/dist/storage/taskStore.js +9 -8
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +4 -0
- package/skills/yui-operator/SKILL.md +19 -13
|
@@ -7,6 +7,7 @@ import { CliError, dataError, roleNotFound, runtimeError, taskNotFound, usageErr
|
|
|
7
7
|
import { createTaskEvent } from "../event/taskEvent.js";
|
|
8
8
|
import { createTaskRecordRetirement, isTaskRecordRetired, taskRecordRetirement } from "../task/taskRecordRetirement.js";
|
|
9
9
|
import { isRoleRunStalled, RUN_PROGRESS_EVENT, RUN_RECOVERED_EVENT } from "../scheduler/roleRunStall.js";
|
|
10
|
+
import { routeRoleEvent } from "../scheduler/operatorEvent.js";
|
|
10
11
|
import { readCommandText } from "./textInput.js";
|
|
11
12
|
import { assertTaskCompletionPublishedTreeProof } from "./taskCompletionGate.js";
|
|
12
13
|
import { createRoleSessionSet, retireTaskRoleSessionsForWorkspace, updateTaskRoleProviderRuntime } from "../executor/agentExecutor.js";
|
|
@@ -18,17 +19,18 @@ import { formatTimestamp } from "../output/timePresentation.js";
|
|
|
18
19
|
import { renderRoleDetails } from "../output/rolePresentation.js";
|
|
19
20
|
import { createTaskMessage, taskMessageAuthorLabel } from "../message/message.js";
|
|
20
21
|
import { cancelInputRequest } from "../input/inputRequest.js";
|
|
21
|
-
import { recoverExactAgentRun, terminalizeExactTaskRun, validateExactRunReviewRound } from "../lifecycle/exactRunTerminalization.js";
|
|
22
|
+
import { recoverExactAgentRun, retireExactActiveAgentRun, terminalizeExactTaskRun, validateExactRunReviewRound } from "../lifecycle/exactRunTerminalization.js";
|
|
22
23
|
import { copyGlobalRoleToTaskRole, createRole, createRoleAgentBinding, switchActiveRoleAgent, unbindRoleAgent, updateRole, updateRoleStatus } from "../role/role.js";
|
|
23
24
|
import { agentRunDeliveryReceiptId, createAgentRun, withAgentRunContextSnapshot } from "../run/agentRun.js";
|
|
24
25
|
import { projectRunRecovery, readRunRecoveryFacts } from "../run/recoveryProjection.js";
|
|
25
26
|
import { matchYieldReceipt } from "../run/yieldReceipt.js";
|
|
27
|
+
import { createRejectedYieldAttempt, rejectedYieldAttemptEventPayload, rejectedYieldAttemptFromTaskEvent, RUN_YIELD_REJECTED_EVENT } from "../run/rejectedYieldAttempt.js";
|
|
26
28
|
import { createReviewRound, createTaskReviewRound, createTaskDeltaReviewRound, attachReviewExecutionGroup, deltaRecheckBlocksAcceptance, finishReviewRound, parseReviewYieldReport, recordReviewWorkspaceDisposition, retryRunningReviewExecutionLane, retryTaskReviewRound, startReviewRound, updateReviewExecutionGroup, validateTaskReviewCandidate } from "../review/reviewRound.js";
|
|
27
29
|
import { buildDeltaRecheckDispatchContext, verifyDeltaRecheckDiff } from "../review/deltaRecheck.js";
|
|
28
30
|
import { isAcceptedTaskReviewBaseline } from "../review/reviewAcceptance.js";
|
|
29
31
|
import { projectReviewerAvailability } from "../review/reviewerAvailability.js";
|
|
30
32
|
import { buildTaskFinalReviewFindingContext, dispositionReviewFinding, planRepairGroups, reconcileReviewFindings, reconcileReviewFindingsAfterReview } from "../review/reviewFindingLedger.js";
|
|
31
|
-
import {
|
|
33
|
+
import { TASK_CONTROL_FINDING_DISPOSITIONS } from "../review/reviewFinding.js";
|
|
32
34
|
import { createTaskBrief, updateTaskBrief } from "../brief/taskBrief.js";
|
|
33
35
|
import { createDecision, supersedeDecision } from "../decision/decision.js";
|
|
34
36
|
import { createMilestone } from "../milestone/milestone.js";
|
|
@@ -40,7 +42,6 @@ import { blockingProviderContinuations, projectProviderContinuations, runOwnsBlo
|
|
|
40
42
|
import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
|
|
41
43
|
import { addTaskProjectBinding, archiveTask, completeTask, createTask, retireTask, reopenTask, updateTaskMetadata } from "../task/task.js";
|
|
42
44
|
import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
|
|
43
|
-
import { TASK_COMPLETION_PUBLISHED_TREE_AUTHORIZED_EVENT } from "../task/publicationReference.js";
|
|
44
45
|
import { projectCompletionReadiness } from "../task/completionReadiness.js";
|
|
45
46
|
import { assertProjectActive, resolveProject } from "../repository/project.js";
|
|
46
47
|
import { acquireProjectMaintenanceLocks } from "../repository/projectMaintenanceLock.js";
|
|
@@ -63,7 +64,7 @@ import { inspectTaskRoleRuntimeStatuses, renderTaskRoleRuntimeStatus, taskRoleAc
|
|
|
63
64
|
import { assertNoOpenInputRequests, isCurrentGlobalOperator, openInputRequestCount, runTaskInputCommand } from "./taskInputCommands.js";
|
|
64
65
|
import { runGrantCommand } from "./grantCommands.js";
|
|
65
66
|
import { runWorkflowCommand } from "./workflowCommands.js";
|
|
66
|
-
import {
|
|
67
|
+
import { taskLocalActor as resolveTaskLocalActor, taskLeaderActionRunId } from "./taskActor.js";
|
|
67
68
|
import { enqueueOperatorEvent } from "../scheduler/operatorEvent.js";
|
|
68
69
|
import { queueLeaderWakeup } from "../scheduler/wakeupQueue.js";
|
|
69
70
|
import { renderWakeReason, wakeReason } from "../scheduler/wakeReason.js";
|
|
@@ -164,7 +165,7 @@ export function parseTaskCompletionRequest(args, summaryOverride) {
|
|
|
164
165
|
*/
|
|
165
166
|
export function preflightTaskCompletion(taskId, store, options = {}, request = {}) {
|
|
166
167
|
const task = requireTask(store, taskId);
|
|
167
|
-
const actor = taskActor(options, task.id);
|
|
168
|
+
const actor = taskActor(store, options, task.id);
|
|
168
169
|
if (task.status === "completed") {
|
|
169
170
|
return { task, actor, completed: true, activeTaskReview: false };
|
|
170
171
|
}
|
|
@@ -201,9 +202,6 @@ export function preflightTaskCompletion(taskId, store, options = {}, request = {
|
|
|
201
202
|
.filter((entry) => entry.run !== null);
|
|
202
203
|
const leaderEntry = activeRuns.find(({ role }) => role.name === LEADER_ROLE);
|
|
203
204
|
if (leaderEntry !== undefined) {
|
|
204
|
-
if (actor !== "leader") {
|
|
205
|
-
throw usageError(`Task ${task.id} has an active Leader run.`);
|
|
206
|
-
}
|
|
207
205
|
if (leaderEntry.run.workItemId !== undefined) {
|
|
208
206
|
throw usageError(`Task ${task.id} has running work: ${leaderEntry.run.workItemId}.`);
|
|
209
207
|
}
|
|
@@ -379,9 +377,7 @@ function taskProjectCommand(args, store, options) {
|
|
|
379
377
|
const updated = store.transaction((tx) => {
|
|
380
378
|
const task = requireTask(tx, parsed.positionals[0]);
|
|
381
379
|
assertTaskOpen(task);
|
|
382
|
-
|
|
383
|
-
throw usageError("Only the Task Leader may add a Project to an active Task.");
|
|
384
|
-
}
|
|
380
|
+
taskActor(tx, options, task.id);
|
|
385
381
|
const project = resolveProject(tx.listProjects(), parsed.positionals[1]);
|
|
386
382
|
if (project === null)
|
|
387
383
|
throw usageError(`Project not found: ${parsed.positionals[1]}.`);
|
|
@@ -444,6 +440,7 @@ function updateTaskCommand(args, store, options) {
|
|
|
444
440
|
const current = requireTask(tx, parsed.positionals[0]);
|
|
445
441
|
if (current.status === "archived")
|
|
446
442
|
throw usageError(`Task is archived: ${current.id}.`);
|
|
443
|
+
taskActor(tx, options, current.id);
|
|
447
444
|
const updated = updateTaskMetadata(current, {
|
|
448
445
|
...(parsed.options.has("--title") ? { title: requiredOption(parsed.options, "--title") } : {}),
|
|
449
446
|
...(parsed.options.has("--type")
|
|
@@ -720,8 +717,7 @@ function completeTaskCommand(args, store, options) {
|
|
|
720
717
|
changed: false,
|
|
721
718
|
runtimeCleanupTargets: [],
|
|
722
719
|
completionAdvisories: [],
|
|
723
|
-
finalReview: undefined
|
|
724
|
-
publishedTreeAuthorization: undefined
|
|
720
|
+
finalReview: undefined
|
|
725
721
|
};
|
|
726
722
|
}
|
|
727
723
|
const taskFinalContract = preflight.taskFinalReviewContract;
|
|
@@ -731,28 +727,6 @@ function completeTaskCommand(args, store, options) {
|
|
|
731
727
|
const publishedTreeProof = request.acceptedPublishedTreePublicationId === undefined
|
|
732
728
|
? undefined
|
|
733
729
|
: assertTaskCompletionPublishedTreeProof(tx, task, request.acceptedPublishedTreePublicationId, options.completionPublishedTreeProof, actualTaskCandidate);
|
|
734
|
-
const requiresContractHandoff = publishedTreeProof !== undefined
|
|
735
|
-
&& taskFinalContract !== undefined;
|
|
736
|
-
if (requiresContractHandoff && actor !== "leader") {
|
|
737
|
-
const existing = matchingPublishedTreeAuthorization(tx, publishedTreeProof);
|
|
738
|
-
const event = existing ?? recordTaskEventRecord(tx, task.id, TASK_COMPLETION_PUBLISHED_TREE_AUTHORIZED_EVENT, publishedTreeAuthorizationPayload(actor, publishedTreeProof), now);
|
|
739
|
-
enqueueWork(tx, leaderMailbox(task.id), wakeReason("published-tree-authorized", event.id), now, [eventRef(task.id, event.id)]);
|
|
740
|
-
return {
|
|
741
|
-
task,
|
|
742
|
-
changed: false,
|
|
743
|
-
runtimeCleanupTargets: [],
|
|
744
|
-
completionAdvisories: [],
|
|
745
|
-
finalReview: undefined,
|
|
746
|
-
publishedTreeAuthorization: {
|
|
747
|
-
event,
|
|
748
|
-
proof: publishedTreeProof,
|
|
749
|
-
created: existing === undefined
|
|
750
|
-
}
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
if (publishedTreeProof !== undefined && actor === "leader") {
|
|
754
|
-
requirePublishedTreeAuthorization(tx, publishedTreeProof);
|
|
755
|
-
}
|
|
756
730
|
const roles = tx.listRoles(task.id);
|
|
757
731
|
const activeRuns = roles
|
|
758
732
|
.map((role) => ({ role, run: tx.getActiveAgentRun(task.id, role.name) }))
|
|
@@ -763,9 +737,6 @@ function completeTaskCommand(args, store, options) {
|
|
|
763
737
|
// the post-Review readiness fence below.
|
|
764
738
|
const leaderEntry = activeRuns.find(({ role }) => role.name === LEADER_ROLE);
|
|
765
739
|
if (leaderEntry !== undefined) {
|
|
766
|
-
if (actor !== "leader") {
|
|
767
|
-
throw usageError(`Task ${task.id} has an active Leader run.`);
|
|
768
|
-
}
|
|
769
740
|
if (leaderEntry.run.workItemId !== undefined) {
|
|
770
741
|
throw usageError(`Task ${task.id} has running work: ${leaderEntry.run.workItemId}.`);
|
|
771
742
|
}
|
|
@@ -796,7 +767,6 @@ function completeTaskCommand(args, store, options) {
|
|
|
796
767
|
runtimeCleanupTargets: [],
|
|
797
768
|
completionAdvisories: [],
|
|
798
769
|
finalReview,
|
|
799
|
-
publishedTreeAuthorization: undefined
|
|
800
770
|
};
|
|
801
771
|
}
|
|
802
772
|
// Issue 06: re-validate the full completion readiness inside the
|
|
@@ -860,21 +830,9 @@ function completeTaskCommand(args, store, options) {
|
|
|
860
830
|
changed: true,
|
|
861
831
|
runtimeCleanupTargets,
|
|
862
832
|
completionAdvisories: readiness.advisories,
|
|
863
|
-
finalReview: undefined
|
|
864
|
-
publishedTreeAuthorization: undefined
|
|
833
|
+
finalReview: undefined
|
|
865
834
|
};
|
|
866
835
|
});
|
|
867
|
-
if (result.publishedTreeAuthorization !== undefined) {
|
|
868
|
-
notifyMailbox(options.runtime, leaderMailbox(result.task.id), result.task.id);
|
|
869
|
-
const authorization = result.publishedTreeAuthorization;
|
|
870
|
-
return output(authorization.created
|
|
871
|
-
? `Authorized published-tree completion for ${result.task.id} as ${authorization.event.id}; exact Task Leader completion is required.\n`
|
|
872
|
-
: `Published-tree completion is already authorized for ${result.task.id} as ${authorization.event.id}; exact Task Leader completion is required.\n`, {
|
|
873
|
-
task: result.task,
|
|
874
|
-
authorizationEvent: authorization.event,
|
|
875
|
-
publishedTreeProof: authorization.proof
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
836
|
if (result.changed) {
|
|
879
837
|
for (const target of result.runtimeCleanupTargets) {
|
|
880
838
|
// Cleanup owns an independent lifecycle lane. Never fall back to the
|
|
@@ -952,7 +910,7 @@ function archiveTaskCommand(args, store, options) {
|
|
|
952
910
|
const now = clock(options);
|
|
953
911
|
const result = store.transaction((tx) => {
|
|
954
912
|
const task = requireTask(tx, request.taskId);
|
|
955
|
-
const actor = taskActor(options, task.id);
|
|
913
|
+
const actor = taskActor(tx, options, task.id);
|
|
956
914
|
if (task.status === "archived")
|
|
957
915
|
return { task, changed: false };
|
|
958
916
|
if (task.status !== "completed"
|
|
@@ -1016,7 +974,7 @@ function retireTaskCommand(args, store, options) {
|
|
|
1016
974
|
const now = clock(options);
|
|
1017
975
|
const result = store.transaction((tx) => {
|
|
1018
976
|
const task = requireTask(tx, taskId);
|
|
1019
|
-
const actor = taskActor(options, task.id);
|
|
977
|
+
const actor = taskActor(tx, options, task.id);
|
|
1020
978
|
if (task.status === "retired") {
|
|
1021
979
|
const same = task.retirementSummary === summary
|
|
1022
980
|
&& task.replacementTaskId === replacementTaskId;
|
|
@@ -1144,10 +1102,7 @@ export function parseTaskArchiveArguments(args) {
|
|
|
1144
1102
|
export function validateTaskArchiveRequest(args, store, options = {}) {
|
|
1145
1103
|
const request = parseTaskArchiveArguments(args);
|
|
1146
1104
|
const task = requireTask(store, request.taskId);
|
|
1147
|
-
|
|
1148
|
-
if (actor === "leader") {
|
|
1149
|
-
throw usageError("Only the global Operator may archive a Task from a managed Session.");
|
|
1150
|
-
}
|
|
1105
|
+
taskActor(store, options, task.id);
|
|
1151
1106
|
if (task.status !== "archived"
|
|
1152
1107
|
&& task.status !== "completed"
|
|
1153
1108
|
&& task.status !== "retired") {
|
|
@@ -1201,7 +1156,7 @@ function taskMessageCommand(args, store, options) {
|
|
|
1201
1156
|
const result = store.transaction((tx) => {
|
|
1202
1157
|
const task = requireTask(tx, parsed.positionals[0]);
|
|
1203
1158
|
assertTaskOpen(task);
|
|
1204
|
-
const actor = taskActor(options, task.id);
|
|
1159
|
+
const actor = taskActor(tx, options, task.id);
|
|
1205
1160
|
const message = actor === "leader"
|
|
1206
1161
|
? appendMessage(tx, task.id, body, "role-result", { type: "role", roleName: LEADER_ROLE }, now)
|
|
1207
1162
|
: actor === "operator"
|
|
@@ -1296,10 +1251,7 @@ function retireMessage(args, store, options) {
|
|
|
1296
1251
|
const result = store.transaction((tx) => {
|
|
1297
1252
|
const task = requireTask(tx, reference.taskId);
|
|
1298
1253
|
assertTaskOpen(task);
|
|
1299
|
-
const actor = taskActor(options, task.id);
|
|
1300
|
-
if (actor === "leader") {
|
|
1301
|
-
throw usageError("Only the user or global Operator may retire a Task Message.");
|
|
1302
|
-
}
|
|
1254
|
+
const actor = taskActor(tx, options, task.id);
|
|
1303
1255
|
const message = tx.listMessages(task.id).find(({ id }) => id === reference.localId);
|
|
1304
1256
|
if (message === undefined) {
|
|
1305
1257
|
throw dataError(`Task Message not found: ${task.id}/${reference.localId}.`);
|
|
@@ -1405,7 +1357,7 @@ function switchTaskRoleSession(args, store, options) {
|
|
|
1405
1357
|
throw usageError(inactiveTaskMessage(task, "switching a Provider Conversation"), usage);
|
|
1406
1358
|
}
|
|
1407
1359
|
const role = requireRole(tx, task.id, parsed.positionals[1]);
|
|
1408
|
-
const requestedBy = taskActor(options, task.id);
|
|
1360
|
+
const requestedBy = taskActor(tx, options, task.id);
|
|
1409
1361
|
const leaderRunId = requestedBy === "leader"
|
|
1410
1362
|
? taskLeaderActionRunId(tx, task.id, options.environment, options.yuiHome)
|
|
1411
1363
|
: undefined;
|
|
@@ -1476,6 +1428,7 @@ function addTaskRole(args, store, options) {
|
|
|
1476
1428
|
const result = store.transaction((tx) => {
|
|
1477
1429
|
const task = requireTask(tx, taskId);
|
|
1478
1430
|
assertTaskOpen(task);
|
|
1431
|
+
taskActor(tx, options, task.id);
|
|
1479
1432
|
assertRoleRuntimeMutationAllowed(tx, {
|
|
1480
1433
|
scope: "task",
|
|
1481
1434
|
taskId: task.id,
|
|
@@ -1594,6 +1547,7 @@ function updateTaskRole(args, store, options) {
|
|
|
1594
1547
|
const updated = store.transaction((tx) => {
|
|
1595
1548
|
const task = requireTask(tx, taskId);
|
|
1596
1549
|
assertTaskOpen(task);
|
|
1550
|
+
taskActor(tx, options, task.id);
|
|
1597
1551
|
const role = requireRole(tx, task.id, roleName);
|
|
1598
1552
|
const changesLaunchContext = hasRoleLaunchContextOptions(parsed) || parsed.has("--profile");
|
|
1599
1553
|
const changesAgentConfig = hasAgentConfigOptions(parsed);
|
|
@@ -1640,6 +1594,7 @@ function removeTaskRole(args, store, options) {
|
|
|
1640
1594
|
const removed = store.transaction((tx) => {
|
|
1641
1595
|
const task = requireTask(tx, args[0]);
|
|
1642
1596
|
assertTaskOpen(task);
|
|
1597
|
+
taskActor(tx, options, task.id);
|
|
1643
1598
|
const role = requireRole(tx, task.id, args[1]);
|
|
1644
1599
|
if (role.name === LEADER_ROLE)
|
|
1645
1600
|
throw usageError("The Task Leader role cannot be removed.");
|
|
@@ -1669,6 +1624,7 @@ function bindTaskRole(args, store, options) {
|
|
|
1669
1624
|
const result = store.transaction((tx) => {
|
|
1670
1625
|
const task = requireTask(tx, args[0]);
|
|
1671
1626
|
assertTaskOpen(task);
|
|
1627
|
+
taskActor(tx, options, task.id);
|
|
1672
1628
|
const role = requireRole(tx, task.id, args[1]);
|
|
1673
1629
|
assertRoleRuntimeMutationAllowed(tx, {
|
|
1674
1630
|
scope: "task",
|
|
@@ -1722,6 +1678,8 @@ function unbindTaskRole(args, store, options) {
|
|
|
1722
1678
|
const now = clock(options);
|
|
1723
1679
|
const result = store.transaction((tx) => {
|
|
1724
1680
|
const task = requireTask(tx, args[0]);
|
|
1681
|
+
assertTaskOpen(task);
|
|
1682
|
+
taskActor(tx, options, task.id);
|
|
1725
1683
|
const role = requireRole(tx, task.id, args[1]);
|
|
1726
1684
|
try {
|
|
1727
1685
|
const unbound = unbindRoleAgent(role, tx.getTaskRoleSessionSet(task.id, role.name), args[2], now);
|
|
@@ -1772,6 +1730,7 @@ function transferTaskRoleAuthority(args, store, options, action) {
|
|
|
1772
1730
|
if (task.status !== "active") {
|
|
1773
1731
|
throw usageError(inactiveTaskMessage(task, `${action} Provider authority`));
|
|
1774
1732
|
}
|
|
1733
|
+
taskActor(tx, options, task.id);
|
|
1775
1734
|
const role = requireRole(tx, task.id, args[1]);
|
|
1776
1735
|
const sessions = tx.getTaskRoleSessionSet(task.id, role.name);
|
|
1777
1736
|
const session = sessions?.sessions[role.activeAgentId];
|
|
@@ -1887,6 +1846,7 @@ function createWork(args, store, options) {
|
|
|
1887
1846
|
const item = store.transaction((tx) => {
|
|
1888
1847
|
const task = requireTask(tx, parsed.positionals[0]);
|
|
1889
1848
|
assertTaskOpen(task);
|
|
1849
|
+
taskActor(tx, options, task.id);
|
|
1890
1850
|
for (const dependencyId of parsed.after) {
|
|
1891
1851
|
const dependency = tx.getWorkItem(task.id, dependencyId);
|
|
1892
1852
|
if (dependency === null)
|
|
@@ -1946,9 +1906,7 @@ function updateWorkScope(args, store, options) {
|
|
|
1946
1906
|
const updated = store.transaction((tx) => {
|
|
1947
1907
|
const item = requireWorkItem(tx, parsed.positionals[0], options);
|
|
1948
1908
|
const task = requireTask(tx, item.taskId);
|
|
1949
|
-
|
|
1950
|
-
throw usageError("Only the Task Leader may change a Work Item Project scope.");
|
|
1951
|
-
}
|
|
1909
|
+
taskActor(tx, options, task.id);
|
|
1952
1910
|
if (tx.getActiveAgentRun(task.id, item.assignee ?? "") !== null) {
|
|
1953
1911
|
throw usageError(`Stop the active Work Item Run before changing scope: ${item.id}.`);
|
|
1954
1912
|
}
|
|
@@ -1996,9 +1954,7 @@ function updateWork(args, store, options) {
|
|
|
1996
1954
|
const task = requireTask(tx, current.taskId);
|
|
1997
1955
|
assertTaskOpen(task);
|
|
1998
1956
|
if (current.assignee === undefined) {
|
|
1999
|
-
|
|
2000
|
-
throw usageError(`Only the Task Leader may update unassigned Work Item execution: ${current.id}.`);
|
|
2001
|
-
}
|
|
1957
|
+
taskActor(tx, options, task.id);
|
|
2002
1958
|
if (status === "running") {
|
|
2003
1959
|
assertWorkItemDependenciesCompleted(tx, current);
|
|
2004
1960
|
}
|
|
@@ -2143,6 +2099,7 @@ function dispatchWork(args, store, options) {
|
|
|
2143
2099
|
if (task.status !== "active") {
|
|
2144
2100
|
throw usageError(inactiveTaskMessage(task, "dispatch"));
|
|
2145
2101
|
}
|
|
2102
|
+
taskActor(tx, options, task.id);
|
|
2146
2103
|
const currentGroup = currentWorkItemExecutionGroup(item);
|
|
2147
2104
|
const existingGroup = currentGroup?.resolution === undefined
|
|
2148
2105
|
? currentGroup
|
|
@@ -2190,9 +2147,8 @@ function dispatchWork(args, store, options) {
|
|
|
2190
2147
|
+ `The Task Leader must run "yui task work update ${item.id} running", `
|
|
2191
2148
|
+ "then execute it directly or create native subagents in the Leader Session.");
|
|
2192
2149
|
}
|
|
2193
|
-
if (expanding
|
|
2194
|
-
|
|
2195
|
-
}
|
|
2150
|
+
if (expanding)
|
|
2151
|
+
taskActor(tx, options, task.id);
|
|
2196
2152
|
assertWorkItemDependenciesCompleted(tx, item);
|
|
2197
2153
|
const workspace = tx.getWorkItemWorkspace(task.id, item.id);
|
|
2198
2154
|
if (workspace?.owner.type === "work-item"
|
|
@@ -2572,9 +2528,7 @@ function resolveWorkExecutionGroup(args, store, options) {
|
|
|
2572
2528
|
const task = requireTask(tx, item.taskId);
|
|
2573
2529
|
if (task.status !== "active")
|
|
2574
2530
|
throw usageError(inactiveTaskMessage(task, "resolving an ExecutionGroup"));
|
|
2575
|
-
|
|
2576
|
-
throw usageError("Only the Task Leader may resolve an ExecutionGroup.");
|
|
2577
|
-
}
|
|
2531
|
+
taskActor(tx, options, task.id);
|
|
2578
2532
|
let group = currentWorkItemExecutionGroup(item);
|
|
2579
2533
|
if (group === undefined
|
|
2580
2534
|
|| (group.stage === undefined
|
|
@@ -2806,9 +2760,7 @@ function acceptWork(args, store, options) {
|
|
|
2806
2760
|
if (task.status !== "active") {
|
|
2807
2761
|
throw usageError(`Task is not active: ${task.id}/${task.status}.`);
|
|
2808
2762
|
}
|
|
2809
|
-
|
|
2810
|
-
throw usageError("Only the Task Leader may accept a Work Item.");
|
|
2811
|
-
}
|
|
2763
|
+
const actor = taskActor(tx, options, task.id);
|
|
2812
2764
|
if (item.status !== "awaiting_acceptance") {
|
|
2813
2765
|
throw usageError(`Work Item is not awaiting acceptance: ${item.id}/${item.status}.`);
|
|
2814
2766
|
}
|
|
@@ -2867,9 +2819,9 @@ function acceptWork(args, store, options) {
|
|
|
2867
2819
|
...(candidate.source.type === "run"
|
|
2868
2820
|
? { runId: candidate.source.runId }
|
|
2869
2821
|
: { workItemRevision: String(candidate.workItemRevision) }),
|
|
2870
|
-
acceptedBy:
|
|
2822
|
+
acceptedBy: actor,
|
|
2871
2823
|
summary,
|
|
2872
|
-
...leaderActionEventPayload(tx, item.taskId, options)
|
|
2824
|
+
...(actor === "leader" ? leaderActionEventPayload(tx, item.taskId, options) : {})
|
|
2873
2825
|
}, now);
|
|
2874
2826
|
return completed;
|
|
2875
2827
|
});
|
|
@@ -2929,9 +2881,7 @@ function rejectWork(args, store, options) {
|
|
|
2929
2881
|
if (task.status !== "active") {
|
|
2930
2882
|
throw usageError(`Task is not active: ${task.id}/${task.status}.`);
|
|
2931
2883
|
}
|
|
2932
|
-
|
|
2933
|
-
throw usageError("Only the Task Leader may reject a Work Item.");
|
|
2934
|
-
}
|
|
2884
|
+
const actor = taskActor(tx, options, task.id);
|
|
2935
2885
|
if (item.status !== "awaiting_acceptance") {
|
|
2936
2886
|
throw usageError(`Work Item is not awaiting acceptance: ${item.id}/${item.status}.`);
|
|
2937
2887
|
}
|
|
@@ -2945,7 +2895,7 @@ function rejectWork(args, store, options) {
|
|
|
2945
2895
|
recordTaskEvent(tx, item.taskId, "work.rejected", {
|
|
2946
2896
|
workItemId: item.id,
|
|
2947
2897
|
candidateId: candidate.id,
|
|
2948
|
-
rejectedBy:
|
|
2898
|
+
rejectedBy: actor,
|
|
2949
2899
|
summary
|
|
2950
2900
|
}, now);
|
|
2951
2901
|
return failed;
|
|
@@ -2966,7 +2916,7 @@ function retireWork(args, store, options) {
|
|
|
2966
2916
|
if (task.status !== "active") {
|
|
2967
2917
|
throw usageError(`Task is not active: ${task.id}/${task.status}.`);
|
|
2968
2918
|
}
|
|
2969
|
-
const actor = taskActor(options, task.id);
|
|
2919
|
+
const actor = taskActor(tx, options, task.id);
|
|
2970
2920
|
if (replacementWorkItemId !== undefined) {
|
|
2971
2921
|
const replacement = requireWorkItem(tx, replacementWorkItemId, options);
|
|
2972
2922
|
if (replacement.taskId !== task.id) {
|
|
@@ -3088,9 +3038,7 @@ function reviewWork(args, store, options) {
|
|
|
3088
3038
|
if (task.status !== "active") {
|
|
3089
3039
|
throw usageError(`Task is not active: ${task.id}/${task.status}.`);
|
|
3090
3040
|
}
|
|
3091
|
-
|
|
3092
|
-
throw usageError("Only the Task Leader may request a Work Item review.");
|
|
3093
|
-
}
|
|
3041
|
+
const requestedBy = taskActor(tx, options, task.id);
|
|
3094
3042
|
if (item.status !== "awaiting_acceptance") {
|
|
3095
3043
|
throw usageError(`Work Item is not awaiting acceptance: ${item.id}/${item.status}.`);
|
|
3096
3044
|
}
|
|
@@ -3117,7 +3065,7 @@ function reviewWork(args, store, options) {
|
|
|
3117
3065
|
}
|
|
3118
3066
|
throw usageError(`ReviewRound is already active: ${activeRound.id}/${activeRound.status}.`);
|
|
3119
3067
|
}
|
|
3120
|
-
const queued = queueReviewRound(tx, item, config,
|
|
3068
|
+
const queued = queueReviewRound(tx, item, config, requestedBy, now);
|
|
3121
3069
|
return { ...queued, resumed: false };
|
|
3122
3070
|
});
|
|
3123
3071
|
if (result.run !== null) {
|
|
@@ -3131,7 +3079,7 @@ function reviewWork(args, store, options) {
|
|
|
3131
3079
|
: output(`Review requested as ${result.round.id}\n`, { reviewRound: result.round });
|
|
3132
3080
|
}
|
|
3133
3081
|
/**
|
|
3134
|
-
*
|
|
3082
|
+
* Task-control recovery for a failed Task-final ReviewRound that never
|
|
3135
3083
|
* created a Reviewer Run. This is deliberately separate from `task run retry`:
|
|
3136
3084
|
* that command requires an exact failed Agent Run and remains the only retry
|
|
3137
3085
|
* path for a failed provider execution. Here the old terminal Round is an
|
|
@@ -3281,9 +3229,7 @@ function resolveReviewExecutionGroup(args, store, options) {
|
|
|
3281
3229
|
const task = requireTask(tx, round.taskId);
|
|
3282
3230
|
if (task.status !== "active")
|
|
3283
3231
|
throw usageError(inactiveTaskMessage(task, "resolving a Review ExecutionGroup"));
|
|
3284
|
-
|
|
3285
|
-
throw usageError("Only the Task Leader may resolve a Reviewer ExecutionGroup.");
|
|
3286
|
-
}
|
|
3232
|
+
taskActor(tx, options, task.id);
|
|
3287
3233
|
const group = round.executionGroup;
|
|
3288
3234
|
if (group === undefined
|
|
3289
3235
|
|| (group.lanes.length < 2 && group.strategy.mode !== "adaptive")) {
|
|
@@ -3402,7 +3348,7 @@ function disposeReviewFindingCommand(args, store, options) {
|
|
|
3402
3348
|
const parsed = parseTail(args, new Set(["--disposition", "--work-item", "--commit", "--verification", "--note", "--superseded-by"]), usage);
|
|
3403
3349
|
exactPositionals(parsed.positionals, 1, usage);
|
|
3404
3350
|
const disposition = requiredOption(parsed.options, "--disposition");
|
|
3405
|
-
if (!
|
|
3351
|
+
if (!TASK_CONTROL_FINDING_DISPOSITIONS.includes(disposition)) {
|
|
3406
3352
|
throw usageError(`Review finding disposition is invalid: ${disposition}.`);
|
|
3407
3353
|
}
|
|
3408
3354
|
const now = clock(options);
|
|
@@ -3414,12 +3360,12 @@ function disposeReviewFindingCommand(args, store, options) {
|
|
|
3414
3360
|
const task = requireTask(tx, reference.taskId);
|
|
3415
3361
|
if (task.status !== "active")
|
|
3416
3362
|
throw usageError(inactiveTaskMessage(task, "dispositioning a review finding"));
|
|
3417
|
-
|
|
3418
|
-
throw usageError("Only the Task Leader may disposition a review finding.");
|
|
3419
|
-
}
|
|
3363
|
+
const actor = taskActor(tx, options, task.id);
|
|
3420
3364
|
const command = {
|
|
3421
3365
|
disposition,
|
|
3422
|
-
by:
|
|
3366
|
+
by: actor === "leader"
|
|
3367
|
+
? taskLeaderActionRunId(tx, task.id, options.environment, options.yuiHome) ?? actor
|
|
3368
|
+
: actor,
|
|
3423
3369
|
...(parsed.options.get("--note") === undefined ? {} : { note: parsed.options.get("--note") }),
|
|
3424
3370
|
...(parsed.options.get("--work-item") === undefined ? {} : { workItemId: parsed.options.get("--work-item") }),
|
|
3425
3371
|
...(parsed.options.get("--commit") === undefined ? {} : { commit: parsed.options.get("--commit") }),
|
|
@@ -3451,9 +3397,7 @@ function planReviewRepairWave(args, store, options) {
|
|
|
3451
3397
|
if (currentTask.status !== "active") {
|
|
3452
3398
|
throw usageError(inactiveTaskMessage(currentTask, "creating a review repair wave"));
|
|
3453
3399
|
}
|
|
3454
|
-
|
|
3455
|
-
throw usageError("Only the Task Leader may create a review repair wave.");
|
|
3456
|
-
}
|
|
3400
|
+
taskActor(tx, options, currentTask.id);
|
|
3457
3401
|
const openItems = tx.listWorkItems(currentTask.id)
|
|
3458
3402
|
.filter((item) => item.status === "pending" || item.status === "running");
|
|
3459
3403
|
return groups.map((group) => {
|
|
@@ -3552,9 +3496,7 @@ function requestTaskReviewRound(args, store, options) {
|
|
|
3552
3496
|
const task = requireTask(tx, parsed.positionals[0]);
|
|
3553
3497
|
if (task.status !== "active")
|
|
3554
3498
|
throw usageError(`Task is not active: ${task.id}.`);
|
|
3555
|
-
|
|
3556
|
-
throw usageError("Only the Task Leader may request a Task-final Review.");
|
|
3557
|
-
}
|
|
3499
|
+
const requestedBy = taskActor(tx, options, task.id);
|
|
3558
3500
|
if (task.projectBindings.length === 0) {
|
|
3559
3501
|
throw usageError(`Task ${task.id} has no bound Projects for a Task-final Review.`);
|
|
3560
3502
|
}
|
|
@@ -3706,8 +3648,8 @@ function requestTaskReviewRound(args, store, options) {
|
|
|
3706
3648
|
deltaRecord = validateDeltaRecheckRequest(tx, task.id, provenance.candidate, options.deltaRecheckPreflight);
|
|
3707
3649
|
}
|
|
3708
3650
|
let created = deltaRecord === undefined
|
|
3709
|
-
? createTaskReviewRound(tx.nextReviewRoundId(task.id), task.id, reviewerRoleName,
|
|
3710
|
-
: createTaskDeltaReviewRound(tx.nextReviewRoundId(task.id), task.id, reviewerRoleName,
|
|
3651
|
+
? createTaskReviewRound(tx.nextReviewRoundId(task.id), task.id, reviewerRoleName, requestedBy, provenance.candidate, now, taskFinalContract)
|
|
3652
|
+
: createTaskDeltaReviewRound(tx.nextReviewRoundId(task.id), task.id, reviewerRoleName, requestedBy, provenance.candidate, deltaRecord, now, taskFinalContract);
|
|
3711
3653
|
let group = createExecutionGroup(`execution-group-${created.id}`, task.id, {
|
|
3712
3654
|
purpose: "review",
|
|
3713
3655
|
target: executionTargetForReviewRound(task, created),
|
|
@@ -3783,9 +3725,7 @@ function forceFreshTaskReviewRound(args, store, options) {
|
|
|
3783
3725
|
const task = requireTask(tx, reference.taskId);
|
|
3784
3726
|
if (task.status !== "active")
|
|
3785
3727
|
throw usageError(`Task is not active: ${task.id}.`);
|
|
3786
|
-
|
|
3787
|
-
throw usageError("Only the Task Leader may force a fresh Task-final ReviewRound.");
|
|
3788
|
-
}
|
|
3728
|
+
const requestedBy = taskActor(tx, options, task.id);
|
|
3789
3729
|
if ((source.scope ?? "work-item") !== "task") {
|
|
3790
3730
|
throw usageError(`ReviewRound ${source.id} is not a Task-final ReviewRound.`);
|
|
3791
3731
|
}
|
|
@@ -3859,7 +3799,7 @@ function forceFreshTaskReviewRound(args, store, options) {
|
|
|
3859
3799
|
reviewer = createTaskRole(tx, task, source.reviewerRoleName, undefined, now, source.reviewerRoleName);
|
|
3860
3800
|
tx.saveRole(task.id, reviewer);
|
|
3861
3801
|
}
|
|
3862
|
-
let created = createTaskReviewRound(tx.nextReviewRoundId(task.id), task.id, source.reviewerRoleName,
|
|
3802
|
+
let created = createTaskReviewRound(tx.nextReviewRoundId(task.id), task.id, source.reviewerRoleName, requestedBy, source.taskCandidate, now, taskFinalContract);
|
|
3863
3803
|
const group = createExecutionGroup(`execution-group-${created.id}`, task.id, {
|
|
3864
3804
|
purpose: "review",
|
|
3865
3805
|
target: executionTargetForReviewRound(task, created),
|
|
@@ -3875,7 +3815,8 @@ function forceFreshTaskReviewRound(args, store, options) {
|
|
|
3875
3815
|
reviewerRoleName: created.reviewerRoleName,
|
|
3876
3816
|
taskCandidate: JSON.stringify(created.taskCandidate),
|
|
3877
3817
|
reason: "source-round-terminal-without-semantic-review",
|
|
3878
|
-
|
|
3818
|
+
requestedBy,
|
|
3819
|
+
...(requestedBy === "leader" ? leaderActionEventPayload(tx, task.id, options) : {})
|
|
3879
3820
|
}, now);
|
|
3880
3821
|
return { round: created, source, created: true };
|
|
3881
3822
|
});
|
|
@@ -3995,9 +3936,7 @@ function retryFailedTaskReviewRound(args, store, options) {
|
|
|
3995
3936
|
const task = requireTask(tx, reference.taskId);
|
|
3996
3937
|
if (task.status !== "active")
|
|
3997
3938
|
throw usageError(`Task is not active: ${task.id}.`);
|
|
3998
|
-
|
|
3999
|
-
throw usageError("Only the Task Leader may retry a failed Task-final ReviewRound.");
|
|
4000
|
-
}
|
|
3939
|
+
const requestedBy = taskActor(tx, options, task.id);
|
|
4001
3940
|
if ((round.scope ?? "work-item") !== "task") {
|
|
4002
3941
|
throw usageError(`ReviewRound ${round.id} is not a failed Task-final ReviewRound.`);
|
|
4003
3942
|
}
|
|
@@ -4079,7 +4018,7 @@ function retryFailedTaskReviewRound(args, store, options) {
|
|
|
4079
4018
|
// Issue 06: infra retry resets the same semantic Round to pending instead
|
|
4080
4019
|
// of manufacturing a new Round, so Round count and finding identity stay
|
|
4081
4020
|
// stable across execution-attempt failures.
|
|
4082
|
-
const resetRound = retryTaskReviewRound(round);
|
|
4021
|
+
const resetRound = retryTaskReviewRound(round, requestedBy);
|
|
4083
4022
|
tx.saveReviewRound(task.id, resetRound);
|
|
4084
4023
|
recordTaskEvent(tx, task.id, "review.task-final-retried", {
|
|
4085
4024
|
reviewRoundId: round.id
|
|
@@ -4117,8 +4056,16 @@ function taskRunCommand(args, store, options) {
|
|
|
4117
4056
|
: `Unknown command: task run ${command}`);
|
|
4118
4057
|
}
|
|
4119
4058
|
function retireRun(args, store, options) {
|
|
4120
|
-
const usage = "Task run retire usage: yui task run retire <task>/<run> --reason <text
|
|
4121
|
-
const parsed = parseTail(args, new Set([
|
|
4059
|
+
const usage = "Task run retire usage: yui task run retire <task>/<run> --reason <text> [--expected-progress-at <timestamp>] [--agent-id <id>] [--adapter-id <id>] [--native-session-id <id>] [--launch-id <id>].";
|
|
4060
|
+
const parsed = parseTail(args, new Set([
|
|
4061
|
+
"--reason",
|
|
4062
|
+
"--expected-progress-at",
|
|
4063
|
+
"--progress-at",
|
|
4064
|
+
"--agent-id",
|
|
4065
|
+
"--adapter-id",
|
|
4066
|
+
"--native-session-id",
|
|
4067
|
+
"--launch-id"
|
|
4068
|
+
]), usage);
|
|
4122
4069
|
exactPositionals(parsed.positionals, 1, usage);
|
|
4123
4070
|
const reason = requiredOption(parsed.options, "--reason");
|
|
4124
4071
|
const reference = taskRecordReference(parsed.positionals[0], "agentRun", "Agent Run reference", options);
|
|
@@ -4126,10 +4073,7 @@ function retireRun(args, store, options) {
|
|
|
4126
4073
|
const result = store.transaction((tx) => {
|
|
4127
4074
|
const task = requireTask(tx, reference.taskId);
|
|
4128
4075
|
assertTaskOpen(task);
|
|
4129
|
-
const actor = taskActor(options, task.id);
|
|
4130
|
-
if (actor === "leader") {
|
|
4131
|
-
throw usageError("Only the user or global Operator may retire an Agent Run.");
|
|
4132
|
-
}
|
|
4076
|
+
const actor = taskActor(tx, options, task.id);
|
|
4133
4077
|
let run = tx.getAgentRun(task.id, reference.localId);
|
|
4134
4078
|
if (run === null)
|
|
4135
4079
|
throw dataError(`Agent Run not found: ${task.id}/${reference.localId}.`);
|
|
@@ -4138,19 +4082,66 @@ function retireRun(args, store, options) {
|
|
|
4138
4082
|
return { task, run, changed: false };
|
|
4139
4083
|
}
|
|
4140
4084
|
if (run.status === "active") {
|
|
4141
|
-
|
|
4085
|
+
if (actor === "leader"
|
|
4086
|
+
&& taskLeaderActionRunId(tx, task.id, options.environment, options.yuiHome) === run.id) {
|
|
4087
|
+
throw usageError("A Task Leader cannot retire its own current authority Run.", usage);
|
|
4088
|
+
}
|
|
4089
|
+
const expectedProgressAt = requiredOption(parsed.options, parsed.options.has("--expected-progress-at")
|
|
4090
|
+
? "--expected-progress-at"
|
|
4091
|
+
: "--progress-at");
|
|
4092
|
+
if (parsed.options.has("--expected-progress-at") && parsed.options.has("--progress-at")) {
|
|
4093
|
+
throw usageError("--expected-progress-at and --progress-at are mutually exclusive.", usage);
|
|
4094
|
+
}
|
|
4095
|
+
const agentId = requiredOption(parsed.options, "--agent-id");
|
|
4096
|
+
const adapterId = requiredOption(parsed.options, "--adapter-id");
|
|
4097
|
+
const nativeSessionId = parsed.options.get("--native-session-id");
|
|
4098
|
+
const launchId = parsed.options.get("--launch-id");
|
|
4099
|
+
const sessions = tx.getTaskRoleSessionSet(task.id, run.roleName);
|
|
4100
|
+
const session = sessions?.sessions[run.effective.agentId];
|
|
4101
|
+
if (session?.nativeSessionId !== undefined && nativeSessionId === undefined) {
|
|
4102
|
+
throw usageError("--native-session-id is required for this active Run.", usage);
|
|
4103
|
+
}
|
|
4104
|
+
if (session?.nativeSessionId === undefined && launchId === undefined) {
|
|
4105
|
+
throw usageError("--launch-id is required for an opaque active Run.", usage);
|
|
4106
|
+
}
|
|
4107
|
+
const terminal = retireExactActiveAgentRun(tx, {
|
|
4142
4108
|
taskId: task.id,
|
|
4143
4109
|
roleName: run.roleName,
|
|
4144
|
-
agentId: run.effective.agentId,
|
|
4145
4110
|
runId: run.id,
|
|
4146
|
-
|
|
4147
|
-
|
|
4111
|
+
agentId,
|
|
4112
|
+
adapterId,
|
|
4113
|
+
...(nativeSessionId === undefined ? {} : { nativeSessionId }),
|
|
4114
|
+
...(launchId === undefined ? {} : { launchId }),
|
|
4115
|
+
expectedProgressAt,
|
|
4116
|
+
reason: `Agent Run retired: ${reason}`
|
|
4148
4117
|
}, now);
|
|
4149
4118
|
if (terminal.disposition !== "applied" || terminal.run === null) {
|
|
4150
|
-
throw usageError(
|
|
4119
|
+
throw usageError(terminal.disposition === "blocked"
|
|
4120
|
+
? `Agent Run retirement is blocked: ${run.id}/${terminal.reason ?? "unsafe"}.`
|
|
4121
|
+
: `Agent Run changed during retirement: ${run.id}/${terminal.reason ?? "obsolete"}.`);
|
|
4151
4122
|
}
|
|
4152
4123
|
run = terminal.run;
|
|
4153
4124
|
}
|
|
4125
|
+
recordTaskEvent(tx, task.id, "run.retired", {
|
|
4126
|
+
runId: run.id,
|
|
4127
|
+
reason,
|
|
4128
|
+
...(parsed.options.get("--expected-progress-at") === undefined
|
|
4129
|
+
&& parsed.options.get("--progress-at") === undefined
|
|
4130
|
+
? {}
|
|
4131
|
+
: {
|
|
4132
|
+
expectedProgressAt: parsed.options.get("--expected-progress-at")
|
|
4133
|
+
?? parsed.options.get("--progress-at")
|
|
4134
|
+
}),
|
|
4135
|
+
...(parsed.options.get("--native-session-id") === undefined
|
|
4136
|
+
? {}
|
|
4137
|
+
: { nativeSessionId: parsed.options.get("--native-session-id") }),
|
|
4138
|
+
...(parsed.options.get("--launch-id") === undefined
|
|
4139
|
+
? {}
|
|
4140
|
+
: { launchId: parsed.options.get("--launch-id") }),
|
|
4141
|
+
...(actor === "leader"
|
|
4142
|
+
? leaderActionEventPayload(tx, task.id, options)
|
|
4143
|
+
: { retiredBy: actor })
|
|
4144
|
+
}, now);
|
|
4154
4145
|
tx.saveEvent(task.id, createTaskRecordRetirement({
|
|
4155
4146
|
eventId: tx.nextEventId(task.id),
|
|
4156
4147
|
taskId: task.id,
|
|
@@ -4289,9 +4280,7 @@ function settleStaleFinalReviewRun(args, store, options) {
|
|
|
4289
4280
|
const task = requireTask(tx, run.taskId);
|
|
4290
4281
|
if (task.status !== "active")
|
|
4291
4282
|
throw usageError(`Task is not active: ${task.id}.`);
|
|
4292
|
-
|
|
4293
|
-
throw usageError("Only the Task Leader may settle a stale final review Run.");
|
|
4294
|
-
}
|
|
4283
|
+
const actor = taskActor(tx, options, task.id);
|
|
4295
4284
|
const round = tx.getReviewRound(task.id, run.reviewRoundId);
|
|
4296
4285
|
if (round === null) {
|
|
4297
4286
|
throw dataError(`ReviewRound not found for run ${run.id}: ${run.reviewRoundId}.`);
|
|
@@ -4400,7 +4389,9 @@ function settleStaleFinalReviewRun(args, store, options) {
|
|
|
4400
4389
|
runId: run.id,
|
|
4401
4390
|
reviewRoundId: round.id,
|
|
4402
4391
|
previousTaskCandidate: JSON.stringify(round.taskCandidate),
|
|
4403
|
-
currentTaskCandidate: JSON.stringify(currentTaskCandidate)
|
|
4392
|
+
currentTaskCandidate: JSON.stringify(currentTaskCandidate),
|
|
4393
|
+
settledBy: actor,
|
|
4394
|
+
...(actor === "leader" ? leaderActionEventPayload(tx, task.id, options) : {})
|
|
4404
4395
|
}, now);
|
|
4405
4396
|
return { run, round: terminal, changed: true };
|
|
4406
4397
|
});
|
|
@@ -4951,7 +4942,7 @@ function assertPendingFinalReviewWorkspaceEvidence(store, task, round) {
|
|
|
4951
4942
|
}
|
|
4952
4943
|
}
|
|
4953
4944
|
/**
|
|
4954
|
-
*
|
|
4945
|
+
* Task-control retry of an exact failed Task-final review Run. The old failed
|
|
4955
4946
|
* Run remains the attempt trail, while the semantic ReviewRound is reset to
|
|
4956
4947
|
* pending under its existing identity. Every identity and frozen-head fence is
|
|
4957
4948
|
* checked inside one transaction so a partial fail-old-without-reset state can
|
|
@@ -4978,9 +4969,7 @@ function retryFailedReviewRun(previous, store, options, now) {
|
|
|
4978
4969
|
throw usageError(`Review Run ${run.id} still owns an unsettled Provider continuation; `
|
|
4979
4970
|
+ "reconcile it before retrying the Lane.");
|
|
4980
4971
|
}
|
|
4981
|
-
|
|
4982
|
-
throw usageError("Only the Task Leader may retry a failed final review Run.");
|
|
4983
|
-
}
|
|
4972
|
+
const requestedBy = taskActor(tx, options, task.id);
|
|
4984
4973
|
const round = tx.getReviewRound(task.id, run.reviewRoundId);
|
|
4985
4974
|
if (round === null) {
|
|
4986
4975
|
throw dataError(`ReviewRound not found for run ${run.id}: ${run.reviewRoundId}.`);
|
|
@@ -5160,7 +5149,7 @@ function retryFailedReviewRun(previous, store, options, now) {
|
|
|
5160
5149
|
// Issue 06: infra retry resets the same semantic Round to pending instead
|
|
5161
5150
|
// of manufacturing a new Round, so Round count and finding identity stay
|
|
5162
5151
|
// stable across execution-attempt failures.
|
|
5163
|
-
const resetRound = retryTaskReviewRound(roundToReset, run.executionLaneId);
|
|
5152
|
+
const resetRound = retryTaskReviewRound(roundToReset, requestedBy, run.executionLaneId);
|
|
5164
5153
|
tx.saveReviewRound(task.id, resetRound);
|
|
5165
5154
|
recordTaskEvent(tx, task.id, "run.review-retried", {
|
|
5166
5155
|
runId: run.id,
|
|
@@ -5205,9 +5194,7 @@ function recoverRun(args, store, options) {
|
|
|
5205
5194
|
const task = requireTask(tx, active.taskId);
|
|
5206
5195
|
if (task.status !== "active")
|
|
5207
5196
|
throw usageError(inactiveTaskMessage(task, "recovering a run"));
|
|
5208
|
-
|
|
5209
|
-
throw usageError("Only the Task Leader may control exact Agent Run recovery.");
|
|
5210
|
-
}
|
|
5197
|
+
taskActor(tx, options, task.id);
|
|
5211
5198
|
const roleName = parsed.options.get("--role") ?? active.roleName;
|
|
5212
5199
|
if (roleName !== active.roleName) {
|
|
5213
5200
|
throw usageError(`Recovery Role does not match Run ${active.id}: ${roleName}.`);
|
|
@@ -5308,18 +5295,27 @@ function showRun(args, store, options) {
|
|
|
5308
5295
|
const retirement = tx.listEvents(run.taskId)
|
|
5309
5296
|
.map(taskRecordRetirement)
|
|
5310
5297
|
.find((entry) => entry?.recordKind === "agent-run" && entry.recordId === run.id) ?? null;
|
|
5311
|
-
|
|
5298
|
+
const allRejectedYieldAttempts = tx.listEvents(run.taskId)
|
|
5299
|
+
.map(rejectedYieldAttemptFromTaskEvent)
|
|
5300
|
+
.filter((attempt) => (attempt !== null && attempt.runId === run.id));
|
|
5301
|
+
return {
|
|
5302
|
+
run,
|
|
5303
|
+
recovery: projectRunRecovery(facts),
|
|
5304
|
+
retirement,
|
|
5305
|
+
rejectedYieldAttemptCount: allRejectedYieldAttempts.length,
|
|
5306
|
+
rejectedYieldAttempts: allRejectedYieldAttempts.slice(-16)
|
|
5307
|
+
};
|
|
5312
5308
|
});
|
|
5313
5309
|
if (asJson) {
|
|
5314
5310
|
return { kind: "output", output: `${JSON.stringify(data, null, 2)}\n`, data };
|
|
5315
5311
|
}
|
|
5316
5312
|
return {
|
|
5317
5313
|
kind: "output",
|
|
5318
|
-
output: renderRunShow(data.run, data.recovery, data.retirement),
|
|
5314
|
+
output: renderRunShow(data.run, data.recovery, data.retirement, data.rejectedYieldAttemptCount, data.rejectedYieldAttempts),
|
|
5319
5315
|
data
|
|
5320
5316
|
};
|
|
5321
5317
|
}
|
|
5322
|
-
function renderRunShow(run, recovery, retirement) {
|
|
5318
|
+
function renderRunShow(run, recovery, retirement, rejectedYieldAttemptCount, rejectedYieldAttempts) {
|
|
5323
5319
|
const lines = [
|
|
5324
5320
|
`Run: ${run.id}`,
|
|
5325
5321
|
`Task: ${run.taskId}`,
|
|
@@ -5339,7 +5335,19 @@ function renderRunShow(run, recovery, retirement) {
|
|
|
5339
5335
|
: [`Provider accepted (durable): ${run.deliveredAt}`]),
|
|
5340
5336
|
...(run.summary === undefined || run.summary.trim().length === 0
|
|
5341
5337
|
? []
|
|
5342
|
-
: [`Summary: ${run.summary}`])
|
|
5338
|
+
: [`Summary: ${run.summary}`]),
|
|
5339
|
+
...(rejectedYieldAttemptCount === 0
|
|
5340
|
+
? []
|
|
5341
|
+
: [
|
|
5342
|
+
`Rejected yield attempts: ${rejectedYieldAttempts.length} shown of ${rejectedYieldAttemptCount} (unaccepted diagnostic evidence only)`,
|
|
5343
|
+
...rejectedYieldAttempts.slice(-5).flatMap((attempt) => [
|
|
5344
|
+
` ${attempt.attemptedAt}: ${attempt.rejectionReason} (${attempt.attemptDigest.slice(0, 16)})`,
|
|
5345
|
+
` Reviewer output: ${truncateRunShowText(attempt.summary)}`,
|
|
5346
|
+
` Checks: ${attempt.checks.length === 0
|
|
5347
|
+
? "none"
|
|
5348
|
+
: attempt.checks.map(({ name, outcome }) => `${name}:${outcome}`).join(", ")}`
|
|
5349
|
+
])
|
|
5350
|
+
])
|
|
5343
5351
|
];
|
|
5344
5352
|
if (recovery.canonicalProgressAt !== null) {
|
|
5345
5353
|
lines.push(`Canonical recovery fence (Yui durable CAS): ${recovery.canonicalProgressAt}`, ...(recovery.canonicalProgressEvidence === undefined
|
|
@@ -5370,6 +5378,10 @@ function renderRunShow(run, recovery, retirement) {
|
|
|
5370
5378
|
}
|
|
5371
5379
|
return `${lines.join("\n")}\n`;
|
|
5372
5380
|
}
|
|
5381
|
+
function truncateRunShowText(value) {
|
|
5382
|
+
const normalized = value.replaceAll(/\s+/gu, " ").trim();
|
|
5383
|
+
return normalized.length <= 400 ? normalized : `${normalized.slice(0, 399)}…`;
|
|
5384
|
+
}
|
|
5373
5385
|
/**
|
|
5374
5386
|
* Issue 04: builds the terminal yield outcome from the command inputs. The
|
|
5375
5387
|
* same construction feeds both the first commit and the idempotent replay, so
|
|
@@ -5462,6 +5474,70 @@ function yieldRunStatus(args, store, options) {
|
|
|
5462
5474
|
+ `Request: ${run.yieldReceipt.requestId}\n`
|
|
5463
5475
|
+ `Committed: ${run.yieldReceipt.committedAt}\n`, { receipt: run.yieldReceipt });
|
|
5464
5476
|
}
|
|
5477
|
+
function recordRejectedReviewYield(store, input) {
|
|
5478
|
+
if (input.run.purpose !== "review" || input.run.reviewRoundId === undefined) {
|
|
5479
|
+
throw new Error(`Only a Review Run can record rejected yield evidence: ${input.run.id}.`);
|
|
5480
|
+
}
|
|
5481
|
+
const receiptId = agentRunDeliveryReceiptId(input.run);
|
|
5482
|
+
const sessions = store.getTaskRoleSessionSet(input.run.taskId, input.run.roleName);
|
|
5483
|
+
const durableSession = sessions?.sessions[input.run.effective.agentId];
|
|
5484
|
+
const attempt = createRejectedYieldAttempt({
|
|
5485
|
+
taskId: input.run.taskId,
|
|
5486
|
+
runId: input.run.id,
|
|
5487
|
+
roleName: input.run.roleName,
|
|
5488
|
+
reviewRoundId: input.run.reviewRoundId,
|
|
5489
|
+
receiptId,
|
|
5490
|
+
rejectionReason: input.rejectionReason,
|
|
5491
|
+
summary: input.outcome.summary,
|
|
5492
|
+
...(input.outcome.reviewResult === undefined
|
|
5493
|
+
? {}
|
|
5494
|
+
: { reviewResult: input.outcome.reviewResult }),
|
|
5495
|
+
...(input.nativeSessionId === undefined
|
|
5496
|
+
? {}
|
|
5497
|
+
: { nativeSessionId: input.nativeSessionId }),
|
|
5498
|
+
...(input.launchId === undefined ? {} : { launchId: input.launchId }),
|
|
5499
|
+
...(durableSession?.nativeSessionId === undefined
|
|
5500
|
+
? {}
|
|
5501
|
+
: { durableNativeSessionId: durableSession.nativeSessionId }),
|
|
5502
|
+
...(durableSession?.launchId === undefined
|
|
5503
|
+
? {}
|
|
5504
|
+
: { durableLaunchId: durableSession.launchId }),
|
|
5505
|
+
...(sessions?.inFlight?.runId === undefined
|
|
5506
|
+
? {}
|
|
5507
|
+
: { inFlightRunId: sessions.inFlight.runId }),
|
|
5508
|
+
...(sessions?.inFlight?.receiptId === undefined
|
|
5509
|
+
? {}
|
|
5510
|
+
: { inFlightReceiptId: sessions.inFlight.receiptId }),
|
|
5511
|
+
...(input.run.assignment.contextSnapshotRef === undefined
|
|
5512
|
+
? {}
|
|
5513
|
+
: { contextSnapshot: input.run.assignment.contextSnapshotRef }),
|
|
5514
|
+
activeRun: input.activeRun === null
|
|
5515
|
+
? null
|
|
5516
|
+
: {
|
|
5517
|
+
id: input.activeRun.id,
|
|
5518
|
+
receiptId: agentRunDeliveryReceiptId(input.activeRun),
|
|
5519
|
+
...(input.activeRun.assignment.contextSnapshotRef === undefined
|
|
5520
|
+
? {}
|
|
5521
|
+
: { contextSnapshot: input.activeRun.assignment.contextSnapshotRef })
|
|
5522
|
+
},
|
|
5523
|
+
attemptedAt: input.now
|
|
5524
|
+
});
|
|
5525
|
+
const existing = store.listEvents(input.run.taskId).find((event) => (event.type === RUN_YIELD_REJECTED_EVENT
|
|
5526
|
+
&& event.payload.runId === input.run.id
|
|
5527
|
+
&& event.payload.receiptId === receiptId
|
|
5528
|
+
&& event.payload.attemptDigest === attempt.attemptDigest));
|
|
5529
|
+
if (existing !== undefined) {
|
|
5530
|
+
const persisted = rejectedYieldAttemptFromTaskEvent(existing);
|
|
5531
|
+
if (persisted === null) {
|
|
5532
|
+
throw new Error(`Rejected yield attempt is malformed: ${existing.id}.`);
|
|
5533
|
+
}
|
|
5534
|
+
return { attempt: persisted, event: existing, created: false };
|
|
5535
|
+
}
|
|
5536
|
+
const event = createTaskEvent(store.nextEventId(input.run.taskId), input.run.taskId, RUN_YIELD_REJECTED_EVENT, rejectedYieldAttemptEventPayload(attempt), input.now);
|
|
5537
|
+
store.saveEvent(input.run.taskId, event);
|
|
5538
|
+
routeRoleEvent(store, event, input.run.roleName, "run-yield-rejected", input.now);
|
|
5539
|
+
return { attempt, event, created: true };
|
|
5540
|
+
}
|
|
5465
5541
|
function yieldRun(args, store, options) {
|
|
5466
5542
|
const usage = "Task run yield usage: yui task run yield <task>/<run> (--summary <text>|--summary-file <path|->).";
|
|
5467
5543
|
const parsed = parseTail(args, new Set(["--summary", "--summary-file"]), usage);
|
|
@@ -5491,8 +5567,6 @@ function yieldRun(args, store, options) {
|
|
|
5491
5567
|
throw usageError(inactiveTaskMessage(task, "yielding a run"));
|
|
5492
5568
|
const role = requireRole(tx, task.id, active.roleName);
|
|
5493
5569
|
const pointer = activeRunPointer(tx, active);
|
|
5494
|
-
if (pointer?.id !== active.id)
|
|
5495
|
-
throw usageError(`Run is not active for ${task.id}/${role.name}: ${active.id}.`);
|
|
5496
5570
|
const taskFinalContract = active.purpose === "execution"
|
|
5497
5571
|
&& active.workItemId !== undefined
|
|
5498
5572
|
? taskFinalReviewContractForMutation(tx, task.id, options)
|
|
@@ -5545,6 +5619,27 @@ function yieldRun(args, store, options) {
|
|
|
5545
5619
|
: { reviewResult: yieldOutcome.reviewResult })
|
|
5546
5620
|
}, now);
|
|
5547
5621
|
if (terminalization.disposition !== "applied" || terminalization.run === null) {
|
|
5622
|
+
if (active.purpose === "review" && active.reviewRoundId !== undefined) {
|
|
5623
|
+
const rejected = recordRejectedReviewYield(tx, {
|
|
5624
|
+
run: active,
|
|
5625
|
+
activeRun: pointer,
|
|
5626
|
+
rejectionReason: terminalization.reason ?? "obsolete",
|
|
5627
|
+
outcome: yieldOutcome,
|
|
5628
|
+
...(options.environment?.YUI_NATIVE_SESSION_ID === undefined
|
|
5629
|
+
? {}
|
|
5630
|
+
: { nativeSessionId: options.environment.YUI_NATIVE_SESSION_ID }),
|
|
5631
|
+
...(options.environment?.YUI_LAUNCH_ID === undefined
|
|
5632
|
+
? {}
|
|
5633
|
+
: { launchId: options.environment.YUI_LAUNCH_ID }),
|
|
5634
|
+
now
|
|
5635
|
+
});
|
|
5636
|
+
return {
|
|
5637
|
+
kind: "rejected",
|
|
5638
|
+
run: active,
|
|
5639
|
+
rejected,
|
|
5640
|
+
notifyLeader: rejected.created
|
|
5641
|
+
};
|
|
5642
|
+
}
|
|
5548
5643
|
throw usageError(`Run ${active.id} no longer matches its exact execution fence: `
|
|
5549
5644
|
+ `${terminalization.reason ?? "obsolete"}.`);
|
|
5550
5645
|
}
|
|
@@ -5727,6 +5822,7 @@ function yieldRun(args, store, options) {
|
|
|
5727
5822
|
if (receipt !== null) {
|
|
5728
5823
|
tx.saveAgentRun(receipt);
|
|
5729
5824
|
return {
|
|
5825
|
+
kind: "yielded",
|
|
5730
5826
|
run: receipt,
|
|
5731
5827
|
reviewDispatch,
|
|
5732
5828
|
notifyLeader: leaderHandoff !== null
|
|
@@ -5734,11 +5830,21 @@ function yieldRun(args, store, options) {
|
|
|
5734
5830
|
}
|
|
5735
5831
|
}
|
|
5736
5832
|
return {
|
|
5833
|
+
kind: "yielded",
|
|
5737
5834
|
run: terminal,
|
|
5738
5835
|
reviewDispatch,
|
|
5739
5836
|
notifyLeader: leaderHandoff !== null
|
|
5740
5837
|
};
|
|
5741
5838
|
});
|
|
5839
|
+
if (yielded.kind === "rejected") {
|
|
5840
|
+
if (yielded.notifyLeader) {
|
|
5841
|
+
notifyMailbox(options.runtime, leaderMailbox(yielded.run.taskId), yielded.run.taskId);
|
|
5842
|
+
}
|
|
5843
|
+
throw usageError(`Run ${yielded.run.id} no longer matches its exact execution fence: `
|
|
5844
|
+
+ `${yielded.rejected.attempt.rejectionReason}. `
|
|
5845
|
+
+ `Rejected Reviewer output was recorded as ${yielded.rejected.event.id} `
|
|
5846
|
+
+ "(unaccepted diagnostic evidence only).");
|
|
5847
|
+
}
|
|
5742
5848
|
if (yielded.notifyLeader) {
|
|
5743
5849
|
notifyMailbox(options.runtime, leaderMailbox(yielded.run.taskId), yielded.run.taskId);
|
|
5744
5850
|
}
|
|
@@ -6267,6 +6373,8 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
|
|
|
6267
6373
|
export function failPendingReviewRound(taskId, reviewRoundId, summary, store, options = {}) {
|
|
6268
6374
|
const now = clock(options);
|
|
6269
6375
|
const failed = store.transaction((tx) => {
|
|
6376
|
+
const task = requireTask(tx, taskId);
|
|
6377
|
+
taskActor(tx, options, task.id);
|
|
6270
6378
|
const round = tx.getReviewRound(taskId, reviewRoundId);
|
|
6271
6379
|
if (round === null)
|
|
6272
6380
|
throw usageError(`ReviewRound not found: ${taskId}/${reviewRoundId}.`);
|
|
@@ -6377,52 +6485,6 @@ function recordTaskEventRecord(store, taskId, type, payload, now) {
|
|
|
6377
6485
|
store.saveEvent(taskId, event);
|
|
6378
6486
|
return event;
|
|
6379
6487
|
}
|
|
6380
|
-
function publishedTreeAuthorizationPayload(actor, proof) {
|
|
6381
|
-
return {
|
|
6382
|
-
by: actor,
|
|
6383
|
-
projectId: proof.projectId,
|
|
6384
|
-
publicationId: proof.publicationId,
|
|
6385
|
-
...(proof.reviewRoundId === undefined
|
|
6386
|
-
? { reviewAnchor: publishedTreeReviewAnchor(proof) }
|
|
6387
|
-
: { reviewRoundId: proof.reviewRoundId }),
|
|
6388
|
-
localCommit: proof.localCommit,
|
|
6389
|
-
remoteCommit: proof.remoteCommit,
|
|
6390
|
-
tree: proof.tree
|
|
6391
|
-
};
|
|
6392
|
-
}
|
|
6393
|
-
function matchingPublishedTreeAuthorization(store, proof) {
|
|
6394
|
-
const events = store.listEvents(proof.taskId);
|
|
6395
|
-
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
6396
|
-
const event = events[index];
|
|
6397
|
-
if (event.type === "task.completed" || event.type === "task.reopened")
|
|
6398
|
-
return undefined;
|
|
6399
|
-
if (event.type === TASK_COMPLETION_PUBLISHED_TREE_AUTHORIZED_EVENT
|
|
6400
|
-
&& (event.payload.by === "user" || event.payload.by === "operator")
|
|
6401
|
-
&& event.payload.projectId === proof.projectId
|
|
6402
|
-
&& event.payload.publicationId === proof.publicationId
|
|
6403
|
-
&& (event.payload.reviewAnchor ?? event.payload.reviewRoundId)
|
|
6404
|
-
=== publishedTreeReviewAnchor(proof)
|
|
6405
|
-
&& event.payload.localCommit === proof.localCommit
|
|
6406
|
-
&& event.payload.remoteCommit === proof.remoteCommit
|
|
6407
|
-
&& event.payload.tree === proof.tree) {
|
|
6408
|
-
return event;
|
|
6409
|
-
}
|
|
6410
|
-
}
|
|
6411
|
-
return undefined;
|
|
6412
|
-
}
|
|
6413
|
-
function requirePublishedTreeAuthorization(store, proof) {
|
|
6414
|
-
const authorization = matchingPublishedTreeAuthorization(store, proof);
|
|
6415
|
-
if (authorization === undefined) {
|
|
6416
|
-
throw usageError(`Published-tree completion requires explicit user or global Operator authorization for `
|
|
6417
|
-
+ `${proof.taskId}/${proof.publicationId} at ${proof.reviewRoundId === undefined
|
|
6418
|
-
? "the Leader-owned Task-main head"
|
|
6419
|
-
: `Task-final Review ${proof.reviewRoundId}`}.`);
|
|
6420
|
-
}
|
|
6421
|
-
return authorization;
|
|
6422
|
-
}
|
|
6423
|
-
function publishedTreeReviewAnchor(proof) {
|
|
6424
|
-
return proof.reviewRoundId ?? "direct";
|
|
6425
|
-
}
|
|
6426
6488
|
/** Keeps a free-text run-fact note bounded so an event payload stays compact. */
|
|
6427
6489
|
function truncateEventNote(note) {
|
|
6428
6490
|
const normalized = note.trim();
|
|
@@ -6565,8 +6627,8 @@ function assertTaskOpen(task) {
|
|
|
6565
6627
|
if (task.status === "retired")
|
|
6566
6628
|
throw usageError(`Task is retired: ${task.id}.`);
|
|
6567
6629
|
}
|
|
6568
|
-
function taskActor(options, taskId) {
|
|
6569
|
-
return
|
|
6630
|
+
function taskActor(store, options, taskId) {
|
|
6631
|
+
return resolveTaskLocalActor(store, options.environment, taskId, options.yuiHome);
|
|
6570
6632
|
}
|
|
6571
6633
|
function taskRoleDispatchMode(store, taskId, roleName, sessions, agentId, effective) {
|
|
6572
6634
|
return roleSessionDispatchModeWithConversationSwitch(sessions, store.listEvents(taskId), store.getWorkMailbox({ kind: "role", taskId, roleName }), roleName, agentId, effective);
|
|
@@ -6741,7 +6803,7 @@ function taskBriefCommand(args, store, options) {
|
|
|
6741
6803
|
const task = requireTask(tx, parsed.positionals[0]);
|
|
6742
6804
|
assertTaskOpen(task);
|
|
6743
6805
|
const existing = tx.getTaskBrief(task.id);
|
|
6744
|
-
const updatedBy = taskActor(options, task.id);
|
|
6806
|
+
const updatedBy = taskActor(tx, options, task.id);
|
|
6745
6807
|
const brief = existing === null
|
|
6746
6808
|
? createTaskBrief({
|
|
6747
6809
|
objective: requiredText(parsed.options.get("--objective"), "--objective"),
|
|
@@ -6792,7 +6854,7 @@ function taskDecisionCommand(args, store, options) {
|
|
|
6792
6854
|
const result = store.transaction((tx) => {
|
|
6793
6855
|
const task = requireTask(tx, parsed.positionals[0]);
|
|
6794
6856
|
assertTaskOpen(task);
|
|
6795
|
-
const actor = taskActor(options, task.id);
|
|
6857
|
+
const actor = taskActor(tx, options, task.id);
|
|
6796
6858
|
const decision = createDecision(tx.nextDecisionId(task.id), task.id, title, rationale, now);
|
|
6797
6859
|
tx.saveDecision(task.id, decision);
|
|
6798
6860
|
recordTaskEvent(tx, task.id, "decision.recorded", {
|
|
@@ -6863,7 +6925,7 @@ function taskDecisionCommand(args, store, options) {
|
|
|
6863
6925
|
const result = store.transaction((tx) => {
|
|
6864
6926
|
const task = requireTask(tx, parsed.positionals[0]);
|
|
6865
6927
|
assertTaskOpen(task);
|
|
6866
|
-
const actor = taskActor(options, task.id);
|
|
6928
|
+
const actor = taskActor(tx, options, task.id);
|
|
6867
6929
|
const existing = tx.getDecision(task.id, parsed.positionals[1]);
|
|
6868
6930
|
if (existing === null)
|
|
6869
6931
|
throw dataError(`Decision not found: ${parsed.positionals[1]}.`);
|
|
@@ -6901,10 +6963,8 @@ function taskMilestoneCommand(args, store, options) {
|
|
|
6901
6963
|
const result = store.transaction((tx) => {
|
|
6902
6964
|
const task = requireTask(tx, parsed.positionals[0]);
|
|
6903
6965
|
assertTaskOpen(task);
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
}
|
|
6907
|
-
const milestone = createMilestone(tx.nextMilestoneId(task.id), task.id, title, summary, now);
|
|
6966
|
+
const actor = taskActor(tx, options, task.id);
|
|
6967
|
+
const milestone = createMilestone(tx.nextMilestoneId(task.id), task.id, title, summary, actor, now);
|
|
6908
6968
|
tx.saveMilestone(task.id, milestone);
|
|
6909
6969
|
recordTaskEvent(tx, task.id, "milestone.added", {
|
|
6910
6970
|
milestoneId: milestone.id,
|