@brainervirus/workit-cursor 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.
@@ -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 expectedTaskRevision = input.expectedTaskRevision ?? input.expectedRevision;
9009
- if (!expectedTaskRevision)
9091
+ const expected = input.expectedRevision;
9092
+ if (!expected)
9010
9093
  return failure("invalid_input", "task revision is required");
9011
- if (task.data.revision !== expectedTaskRevision)
9012
- return this.conflict(expectedTaskRevision, task.data.revision);
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
- return Array.isArray(steps) && steps.every((step) => typeof step === "string") ? steps : null;
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 string array");
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 = task.evidence.some((other) => other.id !== entry.id && other.data.kind === "review" && sameSession(reviewSession, other.provenance.session));
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 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)) : passed;
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
- return evidenceById.get(id) === "passed" && (evidence?.data.kind === "check" || evidence?.data.kind === "review");
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) => item.status === "unsatisfied" || item.status === "unavailable");
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": { ruleIds: ["root-cause-investigation"] },
10548
- "workit-handoff": { ruleIds: ["durable-handoff"] },
10712
+ "workit-debug": {},
10713
+ "workit-handoff": {},
10549
10714
  "workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
10550
10715
  };
10551
- var methodMatches = (id, definition, requirement) => definition.dimensions?.includes(requirement.dimension) === true && !(id === "workit-review" && requirement.ruleId === "self-review") || definition.ruleIds?.includes(requirement.ruleId) === true;
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(id, definition, requirement));
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);
@@ -10737,6 +10902,27 @@ var exportDigest = (bundle) => sha256({
10737
10902
  sourceWorkspaceId: bundle.sourceWorkspaceId,
10738
10903
  task: bundle.task
10739
10904
  });
10905
+ // packages/workit-core/src/core/config.ts
10906
+ var PRESETS = {
10907
+ gitflow: {
10908
+ allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
10909
+ protected: ["main", "develop", "master", "prod", "production"]
10910
+ },
10911
+ "github-flow": { allowed: ["*"], protected: ["main"] },
10912
+ "trunk-based": { allowed: ["*"], protected: ["main"] },
10913
+ custom: { allowed: [], protected: [] }
10914
+ };
10915
+ var DEFAULTS = {
10916
+ locale: "en",
10917
+ localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
10918
+ timezone: "America/Santiago",
10919
+ branchPolicy: {
10920
+ preset: "gitflow",
10921
+ allowed: [...PRESETS.gitflow.allowed],
10922
+ protected: [...PRESETS.gitflow.protected]
10923
+ },
10924
+ commitPolicy: { preset: "conventional" }
10925
+ };
10740
10926
  // packages/workit-core/src/core/external-action.ts
