@brainervirus/workit-core 2.1.3 → 2.1.5
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/package.json +2 -2
- package/scripts/analyze-release-scope.ts +3 -3
- package/scripts/doctor-check.ts +1 -2
- package/scripts/sync-release-manifests.ts +4 -4
- package/src/core/authority.ts +8 -8
- package/src/core/auto-approval.ts +6 -6
- package/src/core/branch.ts +4 -4
- package/src/core/config-conversion.ts +1 -1
- package/src/core/config.ts +3 -3
- package/src/core/cutover.ts +6 -6
- package/src/core/doctor.ts +4 -3
- package/src/core/external-action-effects.ts +15 -15
- package/src/core/external-action.ts +16 -19
- package/src/core/host-install.ts +17 -1
- package/src/core/logger.ts +2 -2
- package/src/core/policy-resolver.ts +9 -6
- package/src/core/registration.ts +3 -5
- package/src/core/repo-context.ts +1 -1
- package/src/core/session-context.ts +2 -2
- package/src/core/setup.ts +2 -2
- package/src/core/skill-manifests.ts +1 -1
- package/src/core/support-matrix.ts +1 -1
- package/src/core/task-context.ts +3 -3
- package/src/core/task-contract.ts +8 -8
- package/src/core/task-engine.ts +159 -159
- package/src/core/task-evaluation.ts +3 -3
- package/src/core/task-store.ts +12 -12
- package/src/core/tracker-issues.ts +1 -1
- package/src/core/uninstall.ts +3 -3
- package/src/core/workers.ts +3 -3
- package/src/core/workspaces.ts +4 -6
- package/src/core/youtrack-tools.ts +1 -1
package/src/core/task-engine.ts
CHANGED
|
@@ -195,13 +195,13 @@ const validNativeProvenance = (
|
|
|
195
195
|
expected: Pick<NativeWorkerObservation, "workerId" | "session">,
|
|
196
196
|
): value is Provenance => {
|
|
197
197
|
if (!provenanceSchema.safeParse(value).success) return false;
|
|
198
|
-
const
|
|
198
|
+
const observed = value as Provenance;
|
|
199
199
|
return (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
sameValue(
|
|
204
|
-
|
|
200
|
+
observed.kind === "host_observed" &&
|
|
201
|
+
observed.host === caller.host &&
|
|
202
|
+
observed.workerId === expected.workerId &&
|
|
203
|
+
sameValue(observed.session, expected.session) &&
|
|
204
|
+
observed.receipts.some((receipt) => receipt.kind === "host" && receipt.host === caller.host)
|
|
205
205
|
);
|
|
206
206
|
};
|
|
207
207
|
|
|
@@ -212,15 +212,15 @@ const validDispatchProvenance = (
|
|
|
212
212
|
workerId: string,
|
|
213
213
|
): value is Provenance => {
|
|
214
214
|
if (!provenanceSchema.safeParse(value).success) return false;
|
|
215
|
-
const
|
|
215
|
+
const observed = value as Provenance;
|
|
216
216
|
return (
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
217
|
+
observed.kind === "host_observed" &&
|
|
218
|
+
observed.host === caller.host &&
|
|
219
|
+
observed.workerId === workerId &&
|
|
220
|
+
observed.session?.kind === "host" &&
|
|
221
|
+
observed.session.host === caller.host &&
|
|
222
|
+
observed.session.handle === caller.actor &&
|
|
223
|
+
observed.receipts.some((receipt) => receipt.kind === "host" && receipt.host === caller.host)
|
|
224
224
|
);
|
|
225
225
|
};
|
|
226
226
|
|
|
@@ -343,9 +343,9 @@ const applyWorkerLifecycle = (input: ObserveWorkerLifecycleInput): Result<Entry<
|
|
|
343
343
|
if (!authority) return failure("permission_denied", "native worker observation is not verified");
|
|
344
344
|
|
|
345
345
|
const task = input.store.readTask(input.taskId);
|
|
346
|
-
if (!task.ok) return task
|
|
346
|
+
if (!task.ok) return task;
|
|
347
347
|
const workspace = input.store.readWorkspace();
|
|
348
|
-
if (!workspace.ok) return workspace
|
|
348
|
+
if (!workspace.ok) return workspace;
|
|
349
349
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
350
350
|
if (
|
|
351
351
|
task.data.workspaceId !== authority.workspaceId ||
|
|
@@ -438,7 +438,7 @@ const applyWorkerLifecycle = (input: ObserveWorkerLifecycleInput): Result<Entry<
|
|
|
438
438
|
),
|
|
439
439
|
}),
|
|
440
440
|
});
|
|
441
|
-
if (!changed.ok) return changed
|
|
441
|
+
if (!changed.ok) return changed;
|
|
442
442
|
const updated = changed.data.task.workers.find((candidate) => candidate.id === input.workerId);
|
|
443
443
|
return updated
|
|
444
444
|
? success(changed.data.task.revision, changed.data.workspace.revision, updated)
|
|
@@ -560,13 +560,13 @@ const importedTask = (
|
|
|
560
560
|
])
|
|
561
561
|
allocate(entry.id);
|
|
562
562
|
const fresh = (id: string) => allocate(id);
|
|
563
|
-
const
|
|
563
|
+
const imported = importedProvenance(context);
|
|
564
564
|
const remap = (ref: Ref): Ref | null => mapRef(ref, ids);
|
|
565
565
|
const intent = {
|
|
566
566
|
...source.intent,
|
|
567
567
|
id: fresh(source.intent.id),
|
|
568
568
|
recordedAt: timestamp,
|
|
569
|
-
provenance,
|
|
569
|
+
provenance: imported,
|
|
570
570
|
data: rewriteRecordRefs(source.intent.data, intentSchema, remap) as typeof source.intent.data,
|
|
571
571
|
};
|
|
572
572
|
const assessments = source.assessments.map((entry) => {
|
|
@@ -575,7 +575,7 @@ const importedTask = (
|
|
|
575
575
|
...entry,
|
|
576
576
|
id: fresh(entry.id),
|
|
577
577
|
recordedAt: timestamp,
|
|
578
|
-
provenance,
|
|
578
|
+
provenance: imported,
|
|
579
579
|
data: rewriteRecordRefs(data, assessmentSchema, remap) as typeof data,
|
|
580
580
|
};
|
|
581
581
|
});
|
|
@@ -583,14 +583,14 @@ const importedTask = (
|
|
|
583
583
|
...entry,
|
|
584
584
|
id: fresh(entry.id),
|
|
585
585
|
recordedAt: timestamp,
|
|
586
|
-
provenance,
|
|
586
|
+
provenance: imported,
|
|
587
587
|
data: rewriteRecordRefs(entry.data, evidenceSchema, remap) as typeof entry.data,
|
|
588
588
|
}));
|
|
589
589
|
const decisions = source.decisions.map((entry) => ({
|
|
590
590
|
...entry,
|
|
591
591
|
id: fresh(entry.id),
|
|
592
592
|
recordedAt: timestamp,
|
|
593
|
-
provenance,
|
|
593
|
+
provenance: imported,
|
|
594
594
|
}));
|
|
595
595
|
const findings = source.findings.map((entry) => {
|
|
596
596
|
const data = rewriteRecordRefs(entry.data, findingSchema, remap) as typeof entry.data;
|
|
@@ -598,7 +598,7 @@ const importedTask = (
|
|
|
598
598
|
...entry,
|
|
599
599
|
id: fresh(entry.id),
|
|
600
600
|
recordedAt: timestamp,
|
|
601
|
-
provenance,
|
|
601
|
+
provenance: imported,
|
|
602
602
|
data: {
|
|
603
603
|
...data,
|
|
604
604
|
resolution: data.resolution
|
|
@@ -615,7 +615,7 @@ const importedTask = (
|
|
|
615
615
|
...entry,
|
|
616
616
|
id: fresh(entry.id),
|
|
617
617
|
recordedAt: timestamp,
|
|
618
|
-
provenance,
|
|
618
|
+
provenance: imported,
|
|
619
619
|
data: {
|
|
620
620
|
...entry.data,
|
|
621
621
|
state: "stopped" as const,
|
|
@@ -768,7 +768,7 @@ export class WorkitCore {
|
|
|
768
768
|
private helperTaskGuard(task: TaskRecord, allowRead = false): Result<null> {
|
|
769
769
|
if ((this.context.workerId ?? null) === null) return success(null, null, null);
|
|
770
770
|
const worker = this.helperEntry(task);
|
|
771
|
-
if (!worker.ok) return worker
|
|
771
|
+
if (!worker.ok) return worker;
|
|
772
772
|
return allowRead
|
|
773
773
|
? success(null, null, null)
|
|
774
774
|
: failure("permission_denied", "helpers cannot control task lifecycle or scope");
|
|
@@ -787,17 +787,17 @@ export class WorkitCore {
|
|
|
787
787
|
|
|
788
788
|
state(request: unknown): Result<ExportBundle | TaskSummary | TaskRecord | WorkspaceRecord> {
|
|
789
789
|
const root = this.contextRootError();
|
|
790
|
-
if (!root.ok) return root
|
|
790
|
+
if (!root.ok) return root;
|
|
791
791
|
if ((this.context.workerId ?? null) !== null)
|
|
792
792
|
return failure("permission_denied", "helpers cannot control task state");
|
|
793
793
|
const parsed = parseOperation("state", request);
|
|
794
|
-
if (!parsed.ok) return parsed
|
|
795
|
-
const input = parsed.data
|
|
794
|
+
if (!parsed.ok) return parsed;
|
|
795
|
+
const input = parsed.data;
|
|
796
796
|
if (input.action === "export") {
|
|
797
797
|
const task = this.store.readTask(input.taskId);
|
|
798
|
-
if (!task.ok) return task
|
|
798
|
+
if (!task.ok) return task;
|
|
799
799
|
const workspace = this.store.readWorkspace();
|
|
800
|
-
if (!workspace.ok) return workspace
|
|
800
|
+
if (!workspace.ok) return workspace;
|
|
801
801
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
802
802
|
const portable = taskRecordSchema.safeParse(portableTask(task.data));
|
|
803
803
|
if (!portable.success)
|
|
@@ -830,7 +830,7 @@ export class WorkitCore {
|
|
|
830
830
|
)
|
|
831
831
|
return failure("invalid_input", "export bundle digest is invalid");
|
|
832
832
|
const workspace = this.store.readWorkspace();
|
|
833
|
-
if (!workspace.ok) return workspace
|
|
833
|
+
if (!workspace.ok) return workspace;
|
|
834
834
|
const destinationWorkspaceId = workspace.data?.id ?? newId();
|
|
835
835
|
this.fillRevisions(input, undefined, workspace.data ?? null);
|
|
836
836
|
const timestamp = trustedNow(this.context);
|
|
@@ -847,7 +847,7 @@ export class WorkitCore {
|
|
|
847
847
|
workspaceId: destinationWorkspaceId,
|
|
848
848
|
now: timestamp,
|
|
849
849
|
});
|
|
850
|
-
if (!imported.ok) return imported
|
|
850
|
+
if (!imported.ok) return imported;
|
|
851
851
|
return this.summary(imported.data);
|
|
852
852
|
}
|
|
853
853
|
if (!this.context.nativeRecovery)
|
|
@@ -872,7 +872,7 @@ export class WorkitCore {
|
|
|
872
872
|
processEvidence,
|
|
873
873
|
});
|
|
874
874
|
const workspace = this.store.readWorkspace();
|
|
875
|
-
if (!workspace.ok) return workspace
|
|
875
|
+
if (!workspace.ok) return workspace;
|
|
876
876
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
877
877
|
this.fillRevisions(input, undefined, workspace.data);
|
|
878
878
|
if (input.target === "workspace" && input.taskId !== undefined)
|
|
@@ -891,10 +891,10 @@ export class WorkitCore {
|
|
|
891
891
|
|
|
892
892
|
task(request: unknown): Result<TaskSummary | TaskListItem[] | TaskView> {
|
|
893
893
|
const root = this.contextRootError();
|
|
894
|
-
if (!root.ok) return root
|
|
894
|
+
if (!root.ok) return root;
|
|
895
895
|
const parsed = parseOperation("task", request);
|
|
896
|
-
if (!parsed.ok) return parsed
|
|
897
|
-
const input = parsed.data
|
|
896
|
+
if (!parsed.ok) return parsed;
|
|
897
|
+
const input = parsed.data;
|
|
898
898
|
if ((this.context.workerId ?? null) !== null) {
|
|
899
899
|
if (input.action !== "inspect" && input.action !== "list")
|
|
900
900
|
return failure("permission_denied", "helpers cannot control task lifecycle or scope");
|
|
@@ -903,7 +903,7 @@ export class WorkitCore {
|
|
|
903
903
|
case "start": {
|
|
904
904
|
if (input.expectedWorkspaceRevision === undefined) {
|
|
905
905
|
const workspace = this.store.readWorkspace();
|
|
906
|
-
if (!workspace.ok) return workspace
|
|
906
|
+
if (!workspace.ok) return workspace;
|
|
907
907
|
input.expectedWorkspaceRevision = workspace.data?.revision ?? null;
|
|
908
908
|
}
|
|
909
909
|
const created = this.store.create({
|
|
@@ -912,12 +912,12 @@ export class WorkitCore {
|
|
|
912
912
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
913
913
|
now: trustedNow(this.context),
|
|
914
914
|
});
|
|
915
|
-
if (!created.ok) return created
|
|
915
|
+
if (!created.ok) return created;
|
|
916
916
|
return this.summary(created.data);
|
|
917
917
|
}
|
|
918
918
|
case "list": {
|
|
919
919
|
const listed = this.store.listTasks();
|
|
920
|
-
if (!listed.ok) return listed
|
|
920
|
+
if (!listed.ok) return listed;
|
|
921
921
|
const status = input.status ?? "open";
|
|
922
922
|
const limit = input.limit ?? 20;
|
|
923
923
|
const query = input.query?.toLowerCase().split(/\s+/);
|
|
@@ -948,11 +948,11 @@ export class WorkitCore {
|
|
|
948
948
|
.includes(term),
|
|
949
949
|
),
|
|
950
950
|
)
|
|
951
|
-
.
|
|
951
|
+
.toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))
|
|
952
952
|
.slice(0, limit);
|
|
953
953
|
if (selected.length === 0) return success(null, null, []);
|
|
954
954
|
const workspace = this.store.readWorkspace();
|
|
955
|
-
if (!workspace.ok) return workspace
|
|
955
|
+
if (!workspace.ok) return workspace;
|
|
956
956
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
957
957
|
const tasks = selected.map((task): TaskListItem => ({
|
|
958
958
|
id: task.id,
|
|
@@ -975,9 +975,9 @@ export class WorkitCore {
|
|
|
975
975
|
}
|
|
976
976
|
case "inspect": {
|
|
977
977
|
const task = this.store.readTask(input.taskId);
|
|
978
|
-
if (!task.ok) return task
|
|
978
|
+
if (!task.ok) return task;
|
|
979
979
|
const helper = this.helperTaskGuard(task.data, true);
|
|
980
|
-
if (!helper.ok) return helper
|
|
980
|
+
if (!helper.ok) return helper;
|
|
981
981
|
if ((input.view ?? "summary") === "summary") return this.summary(task.data);
|
|
982
982
|
return this.view(task.data);
|
|
983
983
|
}
|
|
@@ -998,12 +998,12 @@ export class WorkitCore {
|
|
|
998
998
|
|
|
999
999
|
policy(request: unknown): Result<Policy | null> {
|
|
1000
1000
|
const root = this.contextRootError();
|
|
1001
|
-
if (!root.ok) return root
|
|
1001
|
+
if (!root.ok) return root;
|
|
1002
1002
|
const parsed = parseOperation("policy", request);
|
|
1003
|
-
if (!parsed.ok) return parsed
|
|
1004
|
-
const input = parsed.data
|
|
1003
|
+
if (!parsed.ok) return parsed;
|
|
1004
|
+
const input = parsed.data;
|
|
1005
1005
|
const task = this.store.readTask(input.taskId);
|
|
1006
|
-
if (!task.ok) return task
|
|
1006
|
+
if (!task.ok) return task;
|
|
1007
1007
|
if (input.action === "explain") return success(task.data.revision, null, task.data.policy);
|
|
1008
1008
|
if ((this.context.workerId ?? null) !== null)
|
|
1009
1009
|
return failure("permission_denied", "helpers cannot change task requirements");
|
|
@@ -1044,24 +1044,24 @@ export class WorkitCore {
|
|
|
1044
1044
|
},
|
|
1045
1045
|
trustedNow(this.context),
|
|
1046
1046
|
);
|
|
1047
|
-
if (!changed.ok) return changed
|
|
1047
|
+
if (!changed.ok) return changed;
|
|
1048
1048
|
return success(changed.data.revision, null, changed.data.policy);
|
|
1049
1049
|
}
|
|
1050
1050
|
|
|
1051
1051
|
evidence(request: unknown): Result<Entry<Evidence>> {
|
|
1052
1052
|
const root = this.contextRootError();
|
|
1053
|
-
if (!root.ok) return root
|
|
1053
|
+
if (!root.ok) return root;
|
|
1054
1054
|
const parsed = parseOperation("evidence", request);
|
|
1055
|
-
if (!parsed.ok) return parsed
|
|
1056
|
-
const input = parsed.data
|
|
1055
|
+
if (!parsed.ok) return parsed;
|
|
1056
|
+
const input = parsed.data;
|
|
1057
1057
|
const task = this.store.readTask(input.taskId);
|
|
1058
|
-
if (!task.ok) return task
|
|
1058
|
+
if (!task.ok) return task;
|
|
1059
1059
|
if (task.data.status === "closed")
|
|
1060
1060
|
return failure("invalid_transition", "closed task cannot record evidence");
|
|
1061
1061
|
this.fillRevisions(input, task.data);
|
|
1062
1062
|
const helper =
|
|
1063
1063
|
(this.context.workerId ?? null) === null ? null : this.helperEntry(task.data, true, true);
|
|
1064
|
-
if (helper && !helper.ok) return helper
|
|
1064
|
+
if (helper && !helper.ok) return helper;
|
|
1065
1065
|
const evidence = input.evidence as Evidence;
|
|
1066
1066
|
if (
|
|
1067
1067
|
(evidence.result === "missing" || evidence.result === "skipped") &&
|
|
@@ -1077,7 +1077,7 @@ export class WorkitCore {
|
|
|
1077
1077
|
task.data.intent.data.scope,
|
|
1078
1078
|
environment(),
|
|
1079
1079
|
);
|
|
1080
|
-
if (!currentCandidate.ok) return currentCandidate
|
|
1080
|
+
if (!currentCandidate.ok) return currentCandidate;
|
|
1081
1081
|
// Resolve the binding first (explicit IDs, else the worker pin, else the
|
|
1082
1082
|
// current candidate), then validate and pin-check the resolved values —
|
|
1083
1083
|
// never the caller's possibly-empty fields. The input itself is untouched.
|
|
@@ -1154,7 +1154,7 @@ export class WorkitCore {
|
|
|
1154
1154
|
},
|
|
1155
1155
|
trustedNow(this.context),
|
|
1156
1156
|
);
|
|
1157
|
-
if (!changed.ok) return changed
|
|
1157
|
+
if (!changed.ok) return changed;
|
|
1158
1158
|
return success(changed.data.revision, null, changed.data.evidence.at(-1)!);
|
|
1159
1159
|
}
|
|
1160
1160
|
|
|
@@ -1183,10 +1183,10 @@ export class WorkitCore {
|
|
|
1183
1183
|
standing = false,
|
|
1184
1184
|
): Result<Entry<Decision>> {
|
|
1185
1185
|
const root = this.contextRootError();
|
|
1186
|
-
if (!root.ok) return root
|
|
1186
|
+
if (!root.ok) return root;
|
|
1187
1187
|
const parsed = parseOperation("decision", request);
|
|
1188
|
-
if (!parsed.ok) return parsed
|
|
1189
|
-
const input = parsed.data
|
|
1188
|
+
if (!parsed.ok) return parsed;
|
|
1189
|
+
const input = parsed.data;
|
|
1190
1190
|
if (nativeRequired && input.action !== "record")
|
|
1191
1191
|
return failure(
|
|
1192
1192
|
"permission_denied",
|
|
@@ -1211,7 +1211,7 @@ export class WorkitCore {
|
|
|
1211
1211
|
"standing approvals need an approved action binding with a standing rule",
|
|
1212
1212
|
);
|
|
1213
1213
|
const task = this.store.readTask(input.taskId);
|
|
1214
|
-
if (!task.ok) return task
|
|
1214
|
+
if (!task.ok) return task;
|
|
1215
1215
|
if ((this.context.workerId ?? null) !== null)
|
|
1216
1216
|
return failure("permission_denied", "helpers cannot record or revoke decisions");
|
|
1217
1217
|
this.fillRevisions(input, task.data);
|
|
@@ -1219,12 +1219,12 @@ export class WorkitCore {
|
|
|
1219
1219
|
if (task.data.status === "closed")
|
|
1220
1220
|
return failure("invalid_transition", "closed task cannot record a decision");
|
|
1221
1221
|
const workspace = this.store.readWorkspace();
|
|
1222
|
-
if (!workspace.ok) return workspace
|
|
1222
|
+
if (!workspace.ok) return workspace;
|
|
1223
1223
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
1224
1224
|
if (input.binding.taskId !== task.data.id || input.binding.workspaceId !== workspace.data.id)
|
|
1225
1225
|
return failure("permission_denied", "decision task or workspace binding is invalid");
|
|
1226
1226
|
const content = verifyDecisionContent(this.store, input.binding);
|
|
1227
|
-
if (!content.ok) return content
|
|
1227
|
+
if (!content.ok) return content;
|
|
1228
1228
|
const knownRequirements = new Set(
|
|
1229
1229
|
task.data.policy?.requirements.map((item) => item.id) ?? [],
|
|
1230
1230
|
);
|
|
@@ -1243,7 +1243,7 @@ export class WorkitCore {
|
|
|
1243
1243
|
standing && !stated
|
|
1244
1244
|
? verifyStandingApproval(this.store.root, task.data, this.context.caller, input.binding)
|
|
1245
1245
|
: null;
|
|
1246
|
-
if (standingApproval && !standingApproval.ok) return standingApproval
|
|
1246
|
+
if (standingApproval && !standingApproval.ok) return standingApproval;
|
|
1247
1247
|
const native =
|
|
1248
1248
|
nativeRequired && !stated && !standingApproval
|
|
1249
1249
|
? verifyNativeDecision(
|
|
@@ -1265,7 +1265,7 @@ export class WorkitCore {
|
|
|
1265
1265
|
{ owner: this.authorityOwner, store: this.store, root: this.store.root },
|
|
1266
1266
|
)
|
|
1267
1267
|
: null;
|
|
1268
|
-
if (native && !native.ok) return native
|
|
1268
|
+
if (native && !native.ok) return native;
|
|
1269
1269
|
const nativeProvenance = native?.ok ? retireNativeAuthority(native.data) : null;
|
|
1270
1270
|
if (native?.ok && !nativeProvenance)
|
|
1271
1271
|
return failure("permission_denied", "native decision authority was retired");
|
|
@@ -1273,8 +1273,8 @@ export class WorkitCore {
|
|
|
1273
1273
|
task.data.id,
|
|
1274
1274
|
input.expectedRevision,
|
|
1275
1275
|
(current, mutation) => {
|
|
1276
|
-
const
|
|
1277
|
-
if (!
|
|
1276
|
+
const latestContent = verifyDecisionContent(this.store, input.binding);
|
|
1277
|
+
if (!latestContent.ok) return latestContent;
|
|
1278
1278
|
const known = new Set(current.policy?.requirements.map((item) => item.id) ?? []);
|
|
1279
1279
|
if (input.requirementIds.some((id: string) => !known.has(id)))
|
|
1280
1280
|
return failure("invalid_input", "decision references an unknown requirement");
|
|
@@ -1305,7 +1305,7 @@ export class WorkitCore {
|
|
|
1305
1305
|
},
|
|
1306
1306
|
trustedNow(this.context),
|
|
1307
1307
|
);
|
|
1308
|
-
if (!changed.ok) return changed
|
|
1308
|
+
if (!changed.ok) return changed;
|
|
1309
1309
|
return success(changed.data.revision, null, changed.data.decisions.at(-1)!);
|
|
1310
1310
|
}
|
|
1311
1311
|
const decision = task.data.decisions.find((entry) => entry.id === input.decisionId);
|
|
@@ -1338,7 +1338,7 @@ export class WorkitCore {
|
|
|
1338
1338
|
},
|
|
1339
1339
|
trustedNow(this.context),
|
|
1340
1340
|
);
|
|
1341
|
-
if (!changed.ok) return changed
|
|
1341
|
+
if (!changed.ok) return changed;
|
|
1342
1342
|
const entry = changed.data.decisions.find((candidate) => candidate.id === input.decisionId);
|
|
1343
1343
|
return entry
|
|
1344
1344
|
? success(changed.data.revision, null, entry)
|
|
@@ -1347,18 +1347,18 @@ export class WorkitCore {
|
|
|
1347
1347
|
|
|
1348
1348
|
finding(request: unknown): Result<Entry<Finding>> {
|
|
1349
1349
|
const root = this.contextRootError();
|
|
1350
|
-
if (!root.ok) return root
|
|
1350
|
+
if (!root.ok) return root;
|
|
1351
1351
|
const parsed = parseOperation("finding", request);
|
|
1352
|
-
if (!parsed.ok) return parsed
|
|
1353
|
-
const input = parsed.data
|
|
1352
|
+
if (!parsed.ok) return parsed;
|
|
1353
|
+
const input = parsed.data;
|
|
1354
1354
|
const task = this.store.readTask(input.taskId);
|
|
1355
|
-
if (!task.ok) return task
|
|
1355
|
+
if (!task.ok) return task;
|
|
1356
1356
|
if (task.data.status === "closed")
|
|
1357
1357
|
return failure("invalid_transition", "closed task cannot mutate findings");
|
|
1358
1358
|
this.fillRevisions(input, task.data);
|
|
1359
1359
|
const helper =
|
|
1360
1360
|
(this.context.workerId ?? null) === null ? null : this.helperEntry(task.data, true, true);
|
|
1361
|
-
if (helper && !helper.ok) return helper
|
|
1361
|
+
if (helper && !helper.ok) return helper;
|
|
1362
1362
|
if (input.action === "record") {
|
|
1363
1363
|
if (helper?.ok) {
|
|
1364
1364
|
const assignment = helper.data.data.assignment;
|
|
@@ -1401,7 +1401,7 @@ export class WorkitCore {
|
|
|
1401
1401
|
},
|
|
1402
1402
|
trustedNow(this.context),
|
|
1403
1403
|
);
|
|
1404
|
-
if (!changed.ok) return changed
|
|
1404
|
+
if (!changed.ok) return changed;
|
|
1405
1405
|
return success(changed.data.revision, null, changed.data.findings.at(-1)!);
|
|
1406
1406
|
}
|
|
1407
1407
|
if (helper?.ok) return failure("permission_denied", "helpers cannot resolve findings");
|
|
@@ -1418,7 +1418,7 @@ export class WorkitCore {
|
|
|
1418
1418
|
return failure("invalid_input", "finding resolution references an unknown record");
|
|
1419
1419
|
if (input.disposition === "fixed") {
|
|
1420
1420
|
const current = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
1421
|
-
if (!current.ok) return current
|
|
1421
|
+
if (!current.ok) return current;
|
|
1422
1422
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
1423
1423
|
const verified = evidence.some((entry) => {
|
|
1424
1424
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
@@ -1454,7 +1454,7 @@ export class WorkitCore {
|
|
|
1454
1454
|
}
|
|
1455
1455
|
if (input.disposition === "deferred") {
|
|
1456
1456
|
const workspace = this.store.readWorkspace();
|
|
1457
|
-
if (!workspace.ok) return workspace
|
|
1457
|
+
if (!workspace.ok) return workspace;
|
|
1458
1458
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
1459
1459
|
const allowed = decisions.some(
|
|
1460
1460
|
(entry) =>
|
|
@@ -1507,7 +1507,7 @@ export class WorkitCore {
|
|
|
1507
1507
|
},
|
|
1508
1508
|
trustedNow(this.context),
|
|
1509
1509
|
);
|
|
1510
|
-
if (!changed.ok) return changed
|
|
1510
|
+
if (!changed.ok) return changed;
|
|
1511
1511
|
const entry = changed.data.findings.find((candidate) => candidate.id === input.findingId);
|
|
1512
1512
|
return entry
|
|
1513
1513
|
? success(changed.data.revision, null, entry)
|
|
@@ -1516,14 +1516,14 @@ export class WorkitCore {
|
|
|
1516
1516
|
|
|
1517
1517
|
worker(request: unknown): Result<Entry<Worker>> {
|
|
1518
1518
|
const root = this.contextRootError();
|
|
1519
|
-
if (!root.ok) return root
|
|
1519
|
+
if (!root.ok) return root;
|
|
1520
1520
|
const parsed = parseOperation("worker", request);
|
|
1521
|
-
if (!parsed.ok) return parsed
|
|
1522
|
-
const input = parsed.data
|
|
1521
|
+
if (!parsed.ok) return parsed;
|
|
1522
|
+
const input = parsed.data;
|
|
1523
1523
|
const task = this.store.readTask(input.taskId);
|
|
1524
|
-
if (!task.ok) return task
|
|
1524
|
+
if (!task.ok) return task;
|
|
1525
1525
|
const workspace = this.store.readWorkspace();
|
|
1526
|
-
if (!workspace.ok) return workspace
|
|
1526
|
+
if (!workspace.ok) return workspace;
|
|
1527
1527
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
1528
1528
|
this.fillRevisions(input, task.data, workspace.data);
|
|
1529
1529
|
const helperId = this.context.workerId ?? null;
|
|
@@ -1596,7 +1596,7 @@ export class WorkitCore {
|
|
|
1596
1596
|
});
|
|
1597
1597
|
},
|
|
1598
1598
|
});
|
|
1599
|
-
if (!changed.ok) return changed
|
|
1599
|
+
if (!changed.ok) return changed;
|
|
1600
1600
|
return success(
|
|
1601
1601
|
changed.data.task.revision,
|
|
1602
1602
|
changed.data.workspace.revision,
|
|
@@ -1610,7 +1610,7 @@ export class WorkitCore {
|
|
|
1610
1610
|
if (helperId === null || helperId !== input.workerId)
|
|
1611
1611
|
return failure("permission_denied", "workers can submit only their own report");
|
|
1612
1612
|
const helper = this.helperEntry(task.data, true, true);
|
|
1613
|
-
if (!helper.ok) return helper
|
|
1613
|
+
if (!helper.ok) return helper;
|
|
1614
1614
|
const evidence = task.data.evidence.filter((candidate) =>
|
|
1615
1615
|
input.report.evidenceIds.includes(candidate.id),
|
|
1616
1616
|
);
|
|
@@ -1657,7 +1657,7 @@ export class WorkitCore {
|
|
|
1657
1657
|
),
|
|
1658
1658
|
}),
|
|
1659
1659
|
});
|
|
1660
|
-
if (!changed.ok) return changed
|
|
1660
|
+
if (!changed.ok) return changed;
|
|
1661
1661
|
const updated = changed.data.task.workers.find(
|
|
1662
1662
|
(candidate) => candidate.id === input.workerId,
|
|
1663
1663
|
);
|
|
@@ -1708,7 +1708,7 @@ export class WorkitCore {
|
|
|
1708
1708
|
),
|
|
1709
1709
|
}),
|
|
1710
1710
|
});
|
|
1711
|
-
if (!changed.ok) return changed
|
|
1711
|
+
if (!changed.ok) return changed;
|
|
1712
1712
|
return success(
|
|
1713
1713
|
changed.data.task.revision,
|
|
1714
1714
|
changed.data.workspace.revision,
|
|
@@ -1718,7 +1718,7 @@ export class WorkitCore {
|
|
|
1718
1718
|
|
|
1719
1719
|
observeWorkerLifecycle(input: NativeWorkerObservation): Result<Entry<Worker>> {
|
|
1720
1720
|
const root = this.contextRootError();
|
|
1721
|
-
if (!root.ok) return root
|
|
1721
|
+
if (!root.ok) return root;
|
|
1722
1722
|
if (
|
|
1723
1723
|
this.context.workerId !== undefined &&
|
|
1724
1724
|
this.context.workerId !== null &&
|
|
@@ -1726,9 +1726,9 @@ export class WorkitCore {
|
|
|
1726
1726
|
)
|
|
1727
1727
|
return failure("permission_denied", "worker observation does not match the caller");
|
|
1728
1728
|
const task = this.store.readTask(input.taskId);
|
|
1729
|
-
if (!task.ok) return task
|
|
1729
|
+
if (!task.ok) return task;
|
|
1730
1730
|
const workspace = this.store.readWorkspace();
|
|
1731
|
-
if (!workspace.ok) return workspace
|
|
1731
|
+
if (!workspace.ok) return workspace;
|
|
1732
1732
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
1733
1733
|
const authority = verifyNativeWorker(
|
|
1734
1734
|
this.context.nativeWorker,
|
|
@@ -1744,7 +1744,7 @@ export class WorkitCore {
|
|
|
1744
1744
|
caller: this.context.caller,
|
|
1745
1745
|
},
|
|
1746
1746
|
);
|
|
1747
|
-
if (!authority.ok) return authority
|
|
1747
|
+
if (!authority.ok) return authority;
|
|
1748
1748
|
return applyWorkerLifecycle({
|
|
1749
1749
|
...input,
|
|
1750
1750
|
dispatch: false,
|
|
@@ -1794,13 +1794,13 @@ export class WorkitCore {
|
|
|
1794
1794
|
*/
|
|
1795
1795
|
prepareWorkerDispatch(input: WorkerDispatchRequest): Result<WorkerDispatch> {
|
|
1796
1796
|
const root = this.contextRootError();
|
|
1797
|
-
if (!root.ok) return root
|
|
1797
|
+
if (!root.ok) return root;
|
|
1798
1798
|
if ((this.context.workerId ?? null) !== null)
|
|
1799
1799
|
return failure("permission_denied", "helpers cannot dispatch workers");
|
|
1800
1800
|
const task = this.store.readTask(input.taskId);
|
|
1801
|
-
if (!task.ok) return task
|
|
1801
|
+
if (!task.ok) return task;
|
|
1802
1802
|
const workspace = this.store.readWorkspace();
|
|
1803
|
-
if (!workspace.ok) return workspace
|
|
1803
|
+
if (!workspace.ok) return workspace;
|
|
1804
1804
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
1805
1805
|
if (task.data.workspaceId !== workspace.data.id)
|
|
1806
1806
|
return failure("recovery_required", "worker workspace binding is invalid");
|
|
@@ -1811,7 +1811,7 @@ export class WorkitCore {
|
|
|
1811
1811
|
if (entry.data.state !== "assigned" || entry.data.session !== null)
|
|
1812
1812
|
return failure("invalid_transition", "worker is not awaiting dispatch");
|
|
1813
1813
|
const attested = this.verifyDispatch("prepare", input, workspace.data.id);
|
|
1814
|
-
if (!attested.ok) return attested
|
|
1814
|
+
if (!attested.ok) return attested;
|
|
1815
1815
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
1816
1816
|
taskId: task.data.id,
|
|
1817
1817
|
expectedRevision: input.expectedRevision,
|
|
@@ -1846,7 +1846,7 @@ export class WorkitCore {
|
|
|
1846
1846
|
});
|
|
1847
1847
|
},
|
|
1848
1848
|
});
|
|
1849
|
-
if (!changed.ok) return changed
|
|
1849
|
+
if (!changed.ok) return changed;
|
|
1850
1850
|
const dispatch = {} as WorkerDispatch;
|
|
1851
1851
|
dispatchReservations.set(dispatch, {
|
|
1852
1852
|
owner: this.authorityOwner,
|
|
@@ -1867,7 +1867,7 @@ export class WorkitCore {
|
|
|
1867
1867
|
*/
|
|
1868
1868
|
commitWorkerDispatch(input: WorkerDispatchCommit): Result<Entry<Worker>> {
|
|
1869
1869
|
const root = this.contextRootError();
|
|
1870
|
-
if (!root.ok) return root
|
|
1870
|
+
if (!root.ok) return root;
|
|
1871
1871
|
if ((this.context.workerId ?? null) !== null)
|
|
1872
1872
|
return failure("permission_denied", "helpers cannot dispatch workers");
|
|
1873
1873
|
const reservation = dispatchReservations.get(input.dispatch);
|
|
@@ -1882,7 +1882,7 @@ export class WorkitCore {
|
|
|
1882
1882
|
)
|
|
1883
1883
|
return failure("permission_denied", "worker dispatch reservation is not live");
|
|
1884
1884
|
const workspace = this.store.readWorkspace();
|
|
1885
|
-
if (!workspace.ok) return workspace
|
|
1885
|
+
if (!workspace.ok) return workspace;
|
|
1886
1886
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
1887
1887
|
if (workspace.data.id !== reservation.workspaceId)
|
|
1888
1888
|
return failure("recovery_required", "worker workspace binding is invalid");
|
|
@@ -1912,13 +1912,13 @@ export class WorkitCore {
|
|
|
1912
1912
|
caller: this.context.caller,
|
|
1913
1913
|
},
|
|
1914
1914
|
);
|
|
1915
|
-
if (!verified.ok) return verified
|
|
1915
|
+
if (!verified.ok) return verified;
|
|
1916
1916
|
authority = verified.data;
|
|
1917
1917
|
} else {
|
|
1918
1918
|
if (input.session !== null)
|
|
1919
1919
|
return failure("invalid_input", "a never-dispatched worker stops with no session");
|
|
1920
1920
|
const attested = this.verifyDispatch("not_started", input, workspace.data.id);
|
|
1921
|
-
if (!attested.ok) return attested
|
|
1921
|
+
if (!attested.ok) return attested;
|
|
1922
1922
|
authority = {};
|
|
1923
1923
|
verifiedWorkers.set(authority, {
|
|
1924
1924
|
taskId: input.taskId,
|
|
@@ -1951,14 +1951,14 @@ export class WorkitCore {
|
|
|
1951
1951
|
|
|
1952
1952
|
writer(request: unknown): Result<WorkspaceRecord> {
|
|
1953
1953
|
const root = this.contextRootError();
|
|
1954
|
-
if (!root.ok) return root
|
|
1954
|
+
if (!root.ok) return root;
|
|
1955
1955
|
const parsed = parseOperation("writer", request);
|
|
1956
|
-
if (!parsed.ok) return parsed
|
|
1957
|
-
const input = parsed.data
|
|
1956
|
+
if (!parsed.ok) return parsed;
|
|
1957
|
+
const input = parsed.data;
|
|
1958
1958
|
const task = this.store.readTask(input.taskId);
|
|
1959
|
-
if (!task.ok) return task
|
|
1959
|
+
if (!task.ok) return task;
|
|
1960
1960
|
const workspace = this.store.readWorkspace();
|
|
1961
|
-
if (!workspace.ok) return workspace
|
|
1961
|
+
if (!workspace.ok) return workspace;
|
|
1962
1962
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
1963
1963
|
if (task.data.status !== "active")
|
|
1964
1964
|
return failure("invalid_transition", "paused or closed tasks cannot own product writes");
|
|
@@ -1994,7 +1994,7 @@ export class WorkitCore {
|
|
|
1994
1994
|
}
|
|
1995
1995
|
if (helperId !== null) {
|
|
1996
1996
|
const helper = this.helperEntry(task.data);
|
|
1997
|
-
if (!helper.ok) return helper
|
|
1997
|
+
if (!helper.ok) return helper;
|
|
1998
1998
|
if (helper.data.data.assignment.role !== "implementer")
|
|
1999
1999
|
return failure("permission_denied", "only implementers can own product writes");
|
|
2000
2000
|
if (helper.data.data.state !== "running" || !helper.data.data.session)
|
|
@@ -2046,7 +2046,7 @@ export class WorkitCore {
|
|
|
2046
2046
|
}),
|
|
2047
2047
|
task: (current) => success(null, null, current),
|
|
2048
2048
|
});
|
|
2049
|
-
if (!changed.ok) return changed
|
|
2049
|
+
if (!changed.ok) return changed;
|
|
2050
2050
|
return success(
|
|
2051
2051
|
changed.data.task.revision,
|
|
2052
2052
|
changed.data.workspace.revision,
|
|
@@ -2067,7 +2067,7 @@ export class WorkitCore {
|
|
|
2067
2067
|
return failure("permission_denied", "only the current writer can release ownership");
|
|
2068
2068
|
if (helperId !== null) {
|
|
2069
2069
|
const helper = this.helperEntry(task.data);
|
|
2070
|
-
if (!helper.ok) return helper
|
|
2070
|
+
if (!helper.ok) return helper;
|
|
2071
2071
|
if (helper.data.data.state !== "running")
|
|
2072
2072
|
return failure("recovery_required", "worker must be observed stopped before release");
|
|
2073
2073
|
}
|
|
@@ -2080,7 +2080,7 @@ export class WorkitCore {
|
|
|
2080
2080
|
success(mutation.revision, mutation.revision, { ...currentWorkspace, writer: null }),
|
|
2081
2081
|
task: (currentTask) => success(null, null, currentTask),
|
|
2082
2082
|
});
|
|
2083
|
-
if (!changed.ok) return changed
|
|
2083
|
+
if (!changed.ok) return changed;
|
|
2084
2084
|
return success(
|
|
2085
2085
|
changed.data.task.revision,
|
|
2086
2086
|
changed.data.workspace.revision,
|
|
@@ -2102,9 +2102,9 @@ export class WorkitCore {
|
|
|
2102
2102
|
binding: Decision["binding"],
|
|
2103
2103
|
): Result<Entry<Decision>[]> {
|
|
2104
2104
|
const root = this.contextRootError();
|
|
2105
|
-
if (!root.ok) return root
|
|
2105
|
+
if (!root.ok) return root;
|
|
2106
2106
|
const task = this.store.readTask(taskId);
|
|
2107
|
-
if (!task.ok) return task
|
|
2107
|
+
if (!task.ok) return task;
|
|
2108
2108
|
const entries = storedDecisionApplicable(this.store, task.data, purpose, binding);
|
|
2109
2109
|
return success(task.data.revision, null, entries);
|
|
2110
2110
|
}
|
|
@@ -2116,15 +2116,15 @@ export class WorkitCore {
|
|
|
2116
2116
|
> & { observation: unknown },
|
|
2117
2117
|
): Result<ActionReservation> {
|
|
2118
2118
|
const root = this.contextRootError();
|
|
2119
|
-
if (!root.ok) return root
|
|
2119
|
+
if (!root.ok) return root;
|
|
2120
2120
|
if ((this.context.workerId ?? null) !== null)
|
|
2121
2121
|
return failure("permission_denied", "helpers cannot reserve external actions");
|
|
2122
2122
|
if (input.observation === undefined)
|
|
2123
2123
|
return failure("invalid_input", "native action observation is required");
|
|
2124
2124
|
const task = this.store.readTask(input.taskId);
|
|
2125
|
-
if (!task.ok) return task
|
|
2125
|
+
if (!task.ok) return task;
|
|
2126
2126
|
const workspace = this.store.readWorkspace();
|
|
2127
|
-
if (!workspace.ok) return workspace
|
|
2127
|
+
if (!workspace.ok) return workspace;
|
|
2128
2128
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
2129
2129
|
const decision = task.data.decisions.find((entry) => entry.id === input.decisionId);
|
|
2130
2130
|
if (!decision) return failure("not_found", "decision not found");
|
|
@@ -2146,11 +2146,11 @@ export class WorkitCore {
|
|
|
2146
2146
|
},
|
|
2147
2147
|
{ owner: this.authorityOwner, store: this.store, root: this.store.root },
|
|
2148
2148
|
);
|
|
2149
|
-
if (!authority.ok) return authority
|
|
2149
|
+
if (!authority.ok) return authority;
|
|
2150
2150
|
const operation = decisionOperation(decision.data.binding.approvedContent);
|
|
2151
2151
|
if (operation) {
|
|
2152
2152
|
const view = this.view(task.data);
|
|
2153
|
-
if (!view.ok) return view
|
|
2153
|
+
if (!view.ok) return view;
|
|
2154
2154
|
const evaluations = new Map(
|
|
2155
2155
|
view.data.requirements.map((entry) => [entry.requirementId, entry.status]),
|
|
2156
2156
|
);
|
|
@@ -2184,15 +2184,15 @@ export class WorkitCore {
|
|
|
2184
2184
|
> & { observation: unknown },
|
|
2185
2185
|
): Result<Entry<Decision>> {
|
|
2186
2186
|
const root = this.contextRootError();
|
|
2187
|
-
if (!root.ok) return root
|
|
2187
|
+
if (!root.ok) return root;
|
|
2188
2188
|
if ((this.context.workerId ?? null) !== null)
|
|
2189
2189
|
return failure("permission_denied", "helpers cannot settle external actions");
|
|
2190
2190
|
if (input.observation === undefined)
|
|
2191
2191
|
return failure("invalid_input", "native action observation is required");
|
|
2192
2192
|
const task = this.store.readTask(input.taskId);
|
|
2193
|
-
if (!task.ok) return task
|
|
2193
|
+
if (!task.ok) return task;
|
|
2194
2194
|
const workspace = this.store.readWorkspace();
|
|
2195
|
-
if (!workspace.ok) return workspace
|
|
2195
|
+
if (!workspace.ok) return workspace;
|
|
2196
2196
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
2197
2197
|
const decision = task.data.decisions.find((entry) => entry.id === input.decisionId);
|
|
2198
2198
|
if (!decision) return failure("not_found", "decision not found");
|
|
@@ -2214,7 +2214,7 @@ export class WorkitCore {
|
|
|
2214
2214
|
},
|
|
2215
2215
|
{ owner: this.authorityOwner, store: this.store, root: this.store.root },
|
|
2216
2216
|
);
|
|
2217
|
-
if (!authority.ok) return authority
|
|
2217
|
+
if (!authority.ok) return authority;
|
|
2218
2218
|
return settleBoundedAction({
|
|
2219
2219
|
...input,
|
|
2220
2220
|
store: this.store,
|
|
@@ -2242,13 +2242,13 @@ export class WorkitCore {
|
|
|
2242
2242
|
},
|
|
2243
2243
|
): Result<Entry<Decision>> {
|
|
2244
2244
|
const root = this.contextRootError();
|
|
2245
|
-
if (!root.ok) return root
|
|
2245
|
+
if (!root.ok) return root;
|
|
2246
2246
|
if ((this.context.workerId ?? null) !== null)
|
|
2247
2247
|
return failure("permission_denied", "helpers cannot reconcile external actions");
|
|
2248
2248
|
const task = this.store.readTask(input.taskId);
|
|
2249
|
-
if (!task.ok) return task
|
|
2249
|
+
if (!task.ok) return task;
|
|
2250
2250
|
const workspace = this.store.readWorkspace();
|
|
2251
|
-
if (!workspace.ok) return workspace
|
|
2251
|
+
if (!workspace.ok) return workspace;
|
|
2252
2252
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
2253
2253
|
const decision = task.data.decisions.find((entry) => entry.id === input.decisionId);
|
|
2254
2254
|
if (!decision) return failure("not_found", "decision not found");
|
|
@@ -2272,7 +2272,7 @@ export class WorkitCore {
|
|
|
2272
2272
|
},
|
|
2273
2273
|
{ owner: this.authorityOwner, store: this.store, root: this.store.root },
|
|
2274
2274
|
);
|
|
2275
|
-
if (!authority.ok) return authority
|
|
2275
|
+
if (!authority.ok) return authority;
|
|
2276
2276
|
return reconcileBoundedAction({
|
|
2277
2277
|
...input,
|
|
2278
2278
|
taskRevision: input.expectedRevision,
|
|
@@ -2304,13 +2304,13 @@ export class WorkitCore {
|
|
|
2304
2304
|
|
|
2305
2305
|
private summary(task: TaskRecord): Result<TaskSummary> {
|
|
2306
2306
|
const workspace = this.store.readWorkspace();
|
|
2307
|
-
if (!workspace.ok) return workspace
|
|
2307
|
+
if (!workspace.ok) return workspace;
|
|
2308
2308
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
2309
2309
|
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
2310
2310
|
const current = historical
|
|
2311
2311
|
? success(null, null, historical)
|
|
2312
2312
|
: captureCandidate(this.store.root, task.intent.data.scope, environment());
|
|
2313
|
-
if (!current.ok) return current
|
|
2313
|
+
if (!current.ok) return current;
|
|
2314
2314
|
const evaluationWorkspace =
|
|
2315
2315
|
task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
2316
2316
|
const requirements = evaluateRequirements(
|
|
@@ -2346,19 +2346,19 @@ export class WorkitCore {
|
|
|
2346
2346
|
*/
|
|
2347
2347
|
compactContext(taskId: string): Result<string> {
|
|
2348
2348
|
const root = this.contextRootError();
|
|
2349
|
-
if (!root.ok) return root
|
|
2349
|
+
if (!root.ok) return root;
|
|
2350
2350
|
const task = this.store.readTask(taskId);
|
|
2351
|
-
if (!task.ok) return task
|
|
2351
|
+
if (!task.ok) return task;
|
|
2352
2352
|
const helper = this.helperTaskGuard(task.data, true);
|
|
2353
|
-
if (!helper.ok) return helper
|
|
2353
|
+
if (!helper.ok) return helper;
|
|
2354
2354
|
const view = this.view(task.data, false);
|
|
2355
|
-
if (!view.ok) return view
|
|
2355
|
+
if (!view.ok) return view;
|
|
2356
2356
|
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
|
|
2357
2357
|
}
|
|
2358
2358
|
|
|
2359
2359
|
private view(task: TaskRecord, capture = true): Result<TaskView> {
|
|
2360
2360
|
const workspace = this.store.readWorkspace();
|
|
2361
|
-
if (!workspace.ok) return workspace
|
|
2361
|
+
if (!workspace.ok) return workspace;
|
|
2362
2362
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
2363
2363
|
const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
|
|
2364
2364
|
const current = historical
|
|
@@ -2366,7 +2366,7 @@ export class WorkitCore {
|
|
|
2366
2366
|
: capture
|
|
2367
2367
|
? captureCandidate(this.store.root, task.intent.data.scope, environment())
|
|
2368
2368
|
: success<Candidate | null>(null, null, task.candidates.at(-1) ?? null);
|
|
2369
|
-
if (!current.ok) return current
|
|
2369
|
+
if (!current.ok) return current;
|
|
2370
2370
|
const evaluationWorkspace =
|
|
2371
2371
|
task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
2372
2372
|
const requirements = evaluateRequirements(
|
|
@@ -2391,7 +2391,7 @@ export class WorkitCore {
|
|
|
2391
2391
|
observations: NativeWorkerObservation[] = [],
|
|
2392
2392
|
): Result<ResumeReconciliation> {
|
|
2393
2393
|
const root = this.contextRootError();
|
|
2394
|
-
if (!root.ok) return root
|
|
2394
|
+
if (!root.ok) return root;
|
|
2395
2395
|
if ((this.context.workerId ?? null) !== null)
|
|
2396
2396
|
return failure("permission_denied", "helpers cannot reconcile task workers");
|
|
2397
2397
|
if (view.task.workspaceId !== view.workspace.id)
|
|
@@ -2419,8 +2419,8 @@ export class WorkitCore {
|
|
|
2419
2419
|
// current state. Genuinely stale observations still conflict below.
|
|
2420
2420
|
const freshTask = this.store.readTask(view.task.id);
|
|
2421
2421
|
const freshWorkspace = this.store.readWorkspace();
|
|
2422
|
-
if (!freshTask.ok) return freshTask
|
|
2423
|
-
if (!freshWorkspace.ok) return freshWorkspace
|
|
2422
|
+
if (!freshTask.ok) return freshTask;
|
|
2423
|
+
if (!freshWorkspace.ok) return freshWorkspace;
|
|
2424
2424
|
if (!freshWorkspace.data) return failure("not_found", "workspace not found");
|
|
2425
2425
|
if (
|
|
2426
2426
|
observation.taskId !== view.task.id ||
|
|
@@ -2439,7 +2439,7 @@ export class WorkitCore {
|
|
|
2439
2439
|
},
|
|
2440
2440
|
binding,
|
|
2441
2441
|
);
|
|
2442
|
-
if (!verified.ok) return verified
|
|
2442
|
+
if (!verified.ok) return verified;
|
|
2443
2443
|
if (!takeWorker(verified.data, binding, observation))
|
|
2444
2444
|
return failure("permission_denied", "worker observation authority was not retained");
|
|
2445
2445
|
updates.push(observation);
|
|
@@ -2480,16 +2480,16 @@ export class WorkitCore {
|
|
|
2480
2480
|
|
|
2481
2481
|
private transition(input: any, status: "active" | "paused"): Result<TaskSummary> {
|
|
2482
2482
|
const task = this.store.readTask(input.taskId);
|
|
2483
|
-
if (!task.ok) return task
|
|
2483
|
+
if (!task.ok) return task;
|
|
2484
2484
|
const helper = this.helperTaskGuard(task.data);
|
|
2485
|
-
if (!helper.ok) return helper
|
|
2485
|
+
if (!helper.ok) return helper;
|
|
2486
2486
|
if (
|
|
2487
2487
|
(status === "paused" && task.data.status !== "active") ||
|
|
2488
2488
|
(status === "active" && task.data.status !== "paused")
|
|
2489
2489
|
)
|
|
2490
2490
|
return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
|
|
2491
2491
|
const workspace = this.store.readWorkspace();
|
|
2492
|
-
if (!workspace.ok) return workspace
|
|
2492
|
+
if (!workspace.ok) return workspace;
|
|
2493
2493
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
2494
2494
|
this.fillRevisions(input, task.data, workspace.data);
|
|
2495
2495
|
let resumeCandidate: import("./task-contract").Candidate | null = null;
|
|
@@ -2502,9 +2502,9 @@ export class WorkitCore {
|
|
|
2502
2502
|
"stored policy version is unsupported; reassessment is required",
|
|
2503
2503
|
);
|
|
2504
2504
|
const view = this.view(task.data);
|
|
2505
|
-
if (!view.ok) return view
|
|
2505
|
+
if (!view.ok) return view;
|
|
2506
2506
|
const current = reconcileResumeContext(view.data);
|
|
2507
|
-
if (!current.ok) return current
|
|
2507
|
+
if (!current.ok) return current;
|
|
2508
2508
|
const requirements = view.data.requirements;
|
|
2509
2509
|
if (
|
|
2510
2510
|
requirements.some(
|
|
@@ -2542,7 +2542,7 @@ export class WorkitCore {
|
|
|
2542
2542
|
? failure("recovery_required", "writer ownership must be released first")
|
|
2543
2543
|
: null
|
|
2544
2544
|
: this.activeWorkerBlocker(task.data, workspace.data);
|
|
2545
|
-
if (blocker) return blocker
|
|
2545
|
+
if (blocker) return blocker;
|
|
2546
2546
|
let pauseCandidate: import("./task-contract").Candidate | null = null;
|
|
2547
2547
|
if (status === "paused") {
|
|
2548
2548
|
// Freezing records the tree state the task resumes from.
|
|
@@ -2551,7 +2551,7 @@ export class WorkitCore {
|
|
|
2551
2551
|
task.data.intent.data.scope,
|
|
2552
2552
|
environment(),
|
|
2553
2553
|
);
|
|
2554
|
-
if (!captured.ok) return captured
|
|
2554
|
+
if (!captured.ok) return captured;
|
|
2555
2555
|
pauseCandidate = captured.data;
|
|
2556
2556
|
}
|
|
2557
2557
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
@@ -2559,7 +2559,7 @@ export class WorkitCore {
|
|
|
2559
2559
|
expectedRevision: input.expectedRevision,
|
|
2560
2560
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
2561
2561
|
now: trustedNow(this.context),
|
|
2562
|
-
workspace: (
|
|
2562
|
+
workspace: (unchanged, context) => success(context.revision, context.revision, unchanged),
|
|
2563
2563
|
task: (current, context) =>
|
|
2564
2564
|
success(context.revision, null, {
|
|
2565
2565
|
...current,
|
|
@@ -2575,15 +2575,15 @@ export class WorkitCore {
|
|
|
2575
2575
|
pauseReason: status === "paused" ? (input.reason ?? null) : null,
|
|
2576
2576
|
}),
|
|
2577
2577
|
});
|
|
2578
|
-
if (!changed.ok) return changed
|
|
2578
|
+
if (!changed.ok) return changed;
|
|
2579
2579
|
return this.summary(changed.data.task);
|
|
2580
2580
|
}
|
|
2581
2581
|
|
|
2582
2582
|
private progress(input: any): Result<TaskSummary> {
|
|
2583
2583
|
const task = this.store.readTask(input.taskId);
|
|
2584
|
-
if (!task.ok) return task
|
|
2584
|
+
if (!task.ok) return task;
|
|
2585
2585
|
const helper = this.helperTaskGuard(task.data);
|
|
2586
|
-
if (!helper.ok) return helper
|
|
2586
|
+
if (!helper.ok) return helper;
|
|
2587
2587
|
if (task.data.status === "closed")
|
|
2588
2588
|
return failure("invalid_transition", "closed task cannot update progress");
|
|
2589
2589
|
this.fillRevisions(input, task.data);
|
|
@@ -2594,29 +2594,29 @@ export class WorkitCore {
|
|
|
2594
2594
|
success(context.revision, null, { ...current, progress: input.progress }),
|
|
2595
2595
|
trustedNow(this.context),
|
|
2596
2596
|
);
|
|
2597
|
-
if (!changed.ok) return changed
|
|
2597
|
+
if (!changed.ok) return changed;
|
|
2598
2598
|
return this.summary(changed.data);
|
|
2599
2599
|
}
|
|
2600
2600
|
|
|
2601
2601
|
private revise(input: any): Result<TaskSummary> {
|
|
2602
2602
|
const task = this.store.readTask(input.taskId);
|
|
2603
|
-
if (!task.ok) return task
|
|
2603
|
+
if (!task.ok) return task;
|
|
2604
2604
|
const helper = this.helperTaskGuard(task.data);
|
|
2605
|
-
if (!helper.ok) return helper
|
|
2605
|
+
if (!helper.ok) return helper;
|
|
2606
2606
|
if (task.data.status === "closed")
|
|
2607
2607
|
return failure("invalid_transition", "closed task cannot be revised");
|
|
2608
2608
|
const workspace = this.store.readWorkspace();
|
|
2609
|
-
if (!workspace.ok) return workspace
|
|
2609
|
+
if (!workspace.ok) return workspace;
|
|
2610
2610
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
2611
2611
|
const revisionBlocker = this.activeWorkerBlocker(task.data, workspace.data);
|
|
2612
|
-
if (revisionBlocker) return revisionBlocker
|
|
2612
|
+
if (revisionBlocker) return revisionBlocker;
|
|
2613
2613
|
this.fillRevisions(input, task.data, workspace.data);
|
|
2614
2614
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
2615
2615
|
taskId: task.data.id,
|
|
2616
2616
|
expectedRevision: input.expectedRevision,
|
|
2617
2617
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
2618
2618
|
now: trustedNow(this.context),
|
|
2619
|
-
workspace: (
|
|
2619
|
+
workspace: (unchanged, context) => success(context.revision, context.revision, unchanged),
|
|
2620
2620
|
task: (current, context) =>
|
|
2621
2621
|
success(context.revision, null, {
|
|
2622
2622
|
...current,
|
|
@@ -2630,25 +2630,25 @@ export class WorkitCore {
|
|
|
2630
2630
|
progress: { ...current.progress, summary: `Policy invalidated: ${input.reason}` },
|
|
2631
2631
|
}),
|
|
2632
2632
|
});
|
|
2633
|
-
if (!changed.ok) return changed
|
|
2633
|
+
if (!changed.ok) return changed;
|
|
2634
2634
|
return this.summary(changed.data.task);
|
|
2635
2635
|
}
|
|
2636
2636
|
|
|
2637
2637
|
private close(input: any): Result<TaskSummary> {
|
|
2638
2638
|
const task = this.store.readTask(input.taskId);
|
|
2639
|
-
if (!task.ok) return task
|
|
2639
|
+
if (!task.ok) return task;
|
|
2640
2640
|
const helper = this.helperTaskGuard(task.data);
|
|
2641
|
-
if (!helper.ok) return helper
|
|
2641
|
+
if (!helper.ok) return helper;
|
|
2642
2642
|
if (task.data.status !== "active" && task.data.status !== "paused")
|
|
2643
2643
|
return failure("invalid_transition", "closed task cannot be reopened or closed again");
|
|
2644
2644
|
const workspace = this.store.readWorkspace();
|
|
2645
|
-
if (!workspace.ok) return workspace
|
|
2645
|
+
if (!workspace.ok) return workspace;
|
|
2646
2646
|
if (!workspace.data) return failure("not_found", "workspace not found");
|
|
2647
2647
|
const blocker = this.activeWorkerBlocker(task.data, workspace.data);
|
|
2648
|
-
if (blocker) return blocker
|
|
2648
|
+
if (blocker) return blocker;
|
|
2649
2649
|
this.fillRevisions(input, task.data, workspace.data);
|
|
2650
2650
|
const current = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
2651
|
-
if (!current.ok) return current
|
|
2651
|
+
if (!current.ok) return current;
|
|
2652
2652
|
const withCandidate = current.data;
|
|
2653
2653
|
const taskForView =
|
|
2654
2654
|
withCandidate.id === task.data.candidates.at(-1)?.id
|
|
@@ -2670,7 +2670,7 @@ export class WorkitCore {
|
|
|
2670
2670
|
evidence: evaluateEvidence(taskForView, withCandidate),
|
|
2671
2671
|
} satisfies TaskView;
|
|
2672
2672
|
const closure = evaluateClosure(input.outcome, view);
|
|
2673
|
-
if (!closure.ok) return closure
|
|
2673
|
+
if (!closure.ok) return closure;
|
|
2674
2674
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
2675
2675
|
taskId: task.data.id,
|
|
2676
2676
|
expectedRevision: input.expectedRevision,
|
|
@@ -2691,7 +2691,7 @@ export class WorkitCore {
|
|
|
2691
2691
|
},
|
|
2692
2692
|
}),
|
|
2693
2693
|
});
|
|
2694
|
-
if (!changed.ok) return changed
|
|
2694
|
+
if (!changed.ok) return changed;
|
|
2695
2695
|
return this.summary(changed.data.task);
|
|
2696
2696
|
}
|
|
2697
2697
|
}
|