@brainervirus/workit-codex 1.0.11 → 1.0.12
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/.codex-plugin/plugin.json +1 -1
- package/dist/launch-mcp.js +2185 -2081
- package/dist/workit-hook.js +330 -206
- package/package.json +3 -3
package/dist/workit-hook.js
CHANGED
|
@@ -7355,6 +7355,89 @@ var refSchema = discriminatedUnion("kind", [
|
|
|
7355
7355
|
object({ kind: literal("host"), host: hostSchema, handle: nonEmpty }).strict(),
|
|
7356
7356
|
object({ kind: literal("external"), url: text.url() }).strict()
|
|
7357
7357
|
]);
|
|
7358
|
+
var rewriteUnknown = (value, map) => {
|
|
7359
|
+
if (Array.isArray(value)) {
|
|
7360
|
+
const out = [];
|
|
7361
|
+
for (const item of value) {
|
|
7362
|
+
const rewritten = rewriteUnknown(item, map);
|
|
7363
|
+
if (!rewritten.drop)
|
|
7364
|
+
out.push(rewritten.value);
|
|
7365
|
+
}
|
|
7366
|
+
return { drop: false, value: out };
|
|
7367
|
+
}
|
|
7368
|
+
if (typeof value === "object" && value !== null) {
|
|
7369
|
+
if (refSchema.safeParse(value).success) {
|
|
7370
|
+
const mapped = map(value);
|
|
7371
|
+
return mapped === null ? { drop: true } : { drop: false, value: mapped };
|
|
7372
|
+
}
|
|
7373
|
+
const out = {};
|
|
7374
|
+
for (const [key, item] of Object.entries(value)) {
|
|
7375
|
+
const rewritten = rewriteUnknown(item, map);
|
|
7376
|
+
out[key] = rewritten.drop ? null : rewritten.value;
|
|
7377
|
+
}
|
|
7378
|
+
return { drop: false, value: out };
|
|
7379
|
+
}
|
|
7380
|
+
return { drop: false, value };
|
|
7381
|
+
};
|
|
7382
|
+
var rewriteNode = (value, schema, map) => {
|
|
7383
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && refSchema.safeParse(value).success) {
|
|
7384
|
+
const mapped = map(value);
|
|
7385
|
+
return mapped === null ? { drop: true } : { drop: false, value: mapped };
|
|
7386
|
+
}
|
|
7387
|
+
let node = schema;
|
|
7388
|
+
while (node instanceof ZodNullable || node instanceof ZodOptional || node instanceof ZodDefault) {
|
|
7389
|
+
node = node.unwrap();
|
|
7390
|
+
}
|
|
7391
|
+
if (node === refSchema) {
|
|
7392
|
+
if (!refSchema.safeParse(value).success)
|
|
7393
|
+
return { drop: false, value };
|
|
7394
|
+
const mapped = map(value);
|
|
7395
|
+
return mapped === null ? { drop: true } : { drop: false, value: mapped };
|
|
7396
|
+
}
|
|
7397
|
+
if (node instanceof ZodArray) {
|
|
7398
|
+
if (!Array.isArray(value))
|
|
7399
|
+
return { drop: false, value };
|
|
7400
|
+
const out = [];
|
|
7401
|
+
for (const item of value) {
|
|
7402
|
+
const rewritten = rewriteNode(item, node.element, map);
|
|
7403
|
+
if (!rewritten.drop)
|
|
7404
|
+
out.push(rewritten.value);
|
|
7405
|
+
}
|
|
7406
|
+
return { drop: false, value: out };
|
|
7407
|
+
}
|
|
7408
|
+
if (node instanceof ZodObject) {
|
|
7409
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
7410
|
+
return { drop: false, value };
|
|
7411
|
+
const shape = node.shape;
|
|
7412
|
+
const record = value;
|
|
7413
|
+
const out = {};
|
|
7414
|
+
for (const [key, item] of Object.entries(record)) {
|
|
7415
|
+
if (!(key in shape)) {
|
|
7416
|
+
const scrubbed = rewriteUnknown(item, map);
|
|
7417
|
+
out[key] = scrubbed.drop ? null : scrubbed.value;
|
|
7418
|
+
continue;
|
|
7419
|
+
}
|
|
7420
|
+
const child = shape[key];
|
|
7421
|
+
const rewritten = rewriteNode(item, child, map);
|
|
7422
|
+
if (rewritten.drop) {
|
|
7423
|
+
if (child instanceof ZodOptional || child instanceof ZodDefault)
|
|
7424
|
+
continue;
|
|
7425
|
+
if (child instanceof ZodNullable) {
|
|
7426
|
+
out[key] = null;
|
|
7427
|
+
continue;
|
|
7428
|
+
}
|
|
7429
|
+
return { drop: true };
|
|
7430
|
+
}
|
|
7431
|
+
out[key] = rewritten.value;
|
|
7432
|
+
}
|
|
7433
|
+
return { drop: false, value: out };
|
|
7434
|
+
}
|
|
7435
|
+
return { drop: false, value };
|
|
7436
|
+
};
|
|
7437
|
+
var rewriteRecordRefs = (value, schema, map) => {
|
|
7438
|
+
const rewritten = rewriteNode(value, schema, map);
|
|
7439
|
+
return rewritten.drop ? null : rewritten.value;
|
|
7440
|
+
};
|
|
7358
7441
|
var provenanceSchema = object({
|
|
7359
7442
|
kind: _enum(["host_observed", "agent_reported", "imported"]),
|
|
7360
7443
|
host: hostSchema,
|
|
@@ -9005,11 +9088,11 @@ class TaskStore {
|
|
|
9005
9088
|
return workspace;
|
|
9006
9089
|
if (!workspace.data)
|
|
9007
9090
|
return failure("not_found", "workspace not found");
|
|
9008
|
-
const
|
|
9009
|
-
if (!
|
|
9091
|
+
const expected = input.expectedRevision;
|
|
9092
|
+
if (!expected)
|
|
9010
9093
|
return failure("invalid_input", "task revision is required");
|
|
9011
|
-
if (task.data.revision !==
|
|
9012
|
-
return this.conflict(
|
|
9094
|
+
if (task.data.revision !== expected)
|
|
9095
|
+
return this.conflict(expected, task.data.revision);
|
|
9013
9096
|
if (workspace.data.revision !== input.expectedWorkspaceRevision)
|
|
9014
9097
|
return this.conflict(input.expectedWorkspaceRevision, workspace.data.revision);
|
|
9015
9098
|
const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
|
|
@@ -9543,6 +9626,7 @@ var same = (left, right) => {
|
|
|
9543
9626
|
};
|
|
9544
9627
|
var UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
|
|
9545
9628
|
var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
9629
|
+
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
9546
9630
|
var callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor };
|
|
9547
9631
|
var validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
|
|
9548
9632
|
const code = char.charCodeAt(0);
|
|
@@ -9604,6 +9688,36 @@ var trustedAuthority = (kind, provenance, expected, binding, caller) => {
|
|
|
9604
9688
|
verifiedAuthorities.set(token, { kind, provenance, ...expected, ...binding, caller });
|
|
9605
9689
|
return token;
|
|
9606
9690
|
};
|
|
9691
|
+
var chainStepKey = (step) => step.kind === "commit" ? step.message : step.kind === "branch" ? `branch:${step.target}` : "pr";
|
|
9692
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9693
|
+
var normalizeChainSteps = (steps) => {
|
|
9694
|
+
if (!Array.isArray(steps) || steps.length === 0 || steps.length > 32)
|
|
9695
|
+
return null;
|
|
9696
|
+
const normalized = [];
|
|
9697
|
+
for (const step of steps) {
|
|
9698
|
+
if (typeof step === "string") {
|
|
9699
|
+
if (!step.trim())
|
|
9700
|
+
return null;
|
|
9701
|
+
normalized.push({ kind: "commit", message: step });
|
|
9702
|
+
continue;
|
|
9703
|
+
}
|
|
9704
|
+
if (!isRecord(step))
|
|
9705
|
+
return null;
|
|
9706
|
+
if (typeof step.branch === "string" && step.branch.trim() && Object.keys(step).length === 1) {
|
|
9707
|
+
normalized.push({ kind: "branch", target: step.branch });
|
|
9708
|
+
continue;
|
|
9709
|
+
}
|
|
9710
|
+
if (step.pr === true && Object.keys(step).length === 1) {
|
|
9711
|
+
normalized.push({ kind: "pr" });
|
|
9712
|
+
continue;
|
|
9713
|
+
}
|
|
9714
|
+
return null;
|
|
9715
|
+
}
|
|
9716
|
+
const keys = normalized.map(chainStepKey);
|
|
9717
|
+
if (new Set(keys).size !== keys.length)
|
|
9718
|
+
return null;
|
|
9719
|
+
return normalized;
|
|
9720
|
+
};
|
|
9607
9721
|
var scopeEqual = (left, right) => canonicalJson(left) === canonicalJson(right);
|
|
9608
9722
|
var parseSteps = (content) => {
|
|
9609
9723
|
let value;
|
|
@@ -9615,7 +9729,10 @@ var parseSteps = (content) => {
|
|
|
9615
9729
|
if (typeof value !== "object" || value === null || !("steps" in value))
|
|
9616
9730
|
return [];
|
|
9617
9731
|
const steps = value.steps;
|
|
9618
|
-
|
|
9732
|
+
if (!Array.isArray(steps))
|
|
9733
|
+
return null;
|
|
9734
|
+
const normalized = normalizeChainSteps(steps);
|
|
9735
|
+
return normalized;
|
|
9619
9736
|
};
|
|
9620
9737
|
var decisionMatches = (entry, purpose, binding) => {
|
|
9621
9738
|
const decision = entry.data;
|
|
@@ -9806,14 +9923,12 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
9806
9923
|
return content;
|
|
9807
9924
|
const requestedSteps = parseSteps(decision.binding.approvedContent);
|
|
9808
9925
|
if (requestedSteps === null)
|
|
9809
|
-
return failure("invalid_input", "bounded action steps must be a
|
|
9810
|
-
if (input.steps && canonicalJson(input.steps) !== canonicalJson(requestedSteps))
|
|
9926
|
+
return failure("invalid_input", "bounded action steps must be a unique non-empty array");
|
|
9927
|
+
if (input.steps && canonicalJson(input.steps) !== canonicalJson(requestedSteps.map(chainStepKey)))
|
|
9811
9928
|
return failure("permission_denied", "bounded action steps do not match approved content");
|
|
9812
|
-
if (requestedSteps.some((step) => !step || typeof step !== "string") || new Set(requestedSteps).size !== requestedSteps.length)
|
|
9813
|
-
return failure("invalid_input", "bounded action steps must be unique and non-empty");
|
|
9814
9929
|
const stored = task.actionProgress?.find((item) => item.decisionId === input.decisionId);
|
|
9815
|
-
const workflow = stored ? { steps: stored.steps, completed: stored.completedSteps } : { steps: requestedSteps, completed: [] };
|
|
9816
|
-
if (workflow.steps.length && requestedSteps.length && canonicalJson(workflow.steps) !== canonicalJson(requestedSteps))
|
|
9930
|
+
const workflow = stored ? { steps: stored.steps, completed: stored.completedSteps } : { steps: requestedSteps.map(chainStepKey), completed: [] };
|
|
9931
|
+
if (workflow.steps.length && requestedSteps.length && canonicalJson(workflow.steps) !== canonicalJson(requestedSteps.map(chainStepKey)))
|
|
9817
9932
|
return failure("permission_denied", "bounded action scope changed");
|
|
9818
9933
|
return success(null, null, { entry, workflow });
|
|
9819
9934
|
};
|
|
@@ -10368,13 +10483,28 @@ function evaluateEvidence(task, candidate) {
|
|
|
10368
10483
|
});
|
|
10369
10484
|
}
|
|
10370
10485
|
var scopeCovers2 = scopeCovers;
|
|
10486
|
+
var checkPin = (assignment, beforeCandidateId, candidateId) => {
|
|
10487
|
+
const pin = assignment.candidateId ?? null;
|
|
10488
|
+
if (pin === null)
|
|
10489
|
+
return true;
|
|
10490
|
+
return (beforeCandidateId ?? null) === pin || (candidateId ?? null) === pin;
|
|
10491
|
+
};
|
|
10492
|
+
var resolveBinding = (evidence, pin, currentCandidateId) => {
|
|
10493
|
+
const bound = evidence.kind === "check" || evidence.kind === "review";
|
|
10494
|
+
const fallback = bound ? pin ?? currentCandidateId : null;
|
|
10495
|
+
return {
|
|
10496
|
+
beforeCandidateId: evidence.beforeCandidateId ?? fallback,
|
|
10497
|
+
candidateId: evidence.candidateId ?? fallback
|
|
10498
|
+
};
|
|
10499
|
+
};
|
|
10500
|
+
var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId === findingCandidateId);
|
|
10371
10501
|
var applicableDecision2 = (task, workspace, requirement, checkoutRoot) => task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data).filter((decision) => decision.purpose === "limitation" && decision.response === "approved" && decision.revoked === null && decision.binding.taskId === task.id && decision.binding.workspaceId === workspace.id && decision.requirementIds.includes(requirement.id) && decision.binding.scope && scopeCovers2(decision.binding.scope, requirement.scope) && decision.digest === decisionDigest(decision) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, decision.binding).ok : decision.binding.contentRefs.every((reference) => reference.kind !== "file")));
|
|
10372
10502
|
var applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && (data.response === "approved" || data.response === "stated") && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id }));
|
|
10373
|
-
var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
|
|
10503
|
+
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
10374
10504
|
if (kind !== "passed")
|
|
10375
10505
|
return false;
|
|
10376
10506
|
if (dimension === "testing" || dimension === "verification")
|
|
10377
|
-
return evidenceKind === "check";
|
|
10507
|
+
return evidenceKind === "check" || ruleId === "pre-pr-cleanup" && evidenceKind === "artifact";
|
|
10378
10508
|
if (dimension === "review")
|
|
10379
10509
|
return evidenceKind === "review";
|
|
10380
10510
|
if (dimension === "investigation" || dimension === "challenge")
|
|
@@ -10383,6 +10513,21 @@ var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
|
|
|
10383
10513
|
return evidenceKind === "artifact";
|
|
10384
10514
|
return false;
|
|
10385
10515
|
};
|
|
10516
|
+
var expectedKinds = (dimension, ruleId) => {
|
|
10517
|
+
if (dimension === "testing")
|
|
10518
|
+
return "check";
|
|
10519
|
+
if (dimension === "verification")
|
|
10520
|
+
return ruleId === "pre-pr-cleanup" ? "check or artifact" : "check";
|
|
10521
|
+
if (dimension === "review")
|
|
10522
|
+
return "review";
|
|
10523
|
+
if (dimension === "investigation" || dimension === "challenge")
|
|
10524
|
+
return "investigation";
|
|
10525
|
+
if (dimension === "artifacts" || dimension === "continuity")
|
|
10526
|
+
return "artifact";
|
|
10527
|
+
if (dimension === "decisions")
|
|
10528
|
+
return "decision";
|
|
10529
|
+
return "worker report";
|
|
10530
|
+
};
|
|
10386
10531
|
function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller) {
|
|
10387
10532
|
const evidence = evaluateEvidence(task, candidate);
|
|
10388
10533
|
if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
|
|
@@ -10396,7 +10541,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10396
10541
|
return (task.policy?.requirements ?? []).map((requirement) => {
|
|
10397
10542
|
const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
|
|
10398
10543
|
const passed = related.filter(({ entry, evaluation }) => {
|
|
10399
|
-
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension))
|
|
10544
|
+
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
|
|
10400
10545
|
return false;
|
|
10401
10546
|
if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
|
|
10402
10547
|
return false;
|
|
@@ -10404,11 +10549,12 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10404
10549
|
return true;
|
|
10405
10550
|
const reviewSession = sessionFromRef(entry.data.reviewContext);
|
|
10406
10551
|
const sameImplementation = sameSession(reviewSession, task.intent.provenance.session);
|
|
10407
|
-
const sameEvidenceSession =
|
|
10552
|
+
const sameEvidenceSession = related.some(({ entry: other }) => other.id !== entry.id && other.data.kind === "review" && other.data.candidateId !== null && other.data.candidateId === entry.data.candidateId && sameSession(reviewSession, other.provenance.session));
|
|
10408
10553
|
return entry.data.kind === "review" && reviewSession !== null && sameSession(reviewSession, entry.provenance.session) && (requirement.ruleId === "self-review" || !sameImplementation && !sameEvidenceSession);
|
|
10409
10554
|
});
|
|
10410
10555
|
if (passed.length) {
|
|
10411
|
-
const
|
|
10556
|
+
const currentId = candidate?.id ?? null;
|
|
10557
|
+
const withRed = requirement.dimension === "testing" ? passed.filter(({ entry }) => related.some((other) => other.entry.data.kind === "check" && other.evaluation.status === "failed" && other.entry.data.requirementIds.includes(requirement.id) && other.entry.recordedAt <= entry.recordedAt) || related.some((baseline) => baseline.entry.id !== entry.id && baseline.entry.data.kind === "check" && baseline.entry.data.result === "passed" && baseline.entry.data.candidateId !== null && currentId !== null && baseline.entry.data.candidateId !== currentId)) : passed;
|
|
10412
10558
|
if (withRed.length)
|
|
10413
10559
|
return {
|
|
10414
10560
|
requirementId: requirement.id,
|
|
@@ -10457,6 +10603,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10457
10603
|
reason: "an applicable approved limitation permits the missing evidence"
|
|
10458
10604
|
};
|
|
10459
10605
|
const unavailable = capabilities.some((capability) => capability.assurance === "unavailable" && (capability.name === requirement.dimension || capability.surface === requirement.dimension));
|
|
10606
|
+
if (!unavailable && related.length > 0 && !passed.length)
|
|
10607
|
+
return {
|
|
10608
|
+
requirementId: requirement.id,
|
|
10609
|
+
status: "unsatisfied",
|
|
10610
|
+
evidenceIds: related.map(({ entry }) => entry.id),
|
|
10611
|
+
decisionIds: [],
|
|
10612
|
+
reason: `no passing evidence (needs kind:${expectedKinds(requirement.dimension, requirement.ruleId)})`
|
|
10613
|
+
};
|
|
10460
10614
|
return {
|
|
10461
10615
|
requirementId: requirement.id,
|
|
10462
10616
|
status: unavailable ? "unavailable" : "unsatisfied",
|
|
@@ -10481,7 +10635,13 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10481
10635
|
if (entry.data.disposition === "fixed") {
|
|
10482
10636
|
const verified = entry.data.resolution?.evidenceIds.some((id) => {
|
|
10483
10637
|
const evidence = view.task.evidence.find((item) => item.id === id);
|
|
10484
|
-
|
|
10638
|
+
if (!evidence)
|
|
10639
|
+
return false;
|
|
10640
|
+
return findingVerificationPasses(entry.data.candidateId, {
|
|
10641
|
+
kind: evidence.data.kind,
|
|
10642
|
+
candidateId: evidence.data.candidateId,
|
|
10643
|
+
status: evidenceById.get(id) ?? "missing"
|
|
10644
|
+
});
|
|
10485
10645
|
});
|
|
10486
10646
|
if (!verified)
|
|
10487
10647
|
return failure("requirements_unsatisfied", "fixed findings require current verification");
|
|
@@ -10497,7 +10657,12 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10497
10657
|
}
|
|
10498
10658
|
}
|
|
10499
10659
|
}
|
|
10500
|
-
const blocking = evaluations.filter((item) =>
|
|
10660
|
+
const blocking = evaluations.filter((item) => {
|
|
10661
|
+
if (item.status !== "unsatisfied" && item.status !== "unavailable")
|
|
10662
|
+
return false;
|
|
10663
|
+
const requirement = view.task.policy?.requirements.find((candidate) => candidate.id === item.requirementId);
|
|
10664
|
+
return !requirement || requirement.before === "close";
|
|
10665
|
+
});
|
|
10501
10666
|
if (requestedOutcome !== "stopped" && blocking.length)
|
|
10502
10667
|
return failure("requirements_unsatisfied", "applicable requirements are unsatisfied", {
|
|
10503
10668
|
requirementIds: blocking.map((item) => item.requirementId),
|
|
@@ -10540,15 +10705,15 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10540
10705
|
// packages/workit-core/src/core/methods.ts
|
|
10541
10706
|
var METHODS = {
|
|
10542
10707
|
"workit-challenge": { dimensions: ["challenge", "decisions"] },
|
|
10543
|
-
"workit-behavioral-tdd": { dimensions: ["testing"] },
|
|
10544
|
-
"workit-review": { dimensions: ["review"], ruleIds: ["fresh-context-review"] },
|
|
10708
|
+
"workit-behavioral-tdd": { dimensions: ["testing"], ruleIds: ["mechanical-existing-checks"] },
|
|
10709
|
+
"workit-review": { dimensions: ["review"], ruleIds: ["fresh-context-review", "self-review"] },
|
|
10545
10710
|
"workit-plan": { dimensions: ["artifacts", "continuity"] },
|
|
10546
10711
|
"workit-implement": { dimensions: ["delegation"] },
|
|
10547
|
-
"workit-debug": {
|
|
10548
|
-
"workit-handoff": {
|
|
10712
|
+
"workit-debug": {},
|
|
10713
|
+
"workit-handoff": {},
|
|
10549
10714
|
"workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
|
|
10550
10715
|
};
|
|
10551
|
-
var methodMatches = (
|
|
10716
|
+
var methodMatches = (definition, requirement) => definition.dimensions?.includes(requirement.dimension) === true || definition.ruleIds?.includes(requirement.ruleId) === true;
|
|
10552
10717
|
var assuranceFor = (id, definition, capabilities) => {
|
|
10553
10718
|
const keys = new Set([
|
|
10554
10719
|
id,
|
|
@@ -10569,7 +10734,7 @@ var assuranceFor = (id, definition, capabilities) => {
|
|
|
10569
10734
|
};
|
|
10570
10735
|
function selectMethods(policy, capabilities) {
|
|
10571
10736
|
return Object.entries(METHODS).flatMap(([id, definition]) => {
|
|
10572
|
-
const requirements = policy.requirements.filter((requirement) => methodMatches(
|
|
10737
|
+
const requirements = policy.requirements.filter((requirement) => methodMatches(definition, requirement));
|
|
10573
10738
|
if (requirements.length === 0)
|
|
10574
10739
|
return [];
|
|
10575
10740
|
const capability = assuranceFor(id, definition, capabilities);
|
|
@@ -10795,6 +10960,27 @@ function shouldDenyShellRoute(dir, command) {
|
|
|
10795
10960
|
return null;
|
|
10796
10961
|
return shellRouteIntent(command);
|
|
10797
10962
|
}
|
|
10963
|
+
// packages/workit-core/src/core/config.ts
|
|
10964
|
+
var PRESETS = {
|
|
10965
|
+
gitflow: {
|
|
10966
|
+
allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
|
|
10967
|
+
protected: ["main", "develop", "master", "prod", "production"]
|
|
10968
|
+
},
|
|
10969
|
+
"github-flow": { allowed: ["*"], protected: ["main"] },
|
|
10970
|
+
"trunk-based": { allowed: ["*"], protected: ["main"] },
|
|
10971
|
+
custom: { allowed: [], protected: [] }
|
|
10972
|
+
};
|
|
10973
|
+
var DEFAULTS = {
|
|
10974
|
+
locale: "en",
|
|
10975
|
+
localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
|
|
10976
|
+
timezone: "America/Santiago",
|
|
10977
|
+
branchPolicy: {
|
|
10978
|
+
preset: "gitflow",
|
|
10979
|
+
allowed: [...PRESETS.gitflow.allowed],
|
|
10980
|
+
protected: [...PRESETS.gitflow.protected]
|
|
10981
|
+
},
|
|
10982
|
+
commitPolicy: { preset: "conventional" }
|
|
10983
|
+
};
|
|
10798
10984
|
// packages/workit-core/src/core/external-action.ts
|
|
10799
10985
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
10800
10986
|
object({
|
|
@@ -11332,6 +11518,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11332
11518
|
if (task.data.status === "paused" && (input.state === "running" || input.state === "cancelling"))
|
|
11333
11519
|
return failure("invalid_transition", "paused task cannot run a worker");
|
|
11334
11520
|
const notStarted = input.dispatch === true;
|
|
11521
|
+
if (notStarted && entry.data.state === "stopped" && input.session === null)
|
|
11522
|
+
return success(task.data.revision, workspace.data.revision, entry);
|
|
11335
11523
|
if (notStarted && (input.state !== "stopped" || input.session !== null))
|
|
11336
11524
|
return failure("invalid_input", "a never-dispatched worker stops with no session");
|
|
11337
11525
|
if (notStarted && entry.data.session !== null)
|
|
@@ -11363,7 +11551,7 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11363
11551
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
11364
11552
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
11365
11553
|
taskId: task.data.id,
|
|
11366
|
-
|
|
11554
|
+
expectedRevision: input.expectedRevision,
|
|
11367
11555
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
11368
11556
|
now: input.now,
|
|
11369
11557
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
@@ -11411,90 +11599,22 @@ var importedProvenance = (context) => ({
|
|
|
11411
11599
|
receipts: []
|
|
11412
11600
|
});
|
|
11413
11601
|
var portableTask = (task) => {
|
|
11414
|
-
const
|
|
11415
|
-
|
|
11416
|
-
|
|
11417
|
-
|
|
11418
|
-
|
|
11419
|
-
|
|
11420
|
-
|
|
11421
|
-
|
|
11422
|
-
|
|
11423
|
-
|
|
11424
|
-
|
|
11425
|
-
|
|
11426
|
-
|
|
11427
|
-
];
|
|
11428
|
-
for (const entry of entries)
|
|
11429
|
-
entry.provenance = portableProvenance(entry.provenance);
|
|
11430
|
-
for (const worker of clone.workers)
|
|
11431
|
-
worker.data.session = null;
|
|
11432
|
-
clone.intent = {
|
|
11433
|
-
...clone.intent,
|
|
11434
|
-
data: { ...clone.intent.data, authorityRefs: portableRefs(clone.intent.data.authorityRefs) }
|
|
11602
|
+
const stripped = rewriteRecordRefs(task, taskRecordSchema, portableRef);
|
|
11603
|
+
if (typeof stripped !== "object" || stripped === null || Array.isArray(stripped))
|
|
11604
|
+
throw new TypeError("portable task rewrite produced a non-record");
|
|
11605
|
+
const clone = structuredClone(stripped);
|
|
11606
|
+
const demote = (facts, signals, consequences) => {
|
|
11607
|
+
for (const fact of facts)
|
|
11608
|
+
Object.assign(fact, portableFact(fact));
|
|
11609
|
+
for (const name of Object.keys(signals)) {
|
|
11610
|
+
const signal = signals[name];
|
|
11611
|
+
Object.assign(signal, portableSignal(signal));
|
|
11612
|
+
}
|
|
11613
|
+
for (const consequence of consequences)
|
|
11614
|
+
Object.assign(consequence.fact, portableFact(consequence.fact));
|
|
11435
11615
|
};
|
|
11436
|
-
|
|
11437
|
-
|
|
11438
|
-
return source ? [
|
|
11439
|
-
{
|
|
11440
|
-
...constraint,
|
|
11441
|
-
source,
|
|
11442
|
-
requires: constraint.requires.map((requirement) => ({
|
|
11443
|
-
...requirement,
|
|
11444
|
-
refs: portableRefs(requirement.refs)
|
|
11445
|
-
}))
|
|
11446
|
-
}
|
|
11447
|
-
] : [];
|
|
11448
|
-
});
|
|
11449
|
-
clone.assessments = clone.assessments.map((entry) => ({
|
|
11450
|
-
...entry,
|
|
11451
|
-
data: {
|
|
11452
|
-
...entry.data,
|
|
11453
|
-
facts: entry.data.facts.map(portableFact),
|
|
11454
|
-
signals: Object.fromEntries(Object.entries(entry.data.signals).map(([name, signal]) => [name, portableSignal(signal)])),
|
|
11455
|
-
consequences: entry.data.consequences.map((consequence) => ({
|
|
11456
|
-
...consequence,
|
|
11457
|
-
fact: portableFact(consequence.fact)
|
|
11458
|
-
})),
|
|
11459
|
-
verification: entry.data.verification.map((verification) => ({
|
|
11460
|
-
...verification,
|
|
11461
|
-
availableChecks: portableRefs(verification.availableChecks)
|
|
11462
|
-
}))
|
|
11463
|
-
}
|
|
11464
|
-
}));
|
|
11465
|
-
clone.progress = {
|
|
11466
|
-
...clone.progress,
|
|
11467
|
-
blockers: clone.progress.blockers.map((blocker) => ({
|
|
11468
|
-
...blocker,
|
|
11469
|
-
refs: portableRefs(blocker.refs)
|
|
11470
|
-
}))
|
|
11471
|
-
};
|
|
11472
|
-
clone.evidence = clone.evidence.map((entry) => ({
|
|
11473
|
-
...entry,
|
|
11474
|
-
data: {
|
|
11475
|
-
...entry.data,
|
|
11476
|
-
refs: portableRefs(entry.data.refs),
|
|
11477
|
-
reviewContext: portableRef(entry.data.reviewContext)
|
|
11478
|
-
}
|
|
11479
|
-
}));
|
|
11480
|
-
clone.decisions = clone.decisions.map((entry) => ({
|
|
11481
|
-
...entry,
|
|
11482
|
-
data: {
|
|
11483
|
-
...entry.data,
|
|
11484
|
-
consumption: entry.data.consumption ? portableRef(entry.data.consumption.actionRef) ? {
|
|
11485
|
-
...entry.data.consumption,
|
|
11486
|
-
actionRef: portableRef(entry.data.consumption.actionRef)
|
|
11487
|
-
} : null : null,
|
|
11488
|
-
binding: {
|
|
11489
|
-
...entry.data.binding,
|
|
11490
|
-
contentRefs: portableRefs(entry.data.binding.contentRefs)
|
|
11491
|
-
}
|
|
11492
|
-
}
|
|
11493
|
-
}));
|
|
11494
|
-
clone.findings = clone.findings.map((entry) => ({
|
|
11495
|
-
...entry,
|
|
11496
|
-
data: { ...entry.data, refs: portableRefs(entry.data.refs) }
|
|
11497
|
-
}));
|
|
11616
|
+
for (const entry of clone.assessments)
|
|
11617
|
+
demote(entry.data.facts, entry.data.signals, entry.data.consequences);
|
|
11498
11618
|
clone.candidates = [];
|
|
11499
11619
|
return clone;
|
|
11500
11620
|
};
|
|
@@ -11519,15 +11639,13 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11519
11639
|
allocate(entry.id);
|
|
11520
11640
|
const fresh = (id) => allocate(id);
|
|
11521
11641
|
const provenance = importedProvenance(context);
|
|
11642
|
+
const remap = (ref) => mapRef(ref, ids);
|
|
11522
11643
|
const intent = {
|
|
11523
11644
|
...source.intent,
|
|
11524
11645
|
id: fresh(source.intent.id),
|
|
11525
11646
|
recordedAt: timestamp,
|
|
11526
11647
|
provenance,
|
|
11527
|
-
data:
|
|
11528
|
-
...source.intent.data,
|
|
11529
|
-
authorityRefs: source.intent.data.authorityRefs.map((ref) => mapRef(ref, ids))
|
|
11530
|
-
}
|
|
11648
|
+
data: rewriteRecordRefs(source.intent.data, intentSchema, remap)
|
|
11531
11649
|
};
|
|
11532
11650
|
const assessments = source.assessments.map((entry) => {
|
|
11533
11651
|
const data = entry.data;
|
|
@@ -11536,28 +11654,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11536
11654
|
id: fresh(entry.id),
|
|
11537
11655
|
recordedAt: timestamp,
|
|
11538
11656
|
provenance,
|
|
11539
|
-
data:
|
|
11540
|
-
...data,
|
|
11541
|
-
facts: data.facts.map((fact) => ({
|
|
11542
|
-
...fact,
|
|
11543
|
-
refs: fact.refs.map((ref) => mapRef(ref, ids))
|
|
11544
|
-
})),
|
|
11545
|
-
signals: Object.fromEntries(Object.entries(data.signals).map(([name, signal]) => [
|
|
11546
|
-
name,
|
|
11547
|
-
{ ...signal, refs: signal.refs.map((ref) => mapRef(ref, ids)) }
|
|
11548
|
-
])),
|
|
11549
|
-
consequences: data.consequences.map((consequence) => ({
|
|
11550
|
-
...consequence,
|
|
11551
|
-
fact: {
|
|
11552
|
-
...consequence.fact,
|
|
11553
|
-
refs: consequence.fact.refs.map((ref) => mapRef(ref, ids))
|
|
11554
|
-
}
|
|
11555
|
-
})),
|
|
11556
|
-
verification: data.verification.map((verification) => ({
|
|
11557
|
-
...verification,
|
|
11558
|
-
availableChecks: verification.availableChecks.map((ref) => mapRef(ref, ids))
|
|
11559
|
-
}))
|
|
11560
|
-
}
|
|
11657
|
+
data: rewriteRecordRefs(data, assessmentSchema, remap)
|
|
11561
11658
|
};
|
|
11562
11659
|
});
|
|
11563
11660
|
const evidence = source.evidence.map((entry) => ({
|
|
@@ -11565,11 +11662,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11565
11662
|
id: fresh(entry.id),
|
|
11566
11663
|
recordedAt: timestamp,
|
|
11567
11664
|
provenance,
|
|
11568
|
-
data:
|
|
11569
|
-
...entry.data,
|
|
11570
|
-
refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
|
|
11571
|
-
reviewContext: entry.data.reviewContext ? mapRef(entry.data.reviewContext, ids) : null
|
|
11572
|
-
}
|
|
11665
|
+
data: rewriteRecordRefs(entry.data, evidenceSchema, remap)
|
|
11573
11666
|
}));
|
|
11574
11667
|
const decisions = source.decisions.map((entry) => ({
|
|
11575
11668
|
...entry,
|
|
@@ -11577,21 +11670,23 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11577
11670
|
recordedAt: timestamp,
|
|
11578
11671
|
provenance
|
|
11579
11672
|
}));
|
|
11580
|
-
const findings = source.findings.map((entry) =>
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11673
|
+
const findings = source.findings.map((entry) => {
|
|
11674
|
+
const data = rewriteRecordRefs(entry.data, findingSchema, remap);
|
|
11675
|
+
return {
|
|
11676
|
+
...entry,
|
|
11677
|
+
id: fresh(entry.id),
|
|
11678
|
+
recordedAt: timestamp,
|
|
11679
|
+
provenance,
|
|
11680
|
+
data: {
|
|
11681
|
+
...data,
|
|
11682
|
+
resolution: data.resolution ? {
|
|
11683
|
+
...data.resolution,
|
|
11684
|
+
evidenceIds: data.resolution.evidenceIds.map((id) => ids.get(id) ?? id),
|
|
11685
|
+
decisionIds: data.resolution.decisionIds.map((id) => ids.get(id) ?? id)
|
|
11686
|
+
} : null
|
|
11687
|
+
}
|
|
11688
|
+
};
|
|
11689
|
+
});
|
|
11595
11690
|
const workers = source.workers.map((entry) => ({
|
|
11596
11691
|
...entry,
|
|
11597
11692
|
id: fresh(entry.id),
|
|
@@ -11634,19 +11729,21 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11634
11729
|
closure: null,
|
|
11635
11730
|
assessments,
|
|
11636
11731
|
evidence,
|
|
11637
|
-
decisions: decisions.map((entry) =>
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
...entry
|
|
11641
|
-
|
|
11642
|
-
...
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11732
|
+
decisions: decisions.map((entry) => {
|
|
11733
|
+
const data = rewriteRecordRefs(entry.data, decisionSchema, remap);
|
|
11734
|
+
return {
|
|
11735
|
+
...entry,
|
|
11736
|
+
data: {
|
|
11737
|
+
...data,
|
|
11738
|
+
binding: {
|
|
11739
|
+
...data.binding,
|
|
11740
|
+
taskId: "",
|
|
11741
|
+
workspaceId: destinationWorkspaceId
|
|
11742
|
+
},
|
|
11743
|
+
consumption: null
|
|
11744
|
+
}
|
|
11745
|
+
};
|
|
11746
|
+
}),
|
|
11650
11747
|
...actionProgress ? { actionProgress } : {},
|
|
11651
11748
|
findings,
|
|
11652
11749
|
workers,
|
|
@@ -11729,7 +11826,7 @@ class WorkitCore {
|
|
|
11729
11826
|
activeWorkerBlocker(task, workspace) {
|
|
11730
11827
|
if (workspace.writer)
|
|
11731
11828
|
return failure("recovery_required", "writer ownership must be released first");
|
|
11732
|
-
if (task.workers.some((entry) =>
|
|
11829
|
+
if (task.workers.some((entry) => workerBlocksTransition("close", entry.data.state)))
|
|
11733
11830
|
return failure("recovery_required", "worker state requires reconciliation");
|
|
11734
11831
|
return null;
|
|
11735
11832
|
}
|
|
@@ -11975,37 +12072,33 @@ class WorkitCore {
|
|
|
11975
12072
|
const currentCandidate = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
11976
12073
|
if (!currentCandidate.ok)
|
|
11977
12074
|
return currentCandidate;
|
|
11978
|
-
|
|
11979
|
-
|
|
11980
|
-
evidence.candidateId ??= currentCandidate.data.id;
|
|
11981
|
-
} else {
|
|
11982
|
-
evidence.beforeCandidateId ??= null;
|
|
11983
|
-
evidence.candidateId ??= null;
|
|
11984
|
-
}
|
|
12075
|
+
const pin = helper && helper.ok ? helper.data.data.assignment.candidateId : null;
|
|
12076
|
+
const bound = resolveBinding(evidence, pin, currentCandidate.data.id);
|
|
11985
12077
|
if (helper?.ok) {
|
|
11986
12078
|
const assignment = helper.data.data.assignment;
|
|
11987
|
-
if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || assignment
|
|
12079
|
+
if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || !checkPin(assignment, bound.beforeCandidateId, bound.candidateId) || !refsWithinScope(evidence.refs, assignment.scope) || !refsWithinScope(evidence.refs, task.data.intent.data.scope))
|
|
11988
12080
|
return failure("permission_denied", "evidence is outside the worker assignment");
|
|
11989
12081
|
}
|
|
11990
12082
|
const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
|
|
11991
|
-
for (const candidateId of [
|
|
12083
|
+
for (const candidateId of [bound.beforeCandidateId, bound.candidateId]) {
|
|
11992
12084
|
if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
|
|
11993
12085
|
return failure("invalid_input", "evidence candidate is not a captured candidate");
|
|
11994
12086
|
}
|
|
12087
|
+
const boundEvidence = { ...evidence, ...bound };
|
|
11995
12088
|
const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
|
|
11996
12089
|
const candidates = current.candidates.some((item) => item.id === currentCandidate.data.id) ? current.candidates : [...current.candidates, currentCandidate.data];
|
|
11997
12090
|
const entry = {
|
|
11998
12091
|
id: newId(),
|
|
11999
12092
|
recordedAt: mutation.now,
|
|
12000
12093
|
provenance: provenance(this.context, "agent_reported"),
|
|
12001
|
-
data:
|
|
12094
|
+
data: boundEvidence
|
|
12002
12095
|
};
|
|
12003
12096
|
return success(mutation.revision, null, {
|
|
12004
12097
|
...current,
|
|
12005
12098
|
candidates,
|
|
12006
12099
|
evidence: [...current.evidence, entry],
|
|
12007
12100
|
findings: current.findings.map((finding) => {
|
|
12008
|
-
if (finding.data.disposition === "open" ||
|
|
12101
|
+
if (finding.data.disposition === "open" || boundEvidence.result === "skipped")
|
|
12009
12102
|
return finding;
|
|
12010
12103
|
if (finding.data.disposition !== "fixed")
|
|
12011
12104
|
return finding;
|
|
@@ -12014,8 +12107,12 @@ class WorkitCore {
|
|
|
12014
12107
|
item.evidenceId,
|
|
12015
12108
|
item.status
|
|
12016
12109
|
]));
|
|
12017
|
-
const verified = withEntry.evidence.some((item) =>
|
|
12018
|
-
|
|
12110
|
+
const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
|
|
12111
|
+
kind: item.data.kind,
|
|
12112
|
+
candidateId: item.data.candidateId,
|
|
12113
|
+
status: statuses.get(item.id) ?? "missing"
|
|
12114
|
+
}));
|
|
12115
|
+
const contradicted = boundEvidence.result === "failed" && finding.data.refs.some((left) => boundEvidence.refs.some((right) => sameValue(left, right)));
|
|
12019
12116
|
return verified && !contradicted ? finding : { ...finding, data: { ...finding.data, disposition: "open", resolution: null } };
|
|
12020
12117
|
})
|
|
12021
12118
|
});
|
|
@@ -12173,7 +12270,7 @@ class WorkitCore {
|
|
|
12173
12270
|
if (input.action === "record") {
|
|
12174
12271
|
if (helper?.ok) {
|
|
12175
12272
|
const assignment = helper.data.data.assignment;
|
|
12176
|
-
if (!bindingCovers(assignment.scope, input.scope) || !bindingCovers(task.data.intent.data.scope, input.scope) || assignment
|
|
12273
|
+
if (!bindingCovers(assignment.scope, input.scope) || !bindingCovers(task.data.intent.data.scope, input.scope) || !checkPin(assignment, null, input.candidateId ?? null) || !refsWithinScope(input.refs, assignment.scope) || !refsWithinScope(input.refs, task.data.intent.data.scope))
|
|
12177
12274
|
return failure("permission_denied", "finding is outside the worker assignment");
|
|
12178
12275
|
}
|
|
12179
12276
|
if (input.candidateId && !task.data.candidates.some((candidate) => candidate.id === input.candidateId))
|
|
@@ -12220,7 +12317,11 @@ class WorkitCore {
|
|
|
12220
12317
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
12221
12318
|
const verified = evidence.some((entry) => {
|
|
12222
12319
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
12223
|
-
return
|
|
12320
|
+
return findingVerificationPasses(finding.data.candidateId, {
|
|
12321
|
+
kind: entry.data.kind,
|
|
12322
|
+
candidateId: entry.data.candidateId,
|
|
12323
|
+
status: evaluation?.status ?? "missing"
|
|
12324
|
+
});
|
|
12224
12325
|
});
|
|
12225
12326
|
if (!verified)
|
|
12226
12327
|
return failure("permission_denied", "fixed findings require passing verification evidence");
|
|
@@ -12232,7 +12333,7 @@ class WorkitCore {
|
|
|
12232
12333
|
if (entry.data.result === "missing" || entry.data.result === "skipped")
|
|
12233
12334
|
return false;
|
|
12234
12335
|
const candidateMatches = finding.data.candidateId === null ? entry.data.candidateId === null : entry.data.candidateId === finding.data.candidateId || entry.data.beforeCandidateId === finding.data.candidateId;
|
|
12235
|
-
const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) =>
|
|
12336
|
+
const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) => sameValue(left, right)));
|
|
12236
12337
|
return candidateMatches && (referenceMatches || entry.data.claim === finding.data.claim);
|
|
12237
12338
|
});
|
|
12238
12339
|
if (!relevant)
|
|
@@ -12310,7 +12411,7 @@ class WorkitCore {
|
|
|
12310
12411
|
const workerId = newId();
|
|
12311
12412
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12312
12413
|
taskId: task.data.id,
|
|
12313
|
-
|
|
12414
|
+
expectedRevision: input.expectedRevision,
|
|
12314
12415
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12315
12416
|
now: trustedNow(this.context),
|
|
12316
12417
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12348,7 +12449,7 @@ class WorkitCore {
|
|
|
12348
12449
|
return failure("permission_denied", "worker report is outside the worker assignment");
|
|
12349
12450
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12350
12451
|
taskId: task.data.id,
|
|
12351
|
-
|
|
12452
|
+
expectedRevision: input.expectedRevision,
|
|
12352
12453
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12353
12454
|
now: trustedNow(this.context),
|
|
12354
12455
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12372,24 +12473,25 @@ class WorkitCore {
|
|
|
12372
12473
|
return failure("invalid_transition", "closed task cannot cancel workers");
|
|
12373
12474
|
if (entry.data.state === "unknown")
|
|
12374
12475
|
return failure("recovery_required", "worker state requires recovery");
|
|
12375
|
-
if (entry.data.state === "stopped")
|
|
12376
|
-
|
|
12377
|
-
|
|
12476
|
+
if (entry.data.state === "stopped") {
|
|
12477
|
+
const stopped = task.data.workers.find((candidate) => candidate.id === input.workerId);
|
|
12478
|
+
return success(task.data.revision, workspace.data.revision, stopped);
|
|
12479
|
+
}
|
|
12378
12480
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12379
12481
|
taskId: task.data.id,
|
|
12380
|
-
|
|
12482
|
+
expectedRevision: input.expectedRevision,
|
|
12381
12483
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12382
12484
|
now: trustedNow(this.context),
|
|
12383
|
-
workspace: (current, mutation) => success(mutation.revision, mutation.revision,
|
|
12485
|
+
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
12486
|
+
...current,
|
|
12487
|
+
writer: current.writer?.owner.workerId === input.workerId ? null : current.writer
|
|
12488
|
+
}),
|
|
12384
12489
|
task: (current, mutation) => success(mutation.revision, null, {
|
|
12385
12490
|
...current,
|
|
12386
12491
|
workers: current.workers.map((candidate) => candidate.id === input.workerId ? {
|
|
12387
12492
|
...candidate,
|
|
12388
12493
|
recordedAt: mutation.now,
|
|
12389
|
-
data: {
|
|
12390
|
-
...candidate.data,
|
|
12391
|
-
state: settles ? "stopped" : "cancelling"
|
|
12392
|
-
}
|
|
12494
|
+
data: { ...candidate.data, state: "stopped" }
|
|
12393
12495
|
} : candidate)
|
|
12394
12496
|
})
|
|
12395
12497
|
});
|
|
@@ -12486,7 +12588,7 @@ class WorkitCore {
|
|
|
12486
12588
|
return attested;
|
|
12487
12589
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12488
12590
|
taskId: task.data.id,
|
|
12489
|
-
|
|
12591
|
+
expectedRevision: input.expectedRevision,
|
|
12490
12592
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12491
12593
|
now: trustedNow(this.context),
|
|
12492
12594
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12652,6 +12754,12 @@ class WorkitCore {
|
|
|
12652
12754
|
if (!sameSession2(helper.data.data.session, this.context))
|
|
12653
12755
|
return failure("permission_denied", "worker session does not match the caller");
|
|
12654
12756
|
}
|
|
12757
|
+
const gate = this.view(task.data);
|
|
12758
|
+
if (gate.ok) {
|
|
12759
|
+
const writeGates = (task.data.policy?.requirements ?? []).filter((requirement) => requirement.before === "write" && gate.data.requirements.some((evaluation) => evaluation.requirementId === requirement.id && (evaluation.status === "unsatisfied" || evaluation.status === "unavailable")));
|
|
12760
|
+
if (writeGates.length)
|
|
12761
|
+
return failure("requirements_unsatisfied", "writer ownership is gated by unsatisfied requirements; record their evidence or an approved limitation waiver first", { requirementIds: writeGates.map((requirement) => requirement.id) });
|
|
12762
|
+
}
|
|
12655
12763
|
const owner = {
|
|
12656
12764
|
taskId: task.data.id,
|
|
12657
12765
|
workerId: helperId,
|
|
@@ -12663,7 +12771,7 @@ class WorkitCore {
|
|
|
12663
12771
|
};
|
|
12664
12772
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12665
12773
|
taskId: task.data.id,
|
|
12666
|
-
|
|
12774
|
+
expectedRevision: input.expectedRevision,
|
|
12667
12775
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12668
12776
|
now: trustedNow(this.context),
|
|
12669
12777
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
@@ -12694,7 +12802,7 @@ class WorkitCore {
|
|
|
12694
12802
|
}
|
|
12695
12803
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12696
12804
|
taskId: task.data.id,
|
|
12697
|
-
|
|
12805
|
+
expectedRevision: input.expectedRevision,
|
|
12698
12806
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12699
12807
|
now: trustedNow(this.context),
|
|
12700
12808
|
workspace: (currentWorkspace, mutation) => success(mutation.revision, mutation.revision, { ...currentWorkspace, writer: null }),
|
|
@@ -12943,7 +13051,15 @@ class WorkitCore {
|
|
|
12943
13051
|
if (seen.has(observation.workerId))
|
|
12944
13052
|
return failure("invalid_input", "worker observations must be unique");
|
|
12945
13053
|
seen.add(observation.workerId);
|
|
12946
|
-
|
|
13054
|
+
const freshTask = this.store.readTask(view.task.id);
|
|
13055
|
+
const freshWorkspace = this.store.readWorkspace();
|
|
13056
|
+
if (!freshTask.ok)
|
|
13057
|
+
return freshTask;
|
|
13058
|
+
if (!freshWorkspace.ok)
|
|
13059
|
+
return freshWorkspace;
|
|
13060
|
+
if (!freshWorkspace.data)
|
|
13061
|
+
return failure("not_found", "workspace not found");
|
|
13062
|
+
if (observation.taskId !== view.task.id || observation.expectedRevision !== freshTask.data.revision || observation.expectedWorkspaceRevision !== freshWorkspace.data.revision)
|
|
12947
13063
|
return failure("revision_conflict", "worker observation is stale");
|
|
12948
13064
|
if (!view.task.workers.some((entry) => entry.id === observation.workerId))
|
|
12949
13065
|
return failure("permission_denied", "worker observation is not assigned");
|
|
@@ -13029,19 +13145,26 @@ class WorkitCore {
|
|
|
13029
13145
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
13030
13146
|
resumeCandidate = current.data.candidate;
|
|
13031
13147
|
}
|
|
13032
|
-
const blocker = this.activeWorkerBlocker(task.data, workspace.data);
|
|
13148
|
+
const blocker = status === "paused" ? workspace.data.writer ? failure("recovery_required", "writer ownership must be released first") : null : this.activeWorkerBlocker(task.data, workspace.data);
|
|
13033
13149
|
if (blocker)
|
|
13034
13150
|
return blocker;
|
|
13151
|
+
let pauseCandidate = null;
|
|
13152
|
+
if (status === "paused") {
|
|
13153
|
+
const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
13154
|
+
if (!captured.ok)
|
|
13155
|
+
return captured;
|
|
13156
|
+
pauseCandidate = captured.data;
|
|
13157
|
+
}
|
|
13035
13158
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
13036
13159
|
taskId: task.data.id,
|
|
13037
|
-
|
|
13160
|
+
expectedRevision: input.expectedRevision,
|
|
13038
13161
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
13039
13162
|
now: trustedNow(this.context),
|
|
13040
13163
|
workspace: (workspace, context) => success(context.revision, context.revision, workspace),
|
|
13041
13164
|
task: (current, context) => success(context.revision, null, {
|
|
13042
13165
|
...current,
|
|
13043
13166
|
status,
|
|
13044
|
-
candidates: resumeCandidate && !current.candidates.some((candidate) => candidate.id === resumeCandidate.id) ? [...current.candidates, resumeCandidate] : current.candidates,
|
|
13167
|
+
candidates: (resumeCandidate ?? pauseCandidate) && !current.candidates.some((candidate) => candidate.id === (resumeCandidate ?? pauseCandidate).id) ? [...current.candidates, resumeCandidate ?? pauseCandidate] : current.candidates,
|
|
13045
13168
|
progress: current.progress,
|
|
13046
13169
|
pauseReason: status === "paused" ? input.reason ?? null : null
|
|
13047
13170
|
})
|
|
@@ -13079,12 +13202,13 @@ class WorkitCore {
|
|
|
13079
13202
|
return workspace;
|
|
13080
13203
|
if (!workspace.data)
|
|
13081
13204
|
return failure("not_found", "workspace not found");
|
|
13082
|
-
|
|
13083
|
-
|
|
13205
|
+
const revisionBlocker = this.activeWorkerBlocker(task.data, workspace.data);
|
|
13206
|
+
if (revisionBlocker)
|
|
13207
|
+
return revisionBlocker;
|
|
13084
13208
|
this.fillRevisions(input, task.data, workspace.data);
|
|
13085
13209
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
13086
13210
|
taskId: task.data.id,
|
|
13087
|
-
|
|
13211
|
+
expectedRevision: input.expectedRevision,
|
|
13088
13212
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
13089
13213
|
now: trustedNow(this.context),
|
|
13090
13214
|
workspace: (workspace, context) => success(context.revision, context.revision, workspace),
|
|
@@ -13140,7 +13264,7 @@ class WorkitCore {
|
|
|
13140
13264
|
return closure;
|
|
13141
13265
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
13142
13266
|
taskId: task.data.id,
|
|
13143
|
-
|
|
13267
|
+
expectedRevision: input.expectedRevision,
|
|
13144
13268
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
13145
13269
|
now: trustedNow(this.context),
|
|
13146
13270
|
workspace: (value, context) => success(context.revision, context.revision, value),
|