10741
10927
  var externalActionSchema = discriminatedUnion("operation", [
10742
10928
  object({
@@ -11274,6 +11460,8 @@ var applyWorkerLifecycle = (input) => {
11274
11460
  if (task.data.status === "paused" && (input.state === "running" || input.state === "cancelling"))
11275
11461
  return failure("invalid_transition", "paused task cannot run a worker");
11276
11462
  const notStarted = input.dispatch === true;
11463
+ if (notStarted && entry.data.state === "stopped" && input.session === null)
11464
+ return success(task.data.revision, workspace.data.revision, entry);
11277
11465
  if (notStarted && (input.state !== "stopped" || input.session !== null))
11278
11466
  return failure("invalid_input", "a never-dispatched worker stops with no session");
11279
11467
  if (notStarted && entry.data.session !== null)
@@ -11305,7 +11493,7 @@ var applyWorkerLifecycle = (input) => {
11305
11493
  return success(task.data.revision, workspace.data.revision, entry);
11306
11494
  const changed = input.store.mutateTaskAndWorkspace({
11307
11495
  taskId: task.data.id,
11308
- expectedTaskRevision: input.expectedRevision,
11496
+ expectedRevision: input.expectedRevision,
11309
11497
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
11310
11498
  now: input.now,
11311
11499
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
@@ -11353,90 +11541,22 @@ var importedProvenance = (context) => ({
11353
11541
  receipts: []
11354
11542
  });
11355
11543
  var portableTask = (task) => {
11356
- const clone = structuredClone(task);
11357
- const portableProvenance = (value) => ({
11358
- ...value,
11359
- session: null,
11360
- receipts: []
11361
- });
11362
- const entries = [
11363
- clone.intent,
11364
- ...clone.assessments,
11365
- ...clone.evidence,
11366
- ...clone.decisions,
11367
- ...clone.findings,
11368
- ...clone.workers
11369
- ];
11370
- for (const entry of entries)
11371
- entry.provenance = portableProvenance(entry.provenance);
11372
- for (const worker of clone.workers)
11373
- worker.data.session = null;
11374
- clone.intent = {
11375
- ...clone.intent,
11376
- data: { ...clone.intent.data, authorityRefs: portableRefs(clone.intent.data.authorityRefs) }
11544
+ const stripped = rewriteRecordRefs(task, taskRecordSchema, portableRef);
11545
+ if (typeof stripped !== "object" || stripped === null || Array.isArray(stripped))
11546
+ throw new TypeError("portable task rewrite produced a non-record");
11547
+ const clone = structuredClone(stripped);
11548
+ const demote = (facts, signals, consequences) => {
11549
+ for (const fact of facts)
11550
+ Object.assign(fact, portableFact(fact));
11551
+ for (const name of Object.keys(signals)) {
11552
+ const signal = signals[name];
11553
+ Object.assign(signal, portableSignal(signal));
11554
+ }
11555
+ for (const consequence of consequences)
11556
+ Object.assign(consequence.fact, portableFact(consequence.fact));
11377
11557
  };
11378
- clone.constraints = clone.constraints.flatMap((constraint) => {
11379
- const source = portableRef(constraint.source);
11380
- return source ? [
11381
- {
11382
- ...constraint,
11383
- source,
11384
- requires: constraint.requires.map((requirement) => ({
11385
- ...requirement,
11386
- refs: portableRefs(requirement.refs)
11387
- }))
11388
- }
11389
- ] : [];
11390
- });
11391
- clone.assessments = clone.assessments.map((entry) => ({
11392
- ...entry,
11393
- data: {
11394
- ...entry.data,
11395
- facts: entry.data.facts.map(portableFact),
11396
- signals: Object.fromEntries(Object.entries(entry.data.signals).map(([name, signal]) => [name, portableSignal(signal)])),
11397
- consequences: entry.data.consequences.map((consequence) => ({
11398
- ...consequence,
11399
- fact: portableFact(consequence.fact)
11400
- })),
11401
- verification: entry.data.verification.map((verification) => ({
11402
- ...verification,
11403
- availableChecks: portableRefs(verification.availableChecks)
11404
- }))
11405
- }
11406
- }));
11407
- clone.progress = {
11408
- ...clone.progress,
11409
- blockers: clone.progress.blockers.map((blocker) => ({
11410
- ...blocker,
11411
- refs: portableRefs(blocker.refs)
11412
- }))
11413
- };
11414
- clone.evidence = clone.evidence.map((entry) => ({
11415
- ...entry,
11416
- data: {
11417
- ...entry.data,
11418
- refs: portableRefs(entry.data.refs),
11419
- reviewContext: portableRef(entry.data.reviewContext)
11420
- }
11421
- }));
11422
- clone.decisions = clone.decisions.map((entry) => ({
11423
- ...entry,
11424
- data: {
11425
- ...entry.data,
11426
- consumption: entry.data.consumption ? portableRef(entry.data.consumption.actionRef) ? {
11427
- ...entry.data.consumption,
11428
- actionRef: portableRef(entry.data.consumption.actionRef)
11429
- } : null : null,
11430
- binding: {
11431
- ...entry.data.binding,
11432
- contentRefs: portableRefs(entry.data.binding.contentRefs)
11433
- }
11434
- }
11435
- }));
11436
- clone.findings = clone.findings.map((entry) => ({
11437
- ...entry,
11438
- data: { ...entry.data, refs: portableRefs(entry.data.refs) }
11439
- }));
11558
+ for (const entry of clone.assessments)
11559
+ demote(entry.data.facts, entry.data.signals, entry.data.consequences);
11440
11560
  clone.candidates = [];
11441
11561
  return clone;
11442
11562
  };
@@ -11461,15 +11581,13 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11461
11581
  allocate(entry.id);
11462
11582
  const fresh = (id) => allocate(id);
11463
11583
  const provenance = importedProvenance(context);
11584
+ const remap = (ref) => mapRef(ref, ids);
11464
11585
  const intent = {
11465
11586
  ...source.intent,
11466
11587
  id: fresh(source.intent.id),
11467
11588
  recordedAt: timestamp,
11468
11589
  provenance,
11469
- data: {
11470
- ...source.intent.data,
11471
- authorityRefs: source.intent.data.authorityRefs.map((ref) => mapRef(ref, ids))
11472
- }
11590
+ data: rewriteRecordRefs(source.intent.data, intentSchema, remap)
11473
11591
  };
11474
11592
  const assessments = source.assessments.map((entry) => {
11475
11593
  const data = entry.data;
@@ -11478,28 +11596,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11478
11596
  id: fresh(entry.id),
11479
11597
  recordedAt: timestamp,
11480
11598
  provenance,
11481
- data: {
11482
- ...data,
11483
- facts: data.facts.map((fact) => ({
11484
- ...fact,
11485
- refs: fact.refs.map((ref) => mapRef(ref, ids))
11486
- })),
11487
- signals: Object.fromEntries(Object.entries(data.signals).map(([name, signal]) => [
11488
- name,
11489
- { ...signal, refs: signal.refs.map((ref) => mapRef(ref, ids)) }
11490
- ])),
11491
- consequences: data.consequences.map((consequence) => ({
11492
- ...consequence,
11493
- fact: {
11494
- ...consequence.fact,
11495
- refs: consequence.fact.refs.map((ref) => mapRef(ref, ids))
11496
- }
11497
- })),
11498
- verification: data.verification.map((verification) => ({
11499
- ...verification,
11500
- availableChecks: verification.availableChecks.map((ref) => mapRef(ref, ids))
11501
- }))
11502
- }
11599
+ data: rewriteRecordRefs(data, assessmentSchema, remap)
11503
11600
  };
11504
11601
  });
11505
11602
  const evidence = source.evidence.map((entry) => ({
@@ -11507,11 +11604,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11507
11604
  id: fresh(entry.id),
11508
11605
  recordedAt: timestamp,
11509
11606
  provenance,
11510
- data: {
11511
- ...entry.data,
11512
- refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
11513
- reviewContext: entry.data.reviewContext ? mapRef(entry.data.reviewContext, ids) : null
11514
- }
11607
+ data: rewriteRecordRefs(entry.data, evidenceSchema, remap)
11515
11608
  }));
11516
11609
  const decisions = source.decisions.map((entry) => ({
11517
11610
  ...entry,
@@ -11519,21 +11612,23 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11519
11612
  recordedAt: timestamp,
11520
11613
  provenance
11521
11614
  }));
11522
- const findings = source.findings.map((entry) => ({
11523
- ...entry,
11524
- id: fresh(entry.id),
11525
- recordedAt: timestamp,
11526
- provenance,
11527
- data: {
11528
- ...entry.data,
11529
- refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
11530
- resolution: entry.data.resolution ? {
11531
- ...entry.data.resolution,
11532
- evidenceIds: entry.data.resolution.evidenceIds.map((id) => ids.get(id) ?? id),
11533
- decisionIds: entry.data.resolution.decisionIds.map((id) => ids.get(id) ?? id)
11534
- } : null
11535
- }
11536
- }));
11615
+ const findings = source.findings.map((entry) => {
11616
+ const data = rewriteRecordRefs(entry.data, findingSchema, remap);
11617
+ return {
11618
+ ...entry,
11619
+ id: fresh(entry.id),
11620
+ recordedAt: timestamp,
11621
+ provenance,
11622
+ data: {
11623
+ ...data,
11624
+ resolution: data.resolution ? {
11625
+ ...data.resolution,
11626
+ evidenceIds: data.resolution.evidenceIds.map((id) => ids.get(id) ?? id),
11627
+ decisionIds: data.resolution.decisionIds.map((id) => ids.get(id) ?? id)
11628
+ } : null
11629
+ }
11630
+ };
11631
+ });
11537
11632
  const workers = source.workers.map((entry) => ({
11538
11633
  ...entry,
11539
11634
  id: fresh(entry.id),
@@ -11576,19 +11671,21 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11576
11671
  closure: null,
11577
11672
  assessments,
11578
11673
  evidence,
11579
- decisions: decisions.map((entry) => ({
11580
- ...entry,
11581
- data: {
11582
- ...entry.data,
11583
- binding: {
11584
- ...entry.data.binding,
11585
- taskId: "",
11586
- workspaceId: destinationWorkspaceId,
11587
- contentRefs: entry.data.binding.contentRefs.map((ref) => mapRef(ref, ids))
11588
- },
11589
- consumption: null
11590
- }
11591
- })),
11674
+ decisions: decisions.map((entry) => {
11675
+ const data = rewriteRecordRefs(entry.data, decisionSchema, remap);
11676
+ return {
11677
+ ...entry,
11678
+ data: {
11679
+ ...data,
11680
+ binding: {
11681
+ ...data.binding,
11682
+ taskId: "",
11683
+ workspaceId: destinationWorkspaceId
11684
+ },
11685
+ consumption: null
11686
+ }
11687
+ };
11688
+ }),
11592
11689
  ...actionProgress ? { actionProgress } : {},
11593
11690
  findings,
11594
11691
  workers,
@@ -11671,7 +11768,7 @@ class WorkitCore {
11671
11768
  activeWorkerBlocker(task, workspace) {
11672
11769
  if (workspace.writer)
11673
11770
  return failure("recovery_required", "writer ownership must be released first");
11674
- if (task.workers.some((entry) => isUncertainWorker(entry.data.state)))
11771
+ if (task.workers.some((entry) => workerBlocksTransition("close", entry.data.state)))
11675
11772
  return failure("recovery_required", "worker state requires reconciliation");
11676
11773
  return null;
11677
11774
  }
@@ -11917,37 +12014,33 @@ class WorkitCore {
11917
12014
  const currentCandidate = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
11918
12015
  if (!currentCandidate.ok)
11919
12016
  return currentCandidate;
11920
- if (evidence.kind === "check" || evidence.kind === "review") {
11921
- evidence.beforeCandidateId ??= currentCandidate.data.id;
11922
- evidence.candidateId ??= currentCandidate.data.id;
11923
- } else {
11924
- evidence.beforeCandidateId ??= null;
11925
- evidence.candidateId ??= null;
11926
- }
12017
+ const pin = helper && helper.ok ? helper.data.data.assignment.candidateId : null;
12018
+ const bound = resolveBinding(evidence, pin, currentCandidate.data.id);
11927
12019
  if (helper?.ok) {
11928
12020
  const assignment = helper.data.data.assignment;
11929
- if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || assignment.candidateId !== null && evidence.candidateId !== assignment.candidateId && evidence.beforeCandidateId !== assignment.candidateId || !refsWithinScope(evidence.refs, assignment.scope) || !refsWithinScope(evidence.refs, task.data.intent.data.scope))
12021
+ 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))
11930
12022
  return failure("permission_denied", "evidence is outside the worker assignment");
11931
12023
  }
11932
12024
  const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
11933
- for (const candidateId of [evidence.beforeCandidateId, evidence.candidateId]) {
12025
+ for (const candidateId of [bound.beforeCandidateId, bound.candidateId]) {
11934
12026
  if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
11935
12027
  return failure("invalid_input", "evidence candidate is not a captured candidate");
11936
12028
  }
12029
+ const boundEvidence = { ...evidence, ...bound };
11937
12030
  const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
11938
12031
  const candidates = current.candidates.some((item) => item.id === currentCandidate.data.id) ? current.candidates : [...current.candidates, currentCandidate.data];
11939
12032
  const entry = {
11940
12033
  id: newId(),
11941
12034
  recordedAt: mutation.now,
11942
12035
  provenance: provenance(this.context, "agent_reported"),
11943
- data: evidence
12036
+ data: boundEvidence
11944
12037
  };
11945
12038
  return success(mutation.revision, null, {
11946
12039
  ...current,
11947
12040
  candidates,
11948
12041
  evidence: [...current.evidence, entry],
11949
12042
  findings: current.findings.map((finding) => {
11950
- if (finding.data.disposition === "open" || evidence.result === "skipped")
12043
+ if (finding.data.disposition === "open" || boundEvidence.result === "skipped")
11951
12044
  return finding;
11952
12045
  if (finding.data.disposition !== "fixed")
11953
12046
  return finding;
@@ -11956,8 +12049,12 @@ class WorkitCore {
11956
12049
  item.evidenceId,
11957
12050
  item.status
11958
12051
  ]));
11959
- const verified = withEntry.evidence.some((item) => statuses.get(item.id) === "passed" && (item.data.kind === "check" || item.data.kind === "review") && (finding.data.candidateId === null || item.data.candidateId === finding.data.candidateId));
11960
- const contradicted = evidence.result === "failed" && finding.data.refs.some((left) => evidence.refs.some((right) => JSON.stringify(left) === JSON.stringify(right)));
12052
+ const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
12053
+ kind: item.data.kind,
12054
+ candidateId: item.data.candidateId,
12055
+ status: statuses.get(item.id) ?? "missing"
12056
+ }));
12057
+ const contradicted = boundEvidence.result === "failed" && finding.data.refs.some((left) => boundEvidence.refs.some((right) => sameValue(left, right)));
11961
12058
  return verified && !contradicted ? finding : { ...finding, data: { ...finding.data, disposition: "open", resolution: null } };
11962
12059
  })
11963
12060
  });
@@ -12115,7 +12212,7 @@ class WorkitCore {
12115
12212
  if (input.action === "record") {
12116
12213
  if (helper?.ok) {
12117
12214
  const assignment = helper.data.data.assignment;
12118
- if (!bindingCovers(assignment.scope, input.scope) || !bindingCovers(task.data.intent.data.scope, input.scope) || assignment.candidateId !== null && input.candidateId !== assignment.candidateId || !refsWithinScope(input.refs, assignment.scope) || !refsWithinScope(input.refs, task.data.intent.data.scope))
12215
+ 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))
12119
12216
  return failure("permission_denied", "finding is outside the worker assignment");
12120
12217
  }
12121
12218
  if (input.candidateId && !task.data.candidates.some((candidate) => candidate.id === input.candidateId))
@@ -12162,7 +12259,11 @@ class WorkitCore {
12162
12259
  const evaluations = evaluateEvidence(task.data, current.data);
12163
12260
  const verified = evidence.some((entry) => {
12164
12261
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
12165
- return evaluation?.status === "passed" && (entry.data.kind === "check" || entry.data.kind === "review") && (finding.data.candidateId === null || entry.data.candidateId === finding.data.candidateId);
12262
+ return findingVerificationPasses(finding.data.candidateId, {
12263
+ kind: entry.data.kind,
12264
+ candidateId: entry.data.candidateId,
12265
+ status: evaluation?.status ?? "missing"
12266
+ });
12166
12267
  });
12167
12268
  if (!verified)
12168
12269
  return failure("permission_denied", "fixed findings require passing verification evidence");
@@ -12174,7 +12275,7 @@ class WorkitCore {
12174
12275
  if (entry.data.result === "missing" || entry.data.result === "skipped")
12175
12276
  return false;
12176
12277
  const candidateMatches = finding.data.candidateId === null ? entry.data.candidateId === null : entry.data.candidateId === finding.data.candidateId || entry.data.beforeCandidateId === finding.data.candidateId;
12177
- const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) => JSON.stringify(left) === JSON.stringify(right)));
12278
+ const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) => sameValue(left, right)));
12178
12279
  return candidateMatches && (referenceMatches || entry.data.claim === finding.data.claim);
12179
12280
  });
