@brainervirus/workit-codex 1.0.10 → 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 +2197 -2087
- package/dist/workit-hook.js +360 -213
- 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,
|
|
@@ -7574,10 +7657,11 @@ var decisionSchema = object({
|
|
|
7574
7657
|
presented: text,
|
|
7575
7658
|
approvedContent: text,
|
|
7576
7659
|
displayed: text.optional(),
|
|
7577
|
-
contentRefs: array(refSchema)
|
|
7660
|
+
contentRefs: array(refSchema),
|
|
7661
|
+
statedChoice: object({ ref: text, text }).strict().optional()
|
|
7578
7662
|
}).strict(),
|
|
7579
7663
|
digest,
|
|
7580
|
-
response: _enum(["approved", "rejected"]),
|
|
7664
|
+
response: _enum(["approved", "rejected", "stated"]),
|
|
7581
7665
|
requirementIds: array(digest),
|
|
7582
7666
|
revoked: object({ at: utc, reason: text }).strict().nullable(),
|
|
7583
7667
|
consumption: object({
|
|
@@ -9004,11 +9088,11 @@ class TaskStore {
|
|
|
9004
9088
|
return workspace;
|
|
9005
9089
|
if (!workspace.data)
|
|
9006
9090
|
return failure("not_found", "workspace not found");
|
|
9007
|
-
const
|
|
9008
|
-
if (!
|
|
9091
|
+
const expected = input.expectedRevision;
|
|
9092
|
+
if (!expected)
|
|
9009
9093
|
return failure("invalid_input", "task revision is required");
|
|
9010
|
-
if (task.data.revision !==
|
|
9011
|
-
return this.conflict(
|
|
9094
|
+
if (task.data.revision !== expected)
|
|
9095
|
+
return this.conflict(expected, task.data.revision);
|
|
9012
9096
|
if (workspace.data.revision !== input.expectedWorkspaceRevision)
|
|
9013
9097
|
return this.conflict(input.expectedWorkspaceRevision, workspace.data.revision);
|
|
9014
9098
|
const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
|
|
@@ -9542,6 +9626,7 @@ var same = (left, right) => {
|
|
|
9542
9626
|
};
|
|
9543
9627
|
var UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
|
|
9544
9628
|
var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
9629
|
+
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
9545
9630
|
var callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor };
|
|
9546
9631
|
var validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
|
|
9547
9632
|
const code = char.charCodeAt(0);
|
|
@@ -9603,6 +9688,36 @@ var trustedAuthority = (kind, provenance, expected, binding, caller) => {
|
|
|
9603
9688
|
verifiedAuthorities.set(token, { kind, provenance, ...expected, ...binding, caller });
|
|
9604
9689
|
return token;
|
|
9605
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
|
+
};
|
|
9606
9721
|
var scopeEqual = (left, right) => canonicalJson(left) === canonicalJson(right);
|
|
9607
9722
|
var parseSteps = (content) => {
|
|
9608
9723
|
let value;
|
|
@@ -9614,7 +9729,10 @@ var parseSteps = (content) => {
|
|
|
9614
9729
|
if (typeof value !== "object" || value === null || !("steps" in value))
|
|
9615
9730
|
return [];
|
|
9616
9731
|
const steps = value.steps;
|
|
9617
|
-
|
|
9732
|
+
if (!Array.isArray(steps))
|
|
9733
|
+
return null;
|
|
9734
|
+
const normalized = normalizeChainSteps(steps);
|
|
9735
|
+
return normalized;
|
|
9618
9736
|
};
|
|
9619
9737
|
var decisionMatches = (entry, purpose, binding) => {
|
|
9620
9738
|
const decision = entry.data;
|
|
@@ -9805,14 +9923,12 @@ var validateAction = (store, task, workspaceId, input, authority) => {
|
|
|
9805
9923
|
return content;
|
|
9806
9924
|
const requestedSteps = parseSteps(decision.binding.approvedContent);
|
|
9807
9925
|
if (requestedSteps === null)
|
|
9808
|
-
return failure("invalid_input", "bounded action steps must be a
|
|
9809
|
-
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)))
|
|
9810
9928
|
return failure("permission_denied", "bounded action steps do not match approved content");
|
|
9811
|
-
if (requestedSteps.some((step) => !step || typeof step !== "string") || new Set(requestedSteps).size !== requestedSteps.length)
|
|
9812
|
-
return failure("invalid_input", "bounded action steps must be unique and non-empty");
|
|
9813
9929
|
const stored = task.actionProgress?.find((item) => item.decisionId === input.decisionId);
|
|
9814
|
-
const workflow = stored ? { steps: stored.steps, completed: stored.completedSteps } : { steps: requestedSteps, completed: [] };
|
|
9815
|
-
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)))
|
|
9816
9932
|
return failure("permission_denied", "bounded action scope changed");
|
|
9817
9933
|
return success(null, null, { entry, workflow });
|
|
9818
9934
|
};
|
|
@@ -10367,13 +10483,28 @@ function evaluateEvidence(task, candidate) {
|
|
|
10367
10483
|
});
|
|
10368
10484
|
}
|
|
10369
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);
|
|
10370
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")));
|
|
10371
|
-
var applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && data.response === "approved" && 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 }));
|
|
10372
|
-
var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
|
|
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 }));
|
|
10503
|
+
var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
|
|
10373
10504
|
if (kind !== "passed")
|
|
10374
10505
|
return false;
|
|
10375
10506
|
if (dimension === "testing" || dimension === "verification")
|
|
10376
|
-
return evidenceKind === "check";
|
|
10507
|
+
return evidenceKind === "check" || ruleId === "pre-pr-cleanup" && evidenceKind === "artifact";
|
|
10377
10508
|
if (dimension === "review")
|
|
10378
10509
|
return evidenceKind === "review";
|
|
10379
10510
|
if (dimension === "investigation" || dimension === "challenge")
|
|
@@ -10382,6 +10513,21 @@ var evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
|
|
|
10382
10513
|
return evidenceKind === "artifact";
|
|
10383
10514
|
return false;
|
|
10384
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
|
+
};
|
|
10385
10531
|
function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller) {
|
|
10386
10532
|
const evidence = evaluateEvidence(task, candidate);
|
|
10387
10533
|
if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
|
|
@@ -10395,7 +10541,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10395
10541
|
return (task.policy?.requirements ?? []).map((requirement) => {
|
|
10396
10542
|
const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
|
|
10397
10543
|
const passed = related.filter(({ entry, evaluation }) => {
|
|
10398
|
-
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension))
|
|
10544
|
+
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
|
|
10399
10545
|
return false;
|
|
10400
10546
|
if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
|
|
10401
10547
|
return false;
|
|
@@ -10403,11 +10549,12 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10403
10549
|
return true;
|
|
10404
10550
|
const reviewSession = sessionFromRef(entry.data.reviewContext);
|
|
10405
10551
|
const sameImplementation = sameSession(reviewSession, task.intent.provenance.session);
|
|
10406
|
-
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));
|
|
10407
10553
|
return entry.data.kind === "review" && reviewSession !== null && sameSession(reviewSession, entry.provenance.session) && (requirement.ruleId === "self-review" || !sameImplementation && !sameEvidenceSession);
|
|
10408
10554
|
});
|
|
10409
10555
|
if (passed.length) {
|
|
10410
|
-
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;
|
|
10411
10558
|
if (withRed.length)
|
|
10412
10559
|
return {
|
|
10413
10560
|
requirementId: requirement.id,
|
|
@@ -10444,7 +10591,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10444
10591
|
status: "satisfied",
|
|
10445
10592
|
evidenceIds: [],
|
|
10446
10593
|
decisionIds: decisions.map((decision) => decision.id),
|
|
10447
|
-
reason: "an applicable approved decision satisfies the requirement"
|
|
10594
|
+
reason: "an applicable approved or stated decision satisfies the requirement"
|
|
10448
10595
|
};
|
|
10449
10596
|
const limitations = requirement.acceptanceAllowed ? applicableDecision2(task, workspace, requirement, checkoutRoot) : [];
|
|
10450
10597
|
if (limitations.length)
|
|
@@ -10456,6 +10603,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
10456
10603
|
reason: "an applicable approved limitation permits the missing evidence"
|
|
10457
10604
|
};
|
|
10458
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
|
+
};
|
|
10459
10614
|
return {
|
|
10460
10615
|
requirementId: requirement.id,
|
|
10461
10616
|
status: unavailable ? "unavailable" : "unsatisfied",
|
|
@@ -10480,7 +10635,13 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10480
10635
|
if (entry.data.disposition === "fixed") {
|
|
10481
10636
|
const verified = entry.data.resolution?.evidenceIds.some((id) => {
|
|
10482
10637
|
const evidence = view.task.evidence.find((item) => item.id === id);
|
|
10483
|
-
|
|
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
|
+
});
|
|
10484
10645
|
});
|
|
10485
10646
|
if (!verified)
|
|
10486
10647
|
return failure("requirements_unsatisfied", "fixed findings require current verification");
|
|
@@ -10496,7 +10657,12 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10496
10657
|
}
|
|
10497
10658
|
}
|
|
10498
10659
|
}
|
|
10499
|
-
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
|
+
});
|
|
10500
10666
|
if (requestedOutcome !== "stopped" && blocking.length)
|
|
10501
10667
|
return failure("requirements_unsatisfied", "applicable requirements are unsatisfied", {
|
|
10502
10668
|
requirementIds: blocking.map((item) => item.requirementId),
|
|
@@ -10539,15 +10705,15 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10539
10705
|
// packages/workit-core/src/core/methods.ts
|
|
10540
10706
|
var METHODS = {
|
|
10541
10707
|
"workit-challenge": { dimensions: ["challenge", "decisions"] },
|
|
10542
|
-
"workit-behavioral-tdd": { dimensions: ["testing"] },
|
|
10543
|
-
"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"] },
|
|
10544
10710
|
"workit-plan": { dimensions: ["artifacts", "continuity"] },
|
|
10545
10711
|
"workit-implement": { dimensions: ["delegation"] },
|
|
10546
|
-
"workit-debug": {
|
|
10547
|
-
"workit-handoff": {
|
|
10712
|
+
"workit-debug": {},
|
|
10713
|
+
"workit-handoff": {},
|
|
10548
10714
|
"workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
|
|
10549
10715
|
};
|
|
10550
|
-
var methodMatches = (
|
|
10716
|
+
var methodMatches = (definition, requirement) => definition.dimensions?.includes(requirement.dimension) === true || definition.ruleIds?.includes(requirement.ruleId) === true;
|
|
10551
10717
|
var assuranceFor = (id, definition, capabilities) => {
|
|
10552
10718
|
const keys = new Set([
|
|
10553
10719
|
id,
|
|
@@ -10568,7 +10734,7 @@ var assuranceFor = (id, definition, capabilities) => {
|
|
|
10568
10734
|
};
|
|
10569
10735
|
function selectMethods(policy, capabilities) {
|
|
10570
10736
|
return Object.entries(METHODS).flatMap(([id, definition]) => {
|
|
10571
|
-
const requirements = policy.requirements.filter((requirement) => methodMatches(
|
|
10737
|
+
const requirements = policy.requirements.filter((requirement) => methodMatches(definition, requirement));
|
|
10572
10738
|
if (requirements.length === 0)
|
|
10573
10739
|
return [];
|
|
10574
10740
|
const capability = assuranceFor(id, definition, capabilities);
|
|
@@ -10777,6 +10943,44 @@ function shellRouteIntent(command) {
|
|
|
10777
10943
|
}
|
|
10778
10944
|
return null;
|
|
10779
10945
|
}
|
|
10946
|
+
function hasLiveWorkitTask(dir) {
|
|
10947
|
+
try {
|
|
10948
|
+
if (typeof dir !== "string" || dir === "")
|
|
10949
|
+
return false;
|
|
10950
|
+
const listed = new TaskStore(dir).listTasks();
|
|
10951
|
+
if (!listed.ok)
|
|
10952
|
+
return false;
|
|
10953
|
+
return listed.data.some((task) => task.status === "active" || task.status === "paused");
|
|
10954
|
+
} catch {
|
|
10955
|
+
return false;
|
|
10956
|
+
}
|
|
10957
|
+
}
|
|
10958
|
+
function shouldDenyShellRoute(dir, command) {
|
|
10959
|
+
if (!hasLiveWorkitTask(dir))
|
|
10960
|
+
return null;
|
|
10961
|
+
return shellRouteIntent(command);
|
|
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
|
+
};
|
|
10780
10984
|
// packages/workit-core/src/core/external-action.ts
|
|
10781
10985
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
10782
10986
|
object({
|
|
@@ -11314,6 +11518,8 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11314
11518
|
if (task.data.status === "paused" && (input.state === "running" || input.state === "cancelling"))
|
|
11315
11519
|
return failure("invalid_transition", "paused task cannot run a worker");
|
|
11316
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);
|
|
11317
11523
|
if (notStarted && (input.state !== "stopped" || input.session !== null))
|
|
11318
11524
|
return failure("invalid_input", "a never-dispatched worker stops with no session");
|
|
11319
11525
|
if (notStarted && entry.data.session !== null)
|
|
@@ -11345,7 +11551,7 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11345
11551
|
return success(task.data.revision, workspace.data.revision, entry);
|
|
11346
11552
|
const changed = input.store.mutateTaskAndWorkspace({
|
|
11347
11553
|
taskId: task.data.id,
|
|
11348
|
-
|
|
11554
|
+
expectedRevision: input.expectedRevision,
|
|
11349
11555
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
11350
11556
|
now: input.now,
|
|
11351
11557
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
@@ -11393,90 +11599,22 @@ var importedProvenance = (context) => ({
|
|
|
11393
11599
|
receipts: []
|
|
11394
11600
|
});
|
|
11395
11601
|
var portableTask = (task) => {
|
|
11396
|
-
const
|
|
11397
|
-
|
|
11398
|
-
|
|
11399
|
-
|
|
11400
|
-
|
|
11401
|
-
|
|
11402
|
-
|
|
11403
|
-
|
|
11404
|
-
|
|
11405
|
-
|
|
11406
|
-
|
|
11407
|
-
|
|
11408
|
-
|
|
11409
|
-
];
|
|
11410
|
-
for (const entry of entries)
|
|
11411
|
-
entry.provenance = portableProvenance(entry.provenance);
|
|
11412
|
-
for (const worker of clone.workers)
|
|
11413
|
-
worker.data.session = null;
|
|
11414
|
-
clone.intent = {
|
|
11415
|
-
...clone.intent,
|
|
11416
|
-
data: { ...clone.intent.data, authorityRefs: portableRefs(clone.intent.data.authorityRefs) }
|
|
11417
|
-
};
|
|
11418
|
-
clone.constraints = clone.constraints.flatMap((constraint) => {
|
|
11419
|
-
const source = portableRef(constraint.source);
|
|
11420
|
-
return source ? [
|
|
11421
|
-
{
|
|
11422
|
-
...constraint,
|
|
11423
|
-
source,
|
|
11424
|
-
requires: constraint.requires.map((requirement) => ({
|
|
11425
|
-
...requirement,
|
|
11426
|
-
refs: portableRefs(requirement.refs)
|
|
11427
|
-
}))
|
|
11428
|
-
}
|
|
11429
|
-
] : [];
|
|
11430
|
-
});
|
|
11431
|
-
clone.assessments = clone.assessments.map((entry) => ({
|
|
11432
|
-
...entry,
|
|
11433
|
-
data: {
|
|
11434
|
-
...entry.data,
|
|
11435
|
-
facts: entry.data.facts.map(portableFact),
|
|
11436
|
-
signals: Object.fromEntries(Object.entries(entry.data.signals).map(([name, signal]) => [name, portableSignal(signal)])),
|
|
11437
|
-
consequences: entry.data.consequences.map((consequence) => ({
|
|
11438
|
-
...consequence,
|
|
11439
|
-
fact: portableFact(consequence.fact)
|
|
11440
|
-
})),
|
|
11441
|
-
verification: entry.data.verification.map((verification) => ({
|
|
11442
|
-
...verification,
|
|
11443
|
-
availableChecks: portableRefs(verification.availableChecks)
|
|
11444
|
-
}))
|
|
11445
|
-
}
|
|
11446
|
-
}));
|
|
11447
|
-
clone.progress = {
|
|
11448
|
-
...clone.progress,
|
|
11449
|
-
blockers: clone.progress.blockers.map((blocker) => ({
|
|
11450
|
-
...blocker,
|
|
11451
|
-
refs: portableRefs(blocker.refs)
|
|
11452
|
-
}))
|
|
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));
|
|
11453
11615
|
};
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
data: {
|
|
11457
|
-
...entry.data,
|
|
11458
|
-
refs: portableRefs(entry.data.refs),
|
|
11459
|
-
reviewContext: portableRef(entry.data.reviewContext)
|
|
11460
|
-
}
|
|
11461
|
-
}));
|
|
11462
|
-
clone.decisions = clone.decisions.map((entry) => ({
|
|
11463
|
-
...entry,
|
|
11464
|
-
data: {
|
|
11465
|
-
...entry.data,
|
|
11466
|
-
consumption: entry.data.consumption ? portableRef(entry.data.consumption.actionRef) ? {
|
|
11467
|
-
...entry.data.consumption,
|
|
11468
|
-
actionRef: portableRef(entry.data.consumption.actionRef)
|
|
11469
|
-
} : null : null,
|
|
11470
|
-
binding: {
|
|
11471
|
-
...entry.data.binding,
|
|
11472
|
-
contentRefs: portableRefs(entry.data.binding.contentRefs)
|
|
11473
|
-
}
|
|
11474
|
-
}
|
|
11475
|
-
}));
|
|
11476
|
-
clone.findings = clone.findings.map((entry) => ({
|
|
11477
|
-
...entry,
|
|
11478
|
-
data: { ...entry.data, refs: portableRefs(entry.data.refs) }
|
|
11479
|
-
}));
|
|
11616
|
+
for (const entry of clone.assessments)
|
|
11617
|
+
demote(entry.data.facts, entry.data.signals, entry.data.consequences);
|
|
11480
11618
|
clone.candidates = [];
|
|
11481
11619
|
return clone;
|
|
11482
11620
|
};
|
|
@@ -11501,15 +11639,13 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11501
11639
|
allocate(entry.id);
|
|
11502
11640
|
const fresh = (id) => allocate(id);
|
|
11503
11641
|
const provenance = importedProvenance(context);
|
|
11642
|
+
const remap = (ref) => mapRef(ref, ids);
|
|
11504
11643
|
const intent = {
|
|
11505
11644
|
...source.intent,
|
|
11506
11645
|
id: fresh(source.intent.id),
|
|
11507
11646
|
recordedAt: timestamp,
|
|
11508
11647
|
provenance,
|
|
11509
|
-
data:
|
|
11510
|
-
...source.intent.data,
|
|
11511
|
-
authorityRefs: source.intent.data.authorityRefs.map((ref) => mapRef(ref, ids))
|
|
11512
|
-
}
|
|
11648
|
+
data: rewriteRecordRefs(source.intent.data, intentSchema, remap)
|
|
11513
11649
|
};
|
|
11514
11650
|
const assessments = source.assessments.map((entry) => {
|
|
11515
11651
|
const data = entry.data;
|
|
@@ -11518,28 +11654,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11518
11654
|
id: fresh(entry.id),
|
|
11519
11655
|
recordedAt: timestamp,
|
|
11520
11656
|
provenance,
|
|
11521
|
-
data:
|
|
11522
|
-
...data,
|
|
11523
|
-
facts: data.facts.map((fact) => ({
|
|
11524
|
-
...fact,
|
|
11525
|
-
refs: fact.refs.map((ref) => mapRef(ref, ids))
|
|
11526
|
-
})),
|
|
11527
|
-
signals: Object.fromEntries(Object.entries(data.signals).map(([name, signal]) => [
|
|
11528
|
-
name,
|
|
11529
|
-
{ ...signal, refs: signal.refs.map((ref) => mapRef(ref, ids)) }
|
|
11530
|
-
])),
|
|
11531
|
-
consequences: data.consequences.map((consequence) => ({
|
|
11532
|
-
...consequence,
|
|
11533
|
-
fact: {
|
|
11534
|
-
...consequence.fact,
|
|
11535
|
-
refs: consequence.fact.refs.map((ref) => mapRef(ref, ids))
|
|
11536
|
-
}
|
|
11537
|
-
})),
|
|
11538
|
-
verification: data.verification.map((verification) => ({
|
|
11539
|
-
...verification,
|
|
11540
|
-
availableChecks: verification.availableChecks.map((ref) => mapRef(ref, ids))
|
|
11541
|
-
}))
|
|
11542
|
-
}
|
|
11657
|
+
data: rewriteRecordRefs(data, assessmentSchema, remap)
|
|
11543
11658
|
};
|
|
11544
11659
|
});
|
|
11545
11660
|
const evidence = source.evidence.map((entry) => ({
|
|
@@ -11547,11 +11662,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11547
11662
|
id: fresh(entry.id),
|
|
11548
11663
|
recordedAt: timestamp,
|
|
11549
11664
|
provenance,
|
|
11550
|
-
data:
|
|
11551
|
-
...entry.data,
|
|
11552
|
-
refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
|
|
11553
|
-
reviewContext: entry.data.reviewContext ? mapRef(entry.data.reviewContext, ids) : null
|
|
11554
|
-
}
|
|
11665
|
+
data: rewriteRecordRefs(entry.data, evidenceSchema, remap)
|
|
11555
11666
|
}));
|
|
11556
11667
|
const decisions = source.decisions.map((entry) => ({
|
|
11557
11668
|
...entry,
|
|
@@ -11559,21 +11670,23 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11559
11670
|
recordedAt: timestamp,
|
|
11560
11671
|
provenance
|
|
11561
11672
|
}));
|
|
11562
|
-
const findings = source.findings.map((entry) =>
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
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
|
+
});
|
|
11577
11690
|
const workers = source.workers.map((entry) => ({
|
|
11578
11691
|
...entry,
|
|
11579
11692
|
id: fresh(entry.id),
|
|
@@ -11616,19 +11729,21 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
11616
11729
|
closure: null,
|
|
11617
11730
|
assessments,
|
|
11618
11731
|
evidence,
|
|
11619
|
-
decisions: decisions.map((entry) =>
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
...entry
|
|
11623
|
-
|
|
11624
|
-
...
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
|
|
11631
|
-
|
|
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
|
+
}),
|
|
11632
11747
|
...actionProgress ? { actionProgress } : {},
|
|
11633
11748
|
findings,
|
|
11634
11749
|
workers,
|
|
@@ -11711,7 +11826,7 @@ class WorkitCore {
|
|
|
11711
11826
|
activeWorkerBlocker(task, workspace) {
|
|
11712
11827
|
if (workspace.writer)
|
|
11713
11828
|
return failure("recovery_required", "writer ownership must be released first");
|
|
11714
|
-
if (task.workers.some((entry) =>
|
|
11829
|
+
if (task.workers.some((entry) => workerBlocksTransition("close", entry.data.state)))
|
|
11715
11830
|
return failure("recovery_required", "worker state requires reconciliation");
|
|
11716
11831
|
return null;
|
|
11717
11832
|
}
|
|
@@ -11957,37 +12072,33 @@ class WorkitCore {
|
|
|
11957
12072
|
const currentCandidate = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
11958
12073
|
if (!currentCandidate.ok)
|
|
11959
12074
|
return currentCandidate;
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
evidence.candidateId ??= currentCandidate.data.id;
|
|
11963
|
-
} else {
|
|
11964
|
-
evidence.beforeCandidateId ??= null;
|
|
11965
|
-
evidence.candidateId ??= null;
|
|
11966
|
-
}
|
|
12075
|
+
const pin = helper && helper.ok ? helper.data.data.assignment.candidateId : null;
|
|
12076
|
+
const bound = resolveBinding(evidence, pin, currentCandidate.data.id);
|
|
11967
12077
|
if (helper?.ok) {
|
|
11968
12078
|
const assignment = helper.data.data.assignment;
|
|
11969
|
-
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))
|
|
11970
12080
|
return failure("permission_denied", "evidence is outside the worker assignment");
|
|
11971
12081
|
}
|
|
11972
12082
|
const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
|
|
11973
|
-
for (const candidateId of [
|
|
12083
|
+
for (const candidateId of [bound.beforeCandidateId, bound.candidateId]) {
|
|
11974
12084
|
if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
|
|
11975
12085
|
return failure("invalid_input", "evidence candidate is not a captured candidate");
|
|
11976
12086
|
}
|
|
12087
|
+
const boundEvidence = { ...evidence, ...bound };
|
|
11977
12088
|
const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
|
|
11978
12089
|
const candidates = current.candidates.some((item) => item.id === currentCandidate.data.id) ? current.candidates : [...current.candidates, currentCandidate.data];
|
|
11979
12090
|
const entry = {
|
|
11980
12091
|
id: newId(),
|
|
11981
12092
|
recordedAt: mutation.now,
|
|
11982
12093
|
provenance: provenance(this.context, "agent_reported"),
|
|
11983
|
-
data:
|
|
12094
|
+
data: boundEvidence
|
|
11984
12095
|
};
|
|
11985
12096
|
return success(mutation.revision, null, {
|
|
11986
12097
|
...current,
|
|
11987
12098
|
candidates,
|
|
11988
12099
|
evidence: [...current.evidence, entry],
|
|
11989
12100
|
findings: current.findings.map((finding) => {
|
|
11990
|
-
if (finding.data.disposition === "open" ||
|
|
12101
|
+
if (finding.data.disposition === "open" || boundEvidence.result === "skipped")
|
|
11991
12102
|
return finding;
|
|
11992
12103
|
if (finding.data.disposition !== "fixed")
|
|
11993
12104
|
return finding;
|
|
@@ -11996,8 +12107,12 @@ class WorkitCore {
|
|
|
11996
12107
|
item.evidenceId,
|
|
11997
12108
|
item.status
|
|
11998
12109
|
]));
|
|
11999
|
-
const verified = withEntry.evidence.some((item) =>
|
|
12000
|
-
|
|
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)));
|
|
12001
12116
|
return verified && !contradicted ? finding : { ...finding, data: { ...finding.data, disposition: "open", resolution: null } };
|
|
12002
12117
|
})
|
|
12003
12118
|
});
|
|
@@ -12022,7 +12137,12 @@ class WorkitCore {
|
|
|
12022
12137
|
const input = parsed.data;
|
|
12023
12138
|
if (nativeRequired && input.action !== "record")
|
|
12024
12139
|
return failure("permission_denied", "native observation is only valid for recording decisions");
|
|
12025
|
-
|
|
12140
|
+
const stated = input.action === "record" && input.response === "stated";
|
|
12141
|
+
if (stated && input.purpose === "action")
|
|
12142
|
+
return failure("permission_denied", "stated choices cannot authorize mutating actions");
|
|
12143
|
+
if (stated && !input.binding?.statedChoice)
|
|
12144
|
+
return failure("invalid_input", "stated choices require binding.statedChoice");
|
|
12145
|
+
if (nativeRequired && nativeObservation === undefined && !stated)
|
|
12026
12146
|
return failure("permission_denied", "native decision observation is required");
|
|
12027
12147
|
const task = this.store.readTask(input.taskId);
|
|
12028
12148
|
if (!task.ok)
|
|
@@ -12055,7 +12175,7 @@ class WorkitCore {
|
|
|
12055
12175
|
consumption: null
|
|
12056
12176
|
};
|
|
12057
12177
|
const data = { ...base, digest: decisionDigest(base) };
|
|
12058
|
-
const native = nativeRequired ? verifyNativeDecision(this.context.nativeAuthority, {
|
|
12178
|
+
const native = nativeRequired && !stated ? verifyNativeDecision(this.context.nativeAuthority, {
|
|
12059
12179
|
observation: nativeObservation,
|
|
12060
12180
|
expected: {
|
|
12061
12181
|
taskId: task.data.id,
|
|
@@ -12150,7 +12270,7 @@ class WorkitCore {
|
|
|
12150
12270
|
if (input.action === "record") {
|
|
12151
12271
|
if (helper?.ok) {
|
|
12152
12272
|
const assignment = helper.data.data.assignment;
|
|
12153
|
-
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))
|
|
12154
12274
|
return failure("permission_denied", "finding is outside the worker assignment");
|
|
12155
12275
|
}
|
|
12156
12276
|
if (input.candidateId && !task.data.candidates.some((candidate) => candidate.id === input.candidateId))
|
|
@@ -12197,7 +12317,11 @@ class WorkitCore {
|
|
|
12197
12317
|
const evaluations = evaluateEvidence(task.data, current.data);
|
|
12198
12318
|
const verified = evidence.some((entry) => {
|
|
12199
12319
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
12200
|
-
return
|
|
12320
|
+
return findingVerificationPasses(finding.data.candidateId, {
|
|
12321
|
+
kind: entry.data.kind,
|
|
12322
|
+
candidateId: entry.data.candidateId,
|
|
12323
|
+
status: evaluation?.status ?? "missing"
|
|
12324
|
+
});
|
|
12201
12325
|
});
|
|
12202
12326
|
if (!verified)
|
|
12203
12327
|
return failure("permission_denied", "fixed findings require passing verification evidence");
|
|
@@ -12209,7 +12333,7 @@ class WorkitCore {
|
|
|
12209
12333
|
if (entry.data.result === "missing" || entry.data.result === "skipped")
|
|
12210
12334
|
return false;
|
|
12211
12335
|
const candidateMatches = finding.data.candidateId === null ? entry.data.candidateId === null : entry.data.candidateId === finding.data.candidateId || entry.data.beforeCandidateId === finding.data.candidateId;
|
|
12212
|
-
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)));
|
|
12213
12337
|
return candidateMatches && (referenceMatches || entry.data.claim === finding.data.claim);
|
|
12214
12338
|
});
|
|
12215
12339
|
if (!relevant)
|
|
@@ -12287,7 +12411,7 @@ class WorkitCore {
|
|
|
12287
12411
|
const workerId = newId();
|
|
12288
12412
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12289
12413
|
taskId: task.data.id,
|
|
12290
|
-
|
|
12414
|
+
expectedRevision: input.expectedRevision,
|
|
12291
12415
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12292
12416
|
now: trustedNow(this.context),
|
|
12293
12417
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12325,7 +12449,7 @@ class WorkitCore {
|
|
|
12325
12449
|
return failure("permission_denied", "worker report is outside the worker assignment");
|
|
12326
12450
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12327
12451
|
taskId: task.data.id,
|
|
12328
|
-
|
|
12452
|
+
expectedRevision: input.expectedRevision,
|
|
12329
12453
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12330
12454
|
now: trustedNow(this.context),
|
|
12331
12455
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12349,24 +12473,25 @@ class WorkitCore {
|
|
|
12349
12473
|
return failure("invalid_transition", "closed task cannot cancel workers");
|
|
12350
12474
|
if (entry.data.state === "unknown")
|
|
12351
12475
|
return failure("recovery_required", "worker state requires recovery");
|
|
12352
|
-
if (entry.data.state === "stopped")
|
|
12353
|
-
|
|
12354
|
-
|
|
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
|
+
}
|
|
12355
12480
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12356
12481
|
taskId: task.data.id,
|
|
12357
|
-
|
|
12482
|
+
expectedRevision: input.expectedRevision,
|
|
12358
12483
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12359
12484
|
now: trustedNow(this.context),
|
|
12360
|
-
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
|
+
}),
|
|
12361
12489
|
task: (current, mutation) => success(mutation.revision, null, {
|
|
12362
12490
|
...current,
|
|
12363
12491
|
workers: current.workers.map((candidate) => candidate.id === input.workerId ? {
|
|
12364
12492
|
...candidate,
|
|
12365
12493
|
recordedAt: mutation.now,
|
|
12366
|
-
data: {
|
|
12367
|
-
...candidate.data,
|
|
12368
|
-
state: settles ? "stopped" : "cancelling"
|
|
12369
|
-
}
|
|
12494
|
+
data: { ...candidate.data, state: "stopped" }
|
|
12370
12495
|
} : candidate)
|
|
12371
12496
|
})
|
|
12372
12497
|
});
|
|
@@ -12463,7 +12588,7 @@ class WorkitCore {
|
|
|
12463
12588
|
return attested;
|
|
12464
12589
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12465
12590
|
taskId: task.data.id,
|
|
12466
|
-
|
|
12591
|
+
expectedRevision: input.expectedRevision,
|
|
12467
12592
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12468
12593
|
now: trustedNow(this.context),
|
|
12469
12594
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
|
|
@@ -12629,6 +12754,12 @@ class WorkitCore {
|
|
|
12629
12754
|
if (!sameSession2(helper.data.data.session, this.context))
|
|
12630
12755
|
return failure("permission_denied", "worker session does not match the caller");
|
|
12631
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
|
+
}
|
|
12632
12763
|
const owner = {
|
|
12633
12764
|
taskId: task.data.id,
|
|
12634
12765
|
workerId: helperId,
|
|
@@ -12640,7 +12771,7 @@ class WorkitCore {
|
|
|
12640
12771
|
};
|
|
12641
12772
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12642
12773
|
taskId: task.data.id,
|
|
12643
|
-
|
|
12774
|
+
expectedRevision: input.expectedRevision,
|
|
12644
12775
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12645
12776
|
now: trustedNow(this.context),
|
|
12646
12777
|
workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
|
|
@@ -12671,7 +12802,7 @@ class WorkitCore {
|
|
|
12671
12802
|
}
|
|
12672
12803
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
12673
12804
|
taskId: task.data.id,
|
|
12674
|
-
|
|
12805
|
+
expectedRevision: input.expectedRevision,
|
|
12675
12806
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
12676
12807
|
now: trustedNow(this.context),
|
|
12677
12808
|
workspace: (currentWorkspace, mutation) => success(mutation.revision, mutation.revision, { ...currentWorkspace, writer: null }),
|
|
@@ -12920,7 +13051,15 @@ class WorkitCore {
|
|
|
12920
13051
|
if (seen.has(observation.workerId))
|
|
12921
13052
|
return failure("invalid_input", "worker observations must be unique");
|
|
12922
13053
|
seen.add(observation.workerId);
|
|
12923
|
-
|
|
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)
|
|
12924
13063
|
return failure("revision_conflict", "worker observation is stale");
|
|
12925
13064
|
if (!view.task.workers.some((entry) => entry.id === observation.workerId))
|
|
12926
13065
|
return failure("permission_denied", "worker observation is not assigned");
|
|
@@ -13006,19 +13145,26 @@ class WorkitCore {
|
|
|
13006
13145
|
return failure("permission_denied", "imported resume requires native destination approval");
|
|
13007
13146
|
resumeCandidate = current.data.candidate;
|
|
13008
13147
|
}
|
|
13009
|
-
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);
|
|
13010
13149
|
if (blocker)
|
|
13011
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
|
+
}
|
|
13012
13158
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
13013
13159
|
taskId: task.data.id,
|
|
13014
|
-
|
|
13160
|
+
expectedRevision: input.expectedRevision,
|
|
13015
13161
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
13016
13162
|
now: trustedNow(this.context),
|
|
13017
13163
|
workspace: (workspace, context) => success(context.revision, context.revision, workspace),
|
|
13018
13164
|
task: (current, context) => success(context.revision, null, {
|
|
13019
13165
|
...current,
|
|
13020
13166
|
status,
|
|
13021
|
-
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,
|
|
13022
13168
|
progress: current.progress,
|
|
13023
13169
|
pauseReason: status === "paused" ? input.reason ?? null : null
|
|
13024
13170
|
})
|
|
@@ -13056,12 +13202,13 @@ class WorkitCore {
|
|
|
13056
13202
|
return workspace;
|
|
13057
13203
|
if (!workspace.data)
|
|
13058
13204
|
return failure("not_found", "workspace not found");
|
|
13059
|
-
|
|
13060
|
-
|
|
13205
|
+
const revisionBlocker = this.activeWorkerBlocker(task.data, workspace.data);
|
|
13206
|
+
if (revisionBlocker)
|
|
13207
|
+
return revisionBlocker;
|
|
13061
13208
|
this.fillRevisions(input, task.data, workspace.data);
|
|
13062
13209
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
13063
13210
|
taskId: task.data.id,
|
|
13064
|
-
|
|
13211
|
+
expectedRevision: input.expectedRevision,
|
|
13065
13212
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
13066
13213
|
now: trustedNow(this.context),
|
|
13067
13214
|
workspace: (workspace, context) => success(context.revision, context.revision, workspace),
|
|
@@ -13117,7 +13264,7 @@ class WorkitCore {
|
|
|
13117
13264
|
return closure;
|
|
13118
13265
|
const changed = this.store.mutateTaskAndWorkspace({
|
|
13119
13266
|
taskId: task.data.id,
|
|
13120
|
-
|
|
13267
|
+
expectedRevision: input.expectedRevision,
|
|
13121
13268
|
expectedWorkspaceRevision: input.expectedWorkspaceRevision,
|
|
13122
13269
|
now: trustedNow(this.context),
|
|
13123
13270
|
workspace: (value, context) => success(context.revision, context.revision, value),
|
|
@@ -13386,7 +13533,7 @@ var handleCodexHook = (raw) => {
|
|
|
13386
13533
|
if (input.hook_event_name === "PreToolUse") {
|
|
13387
13534
|
if (["bash", "unified-exec"].includes(String(input.tool_name).toLowerCase())) {
|
|
13388
13535
|
const command = record2(input.tool_input) ? input.tool_input.command : undefined;
|
|
13389
|
-
const route = typeof command === "string" ?
|
|
13536
|
+
const route = typeof command === "string" ? shouldDenyShellRoute(input.cwd, command) : null;
|
|
13390
13537
|
if (route)
|
|
13391
13538
|
return denied("PreToolUse", `direct branch or PR creation bypasses the Workit route; ${route.guidance}`);
|
|
13392
13539
|
}
|