12180
12281
  if (!relevant)
@@ -12252,7 +12353,7 @@ class WorkitCore {
12252
12353
  const workerId = newId();
12253
12354
  const changed = this.store.mutateTaskAndWorkspace({
12254
12355
  taskId: task.data.id,
12255
- expectedTaskRevision: input.expectedRevision,
12356
+ expectedRevision: input.expectedRevision,
12256
12357
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12257
12358
  now: trustedNow(this.context),
12258
12359
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -12290,7 +12391,7 @@ class WorkitCore {
12290
12391
  return failure("permission_denied", "worker report is outside the worker assignment");
12291
12392
  const changed = this.store.mutateTaskAndWorkspace({
12292
12393
  taskId: task.data.id,
12293
- expectedTaskRevision: input.expectedRevision,
12394
+ expectedRevision: input.expectedRevision,
12294
12395
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12295
12396
  now: trustedNow(this.context),
12296
12397
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -12314,24 +12415,25 @@ class WorkitCore {
12314
12415
  return failure("invalid_transition", "closed task cannot cancel workers");
12315
12416
  if (entry.data.state === "unknown")
12316
12417
  return failure("recovery_required", "worker state requires recovery");
12317
- if (entry.data.state === "stopped")
12318
- return failure("invalid_transition", "stopped worker cannot be cancelled");
12319
- const settles = entry.data.report !== null || entry.data.state === "cancelling";
12418
+ if (entry.data.state === "stopped") {
12419
+ const stopped = task.data.workers.find((candidate) => candidate.id === input.workerId);
12420
+ return success(task.data.revision, workspace.data.revision, stopped);
12421
+ }
12320
12422
  const changed = this.store.mutateTaskAndWorkspace({
12321
12423
  taskId: task.data.id,
12322
- expectedTaskRevision: input.expectedRevision,
12424
+ expectedRevision: input.expectedRevision,
12323
12425
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12324
12426
  now: trustedNow(this.context),
12325
- workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
12427
+ workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
12428
+ ...current,
12429
+ writer: current.writer?.owner.workerId === input.workerId ? null : current.writer
12430
+ }),
12326
12431
  task: (current, mutation) => success(mutation.revision, null, {
12327
12432
  ...current,
12328
12433
  workers: current.workers.map((candidate) => candidate.id === input.workerId ? {
12329
12434
  ...candidate,
12330
12435
  recordedAt: mutation.now,
12331
- data: {
12332
- ...candidate.data,
12333
- state: settles ? "stopped" : "cancelling"
12334
- }
12436
+ data: { ...candidate.data, state: "stopped" }
12335
12437
  } : candidate)
12336
12438
  })
12337
12439
  });
@@ -12428,7 +12530,7 @@ class WorkitCore {
12428
12530
  return attested;
12429
12531
  const changed = this.store.mutateTaskAndWorkspace({
12430
12532
  taskId: task.data.id,
12431
- expectedTaskRevision: input.expectedRevision,
12533
+ expectedRevision: input.expectedRevision,
12432
12534
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12433
12535
  now: trustedNow(this.context),
12434
12536
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -12594,6 +12696,12 @@ class WorkitCore {
12594
12696
  if (!sameSession2(helper.data.data.session, this.context))
12595
12697
  return failure("permission_denied", "worker session does not match the caller");
12596
12698
  }
12699
+ const gate = this.view(task.data);
12700
+ if (gate.ok) {
12701
+ 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")));
12702
+ if (writeGates.length)
12703
+ 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) });
12704
+ }
12597
12705
  const owner = {
12598
12706
  taskId: task.data.id,
12599
12707
  workerId: helperId,
@@ -12605,7 +12713,7 @@ class WorkitCore {
12605
12713
  };
12606
12714
  const changed = this.store.mutateTaskAndWorkspace({
12607
12715
  taskId: task.data.id,
12608
- expectedTaskRevision: input.expectedRevision,
12716
+ expectedRevision: input.expectedRevision,
12609
12717
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12610
12718
  now: trustedNow(this.context),
12611
12719
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
@@ -12636,7 +12744,7 @@ class WorkitCore {
12636
12744
  }
12637
12745
  const changed = this.store.mutateTaskAndWorkspace({
12638
12746
  taskId: task.data.id,
12639
- expectedTaskRevision: input.expectedRevision,
12747
+ expectedRevision: input.expectedRevision,
12640
12748
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12641
12749
  now: trustedNow(this.context),
12642
12750
  workspace: (currentWorkspace, mutation) => success(mutation.revision, mutation.revision, { ...currentWorkspace, writer: null }),
@@ -12885,7 +12993,15 @@ class WorkitCore {
12885
12993
  if (seen.has(observation.workerId))
12886
12994
  return failure("invalid_input", "worker observations must be unique");
12887
12995
  seen.add(observation.workerId);
12888
- if (observation.taskId !== view.task.id || observation.expectedRevision !== view.task.revision || observation.expectedWorkspaceRevision !== view.workspace.revision)
12996
+ const freshTask = this.store.readTask(view.task.id);
12997
+ const freshWorkspace = this.store.readWorkspace();
12998
+ if (!freshTask.ok)
12999
+ return freshTask;
13000
+ if (!freshWorkspace.ok)
13001
+ return freshWorkspace;
13002
+ if (!freshWorkspace.data)
13003
+ return failure("not_found", "workspace not found");
13004
+ if (observation.taskId !== view.task.id || observation.expectedRevision !== freshTask.data.revision || observation.expectedWorkspaceRevision !== freshWorkspace.data.revision)
12889
13005
  return failure("revision_conflict", "worker observation is stale");
12890
13006
  if (!view.task.workers.some((entry) => entry.id === observation.workerId))
12891
13007
  return failure("permission_denied", "worker observation is not assigned");
@@ -12971,19 +13087,26 @@ class WorkitCore {
12971
13087
  return failure("permission_denied", "imported resume requires native destination approval");
12972
13088
  resumeCandidate = current.data.candidate;
12973
13089
  }
12974
- const blocker = this.activeWorkerBlocker(task.data, workspace.data);
13090
+ const blocker = status === "paused" ? workspace.data.writer ? failure("recovery_required", "writer ownership must be released first") : null : this.activeWorkerBlocker(task.data, workspace.data);
12975
13091
  if (blocker)
12976
13092
  return blocker;
13093
+ let pauseCandidate = null;
13094
+ if (status === "paused") {
13095
+ const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
13096
+ if (!captured.ok)
13097
+ return captured;
13098
+ pauseCandidate = captured.data;
13099
+ }
12977
13100
  const changed = this.store.mutateTaskAndWorkspace({
12978
13101
  taskId: task.data.id,
12979
- expectedTaskRevision: input.expectedRevision,
13102
+ expectedRevision: input.expectedRevision,
12980
13103
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12981
13104
  now: trustedNow(this.context),
12982
13105
  workspace: (workspace, context) => success(context.revision, context.revision, workspace),
12983
13106
  task: (current, context) => success(context.revision, null, {
12984
13107
  ...current,
12985
13108
  status,
12986
- candidates: resumeCandidate && !current.candidates.some((candidate) => candidate.id === resumeCandidate.id) ? [...current.candidates, resumeCandidate] : current.candidates,
13109
+ candidates: (resumeCandidate ?? pauseCandidate) && !current.candidates.some((candidate) => candidate.id === (resumeCandidate ?? pauseCandidate).id) ? [...current.candidates, resumeCandidate ?? pauseCandidate] : current.candidates,
12987
13110
  progress: current.progress,
12988
13111
  pauseReason: status === "paused" ? input.reason ?? null : null
12989
13112
  })
@@ -13021,12 +13144,13 @@ class WorkitCore {
13021
13144
  return workspace;
13022
13145
  if (!workspace.data)
13023
13146
  return failure("not_found", "workspace not found");
13024
- if (workspace.data.writer || task.data.workers.some((entry) => isUncertainWorker(entry.data.state)))
13025
- return failure("recovery_required", "scope revision requires worker ownership reconciliation");
13147
+ const revisionBlocker = this.activeWorkerBlocker(task.data, workspace.data);
13148
+ if (revisionBlocker)
13149
+ return revisionBlocker;
13026
13150
  this.fillRevisions(input, task.data, workspace.data);
13027
13151
  const changed = this.store.mutateTaskAndWorkspace({
13028
13152
  taskId: task.data.id,
13029
- expectedTaskRevision: input.expectedRevision,
13153
+ expectedRevision: input.expectedRevision,
13030
13154
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
13031
13155
  now: trustedNow(this.context),
13032
13156
  workspace: (workspace, context) => success(context.revision, context.revision, workspace),
@@ -13082,7 +13206,7 @@ class WorkitCore {
13082
13206
  return closure;
13083
13207
  const changed = this.store.mutateTaskAndWorkspace({
13084
13208
  taskId: task.data.id,
13085
- expectedTaskRevision: input.expectedRevision,
13209
+ expectedRevision: input.expectedRevision,
13086
13210
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
13087
13211
  now: trustedNow(this.context),
13088
13212
  workspace: (value, context) => success(context.revision, context.revision, value),
@@ -13173,7 +13297,7 @@ var cursorCapabilities = (availability = {}) => {
13173
13297
  }
13174
13298
  ];
13175
13299
  };
13176
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
13300
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
13177
13301
  var eventNames = new Set([
13178
13302
  "sessionStart",
13179
13303
  "preToolUse",
@@ -13184,7 +13308,7 @@ var eventNames = new Set([
13184
13308
  ]);
13185
13309
  var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
13186
13310
  var parseCursorHookInput = (value) => {
13187
- if (!isRecord(value) || !eventNames.has(value.hook_event_name))
13311
+ if (!isRecord2(value) || !eventNames.has(value.hook_event_name))
13188
13312
  return { ok: false, error: "hook_event_name is required" };
13189
13313
  const roots = value.workspace_roots;
13190
13314
  if (!Array.isArray(roots) || roots.length !== 1 || !roots.every(nonEmpty2))
@@ -13331,7 +13455,7 @@ var handleSubagentStop = () => ({});
13331
13455
  var handleCursorHook = (raw) => {
13332
13456
  const parsed = parseCursorHookInput(raw);
13333
13457
  if (!parsed.ok) {
13334
- return isRecord(raw) && raw.hook_event_name === "sessionStart" ? {} : deny(parsed.error);
13458
+ return isRecord2(raw) && raw.hook_event_name === "sessionStart" ? {} : deny(parsed.error);
13335
13459
  }
13336
13460
  const input = parsed.data;
13337
13461
  const root = realpathSync5(input.workspace_roots[0]);
@@ -13376,7 +13500,7 @@ var runCursorHook = async () => {
13376
13500
  const output = handleCursorHook(input);
13377
13501
  process.stdout.write(`${JSON.stringify(output)}
13378
13502
  `);
13379
- const event = isRecord(input) ? input.hook_event_name : undefined;
13503
+ const event = isRecord2(input) ? input.hook_event_name : undefined;
13380
13504
  if (output.permission === "deny" && ["preToolUse", "beforeShellExecution", "subagentStart"].includes(String(event)))
13381
13505
  process.exitCode = 2;
13382
13506
  } catch {