@brainervirus/workit-cursor 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.
@@ -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 expectedTaskRevision = input.expectedTaskRevision ?? input.expectedRevision;
9008
- if (!expectedTaskRevision)
9091
+ const expected = input.expectedRevision;
9092
+ if (!expected)
9009
9093
  return failure("invalid_input", "task revision is required");
9010
- if (task.data.revision !== expectedTaskRevision)
9011
- return this.conflict(expectedTaskRevision, task.data.revision);
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
- 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;
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 string array");
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 = 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));
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 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;
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
- 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
+ });
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) => 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
+ });
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": { ruleIds: ["root-cause-investigation"] },
10547
- "workit-handoff": { ruleIds: ["durable-handoff"] },
10712
+ "workit-debug": {},
10713
+ "workit-handoff": {},
10548
10714
  "workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
10549
10715
  };
10550
- 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;
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(id, definition, requirement));
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);
@@ -10736,6 +10902,27 @@ var exportDigest = (bundle) => sha256({
10736
10902
  sourceWorkspaceId: bundle.sourceWorkspaceId,
10737
10903
  task: bundle.task
10738
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
+ };
10739
10926
  // packages/workit-core/src/core/external-action.ts
10740
10927
  var externalActionSchema = discriminatedUnion("operation", [
10741
10928
  object({
@@ -11273,6 +11460,8 @@ var applyWorkerLifecycle = (input) => {
11273
11460
  if (task.data.status === "paused" && (input.state === "running" || input.state === "cancelling"))
11274
11461
  return failure("invalid_transition", "paused task cannot run a worker");
11275
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);
11276
11465
  if (notStarted && (input.state !== "stopped" || input.session !== null))
11277
11466
  return failure("invalid_input", "a never-dispatched worker stops with no session");
11278
11467
  if (notStarted && entry.data.session !== null)
@@ -11304,7 +11493,7 @@ var applyWorkerLifecycle = (input) => {
11304
11493
  return success(task.data.revision, workspace.data.revision, entry);
11305
11494
  const changed = input.store.mutateTaskAndWorkspace({
11306
11495
  taskId: task.data.id,
11307
- expectedTaskRevision: input.expectedRevision,
11496
+ expectedRevision: input.expectedRevision,
11308
11497
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
11309
11498
  now: input.now,
11310
11499
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
@@ -11352,90 +11541,22 @@ var importedProvenance = (context) => ({
11352
11541
  receipts: []
11353
11542
  });
11354
11543
  var portableTask = (task) => {
11355
- const clone = structuredClone(task);
11356
- const portableProvenance = (value) => ({
11357
- ...value,
11358
- session: null,
11359
- receipts: []
11360
- });
11361
- const entries = [
11362
- clone.intent,
11363
- ...clone.assessments,
11364
- ...clone.evidence,
11365
- ...clone.decisions,
11366
- ...clone.findings,
11367
- ...clone.workers
11368
- ];
11369
- for (const entry of entries)
11370
- entry.provenance = portableProvenance(entry.provenance);
11371
- for (const worker of clone.workers)
11372
- worker.data.session = null;
11373
- clone.intent = {
11374
- ...clone.intent,
11375
- 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));
11376
11557
  };
11377
- clone.constraints = clone.constraints.flatMap((constraint) => {
11378
- const source = portableRef(constraint.source);
11379
- return source ? [
11380
- {
11381
- ...constraint,
11382
- source,
11383
- requires: constraint.requires.map((requirement) => ({
11384
- ...requirement,
11385
- refs: portableRefs(requirement.refs)
11386
- }))
11387
- }
11388
- ] : [];
11389
- });
11390
- clone.assessments = clone.assessments.map((entry) => ({
11391
- ...entry,
11392
- data: {
11393
- ...entry.data,
11394
- facts: entry.data.facts.map(portableFact),
11395
- signals: Object.fromEntries(Object.entries(entry.data.signals).map(([name, signal]) => [name, portableSignal(signal)])),
11396
- consequences: entry.data.consequences.map((consequence) => ({
11397
- ...consequence,
11398
- fact: portableFact(consequence.fact)
11399
- })),
11400
- verification: entry.data.verification.map((verification) => ({
11401
- ...verification,
11402
- availableChecks: portableRefs(verification.availableChecks)
11403
- }))
11404
- }
11405
- }));
11406
- clone.progress = {
11407
- ...clone.progress,
11408
- blockers: clone.progress.blockers.map((blocker) => ({
11409
- ...blocker,
11410
- refs: portableRefs(blocker.refs)
11411
- }))
11412
- };
11413
- clone.evidence = clone.evidence.map((entry) => ({
11414
- ...entry,
11415
- data: {
11416
- ...entry.data,
11417
- refs: portableRefs(entry.data.refs),
11418
- reviewContext: portableRef(entry.data.reviewContext)
11419
- }
11420
- }));
11421
- clone.decisions = clone.decisions.map((entry) => ({
11422
- ...entry,
11423
- data: {
11424
- ...entry.data,
11425
- consumption: entry.data.consumption ? portableRef(entry.data.consumption.actionRef) ? {
11426
- ...entry.data.consumption,
11427
- actionRef: portableRef(entry.data.consumption.actionRef)
11428
- } : null : null,
11429
- binding: {
11430
- ...entry.data.binding,
11431
- contentRefs: portableRefs(entry.data.binding.contentRefs)
11432
- }
11433
- }
11434
- }));
11435
- clone.findings = clone.findings.map((entry) => ({
11436
- ...entry,
11437
- data: { ...entry.data, refs: portableRefs(entry.data.refs) }
11438
- }));
11558
+ for (const entry of clone.assessments)
11559
+ demote(entry.data.facts, entry.data.signals, entry.data.consequences);
11439
11560
  clone.candidates = [];
11440
11561
  return clone;
11441
11562
  };
@@ -11460,15 +11581,13 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11460
11581
  allocate(entry.id);
11461
11582
  const fresh = (id) => allocate(id);
11462
11583
  const provenance = importedProvenance(context);
11584
+ const remap = (ref) => mapRef(ref, ids);
11463
11585
  const intent = {
11464
11586
  ...source.intent,
11465
11587
  id: fresh(source.intent.id),
11466
11588
  recordedAt: timestamp,
11467
11589
  provenance,
11468
- data: {
11469
- ...source.intent.data,
11470
- authorityRefs: source.intent.data.authorityRefs.map((ref) => mapRef(ref, ids))
11471
- }
11590
+ data: rewriteRecordRefs(source.intent.data, intentSchema, remap)
11472
11591
  };
11473
11592
  const assessments = source.assessments.map((entry) => {
11474
11593
  const data = entry.data;
@@ -11477,28 +11596,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11477
11596
  id: fresh(entry.id),
11478
11597
  recordedAt: timestamp,
11479
11598
  provenance,
11480
- data: {
11481
- ...data,
11482
- facts: data.facts.map((fact) => ({
11483
- ...fact,
11484
- refs: fact.refs.map((ref) => mapRef(ref, ids))
11485
- })),
11486
- signals: Object.fromEntries(Object.entries(data.signals).map(([name, signal]) => [
11487
- name,
11488
- { ...signal, refs: signal.refs.map((ref) => mapRef(ref, ids)) }
11489
- ])),
11490
- consequences: data.consequences.map((consequence) => ({
11491
- ...consequence,
11492
- fact: {
11493
- ...consequence.fact,
11494
- refs: consequence.fact.refs.map((ref) => mapRef(ref, ids))
11495
- }
11496
- })),
11497
- verification: data.verification.map((verification) => ({
11498
- ...verification,
11499
- availableChecks: verification.availableChecks.map((ref) => mapRef(ref, ids))
11500
- }))
11501
- }
11599
+ data: rewriteRecordRefs(data, assessmentSchema, remap)
11502
11600
  };
11503
11601
  });
11504
11602
  const evidence = source.evidence.map((entry) => ({
@@ -11506,11 +11604,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11506
11604
  id: fresh(entry.id),
11507
11605
  recordedAt: timestamp,
11508
11606
  provenance,
11509
- data: {
11510
- ...entry.data,
11511
- refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
11512
- reviewContext: entry.data.reviewContext ? mapRef(entry.data.reviewContext, ids) : null
11513
- }
11607
+ data: rewriteRecordRefs(entry.data, evidenceSchema, remap)
11514
11608
  }));
11515
11609
  const decisions = source.decisions.map((entry) => ({
11516
11610
  ...entry,
@@ -11518,21 +11612,23 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11518
11612
  recordedAt: timestamp,
11519
11613
  provenance
11520
11614
  }));
11521
- const findings = source.findings.map((entry) => ({
11522
- ...entry,
11523
- id: fresh(entry.id),
11524
- recordedAt: timestamp,
11525
- provenance,
11526
- data: {
11527
- ...entry.data,
11528
- refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
11529
- resolution: entry.data.resolution ? {
11530
- ...entry.data.resolution,
11531
- evidenceIds: entry.data.resolution.evidenceIds.map((id) => ids.get(id) ?? id),
11532
- decisionIds: entry.data.resolution.decisionIds.map((id) => ids.get(id) ?? id)
11533
- } : null
11534
- }
11535
- }));
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
+ });
11536
11632
  const workers = source.workers.map((entry) => ({
11537
11633
  ...entry,
11538
11634
  id: fresh(entry.id),
@@ -11575,19 +11671,21 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11575
11671
  closure: null,
11576
11672
  assessments,
11577
11673
  evidence,
11578
- decisions: decisions.map((entry) => ({
11579
- ...entry,
11580
- data: {
11581
- ...entry.data,
11582
- binding: {
11583
- ...entry.data.binding,
11584
- taskId: "",
11585
- workspaceId: destinationWorkspaceId,
11586
- contentRefs: entry.data.binding.contentRefs.map((ref) => mapRef(ref, ids))
11587
- },
11588
- consumption: null
11589
- }
11590
- })),
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
+ }),
11591
11689
  ...actionProgress ? { actionProgress } : {},
11592
11690
  findings,
11593
11691
  workers,
@@ -11670,7 +11768,7 @@ class WorkitCore {
11670
11768
  activeWorkerBlocker(task, workspace) {
11671
11769
  if (workspace.writer)
11672
11770
  return failure("recovery_required", "writer ownership must be released first");
11673
- if (task.workers.some((entry) => isUncertainWorker(entry.data.state)))
11771
+ if (task.workers.some((entry) => workerBlocksTransition("close", entry.data.state)))
11674
11772
  return failure("recovery_required", "worker state requires reconciliation");
11675
11773
  return null;
11676
11774
  }
@@ -11916,37 +12014,33 @@ class WorkitCore {
11916
12014
  const currentCandidate = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
11917
12015
  if (!currentCandidate.ok)
11918
12016
  return currentCandidate;
11919
- if (evidence.kind === "check" || evidence.kind === "review") {
11920
- evidence.beforeCandidateId ??= currentCandidate.data.id;
11921
- evidence.candidateId ??= currentCandidate.data.id;
11922
- } else {
11923
- evidence.beforeCandidateId ??= null;
11924
- evidence.candidateId ??= null;
11925
- }
12017
+ const pin = helper && helper.ok ? helper.data.data.assignment.candidateId : null;
12018
+ const bound = resolveBinding(evidence, pin, currentCandidate.data.id);
11926
12019
  if (helper?.ok) {
11927
12020
  const assignment = helper.data.data.assignment;
11928
- 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))
11929
12022
  return failure("permission_denied", "evidence is outside the worker assignment");
11930
12023
  }
11931
12024
  const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
11932
- for (const candidateId of [evidence.beforeCandidateId, evidence.candidateId]) {
12025
+ for (const candidateId of [bound.beforeCandidateId, bound.candidateId]) {
11933
12026
  if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
11934
12027
  return failure("invalid_input", "evidence candidate is not a captured candidate");
11935
12028
  }
12029
+ const boundEvidence = { ...evidence, ...bound };
11936
12030
  const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
11937
12031
  const candidates = current.candidates.some((item) => item.id === currentCandidate.data.id) ? current.candidates : [...current.candidates, currentCandidate.data];
11938
12032
  const entry = {
11939
12033
  id: newId(),
11940
12034
  recordedAt: mutation.now,
11941
12035
  provenance: provenance(this.context, "agent_reported"),
11942
- data: evidence
12036
+ data: boundEvidence
11943
12037
  };
11944
12038
  return success(mutation.revision, null, {
11945
12039
  ...current,
11946
12040
  candidates,
11947
12041
  evidence: [...current.evidence, entry],
11948
12042
  findings: current.findings.map((finding) => {
11949
- if (finding.data.disposition === "open" || evidence.result === "skipped")
12043
+ if (finding.data.disposition === "open" || boundEvidence.result === "skipped")
11950
12044
  return finding;
11951
12045
  if (finding.data.disposition !== "fixed")
11952
12046
  return finding;
@@ -11955,8 +12049,12 @@ class WorkitCore {
11955
12049
  item.evidenceId,
11956
12050
  item.status
11957
12051
  ]));
11958
- 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));
11959
- 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)));
11960
12058
  return verified && !contradicted ? finding : { ...finding, data: { ...finding.data, disposition: "open", resolution: null } };
11961
12059
  })
11962
12060
  });
@@ -11981,7 +12079,12 @@ class WorkitCore {
11981
12079
  const input = parsed.data;
11982
12080
  if (nativeRequired && input.action !== "record")
11983
12081
  return failure("permission_denied", "native observation is only valid for recording decisions");
11984
- if (nativeRequired && nativeObservation === undefined)
12082
+ const stated = input.action === "record" && input.response === "stated";
12083
+ if (stated && input.purpose === "action")
12084
+ return failure("permission_denied", "stated choices cannot authorize mutating actions");
12085
+ if (stated && !input.binding?.statedChoice)
12086
+ return failure("invalid_input", "stated choices require binding.statedChoice");
12087
+ if (nativeRequired && nativeObservation === undefined && !stated)
11985
12088
  return failure("permission_denied", "native decision observation is required");
11986
12089
  const task = this.store.readTask(input.taskId);
11987
12090
  if (!task.ok)
@@ -12014,7 +12117,7 @@ class WorkitCore {
12014
12117
  consumption: null
12015
12118
  };
12016
12119
  const data = { ...base, digest: decisionDigest(base) };
12017
- const native = nativeRequired ? verifyNativeDecision(this.context.nativeAuthority, {
12120
+ const native = nativeRequired && !stated ? verifyNativeDecision(this.context.nativeAuthority, {
12018
12121
  observation: nativeObservation,
12019
12122
  expected: {
12020
12123
  taskId: task.data.id,
@@ -12109,7 +12212,7 @@ class WorkitCore {
12109
12212
  if (input.action === "record") {
12110
12213
  if (helper?.ok) {
12111
12214
  const assignment = helper.data.data.assignment;
12112
- 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))
12113
12216
  return failure("permission_denied", "finding is outside the worker assignment");
12114
12217
  }
12115
12218
  if (input.candidateId && !task.data.candidates.some((candidate) => candidate.id === input.candidateId))
@@ -12156,7 +12259,11 @@ class WorkitCore {
12156
12259
  const evaluations = evaluateEvidence(task.data, current.data);
12157
12260
  const verified = evidence.some((entry) => {
12158
12261
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
12159
- 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
+ });
12160
12267
  });
12161
12268
  if (!verified)
12162
12269
  return failure("permission_denied", "fixed findings require passing verification evidence");
@@ -12168,7 +12275,7 @@ class WorkitCore {
12168
12275
  if (entry.data.result === "missing" || entry.data.result === "skipped")
12169
12276
  return false;
12170
12277
  const candidateMatches = finding.data.candidateId === null ? entry.data.candidateId === null : entry.data.candidateId === finding.data.candidateId || entry.data.beforeCandidateId === finding.data.candidateId;
12171
- 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)));
12172
12279
  return candidateMatches && (referenceMatches || entry.data.claim === finding.data.claim);
12173
12280
  });
12174
12281
  if (!relevant)
@@ -12246,7 +12353,7 @@ class WorkitCore {
12246
12353
  const workerId = newId();
12247
12354
  const changed = this.store.mutateTaskAndWorkspace({
12248
12355
  taskId: task.data.id,
12249
- expectedTaskRevision: input.expectedRevision,
12356
+ expectedRevision: input.expectedRevision,
12250
12357
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12251
12358
  now: trustedNow(this.context),
12252
12359
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -12284,7 +12391,7 @@ class WorkitCore {
12284
12391
  return failure("permission_denied", "worker report is outside the worker assignment");
12285
12392
  const changed = this.store.mutateTaskAndWorkspace({
12286
12393
  taskId: task.data.id,
12287
- expectedTaskRevision: input.expectedRevision,
12394
+ expectedRevision: input.expectedRevision,
12288
12395
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12289
12396
  now: trustedNow(this.context),
12290
12397
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -12308,24 +12415,25 @@ class WorkitCore {
12308
12415
  return failure("invalid_transition", "closed task cannot cancel workers");
12309
12416
  if (entry.data.state === "unknown")
12310
12417
  return failure("recovery_required", "worker state requires recovery");
12311
- if (entry.data.state === "stopped")
12312
- return failure("invalid_transition", "stopped worker cannot be cancelled");
12313
- 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
+ }
12314
12422
  const changed = this.store.mutateTaskAndWorkspace({
12315
12423
  taskId: task.data.id,
12316
- expectedTaskRevision: input.expectedRevision,
12424
+ expectedRevision: input.expectedRevision,
12317
12425
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12318
12426
  now: trustedNow(this.context),
12319
- 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
+ }),
12320
12431
  task: (current, mutation) => success(mutation.revision, null, {
12321
12432
  ...current,
12322
12433
  workers: current.workers.map((candidate) => candidate.id === input.workerId ? {
12323
12434
  ...candidate,
12324
12435
  recordedAt: mutation.now,
12325
- data: {
12326
- ...candidate.data,
12327
- state: settles ? "stopped" : "cancelling"
12328
- }
12436
+ data: { ...candidate.data, state: "stopped" }
12329
12437
  } : candidate)
12330
12438
  })
12331
12439
  });
@@ -12422,7 +12530,7 @@ class WorkitCore {
12422
12530
  return attested;
12423
12531
  const changed = this.store.mutateTaskAndWorkspace({
12424
12532
  taskId: task.data.id,
12425
- expectedTaskRevision: input.expectedRevision,
12533
+ expectedRevision: input.expectedRevision,
12426
12534
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12427
12535
  now: trustedNow(this.context),
12428
12536
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -12588,6 +12696,12 @@ class WorkitCore {
12588
12696
  if (!sameSession2(helper.data.data.session, this.context))
12589
12697
  return failure("permission_denied", "worker session does not match the caller");
12590
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
+ }
12591
12705
  const owner = {
12592
12706
  taskId: task.data.id,
12593
12707
  workerId: helperId,
@@ -12599,7 +12713,7 @@ class WorkitCore {
12599
12713
  };
12600
12714
  const changed = this.store.mutateTaskAndWorkspace({
12601
12715
  taskId: task.data.id,
12602
- expectedTaskRevision: input.expectedRevision,
12716
+ expectedRevision: input.expectedRevision,
12603
12717
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12604
12718
  now: trustedNow(this.context),
12605
12719
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
@@ -12630,7 +12744,7 @@ class WorkitCore {
12630
12744
  }
12631
12745
  const changed = this.store.mutateTaskAndWorkspace({
12632
12746
  taskId: task.data.id,
12633
- expectedTaskRevision: input.expectedRevision,
12747
+ expectedRevision: input.expectedRevision,
12634
12748
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12635
12749
  now: trustedNow(this.context),
12636
12750
  workspace: (currentWorkspace, mutation) => success(mutation.revision, mutation.revision, { ...currentWorkspace, writer: null }),
@@ -12879,7 +12993,15 @@ class WorkitCore {
12879
12993
  if (seen.has(observation.workerId))
12880
12994
  return failure("invalid_input", "worker observations must be unique");
12881
12995
  seen.add(observation.workerId);
12882
- 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)
12883
13005
  return failure("revision_conflict", "worker observation is stale");
12884
13006
  if (!view.task.workers.some((entry) => entry.id === observation.workerId))
12885
13007
  return failure("permission_denied", "worker observation is not assigned");
@@ -12965,19 +13087,26 @@ class WorkitCore {
12965
13087
  return failure("permission_denied", "imported resume requires native destination approval");
12966
13088
  resumeCandidate = current.data.candidate;
12967
13089
  }
12968
- 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);
12969
13091
  if (blocker)
12970
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
+ }
12971
13100
  const changed = this.store.mutateTaskAndWorkspace({
12972
13101
  taskId: task.data.id,
12973
- expectedTaskRevision: input.expectedRevision,
13102
+ expectedRevision: input.expectedRevision,
12974
13103
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12975
13104
  now: trustedNow(this.context),
12976
13105
  workspace: (workspace, context) => success(context.revision, context.revision, workspace),
12977
13106
  task: (current, context) => success(context.revision, null, {
12978
13107
  ...current,
12979
13108
  status,
12980
- 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,
12981
13110
  progress: current.progress,
12982
13111
  pauseReason: status === "paused" ? input.reason ?? null : null
12983
13112
  })
@@ -13015,12 +13144,13 @@ class WorkitCore {
13015
13144
  return workspace;
13016
13145
  if (!workspace.data)
13017
13146
  return failure("not_found", "workspace not found");
13018
- if (workspace.data.writer || task.data.workers.some((entry) => isUncertainWorker(entry.data.state)))
13019
- 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;
13020
13150
  this.fillRevisions(input, task.data, workspace.data);
13021
13151
  const changed = this.store.mutateTaskAndWorkspace({
13022
13152
  taskId: task.data.id,
13023
- expectedTaskRevision: input.expectedRevision,
13153
+ expectedRevision: input.expectedRevision,
13024
13154
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
13025
13155
  now: trustedNow(this.context),
13026
13156
  workspace: (workspace, context) => success(context.revision, context.revision, workspace),
@@ -13076,7 +13206,7 @@ class WorkitCore {
13076
13206
  return closure;
13077
13207
  const changed = this.store.mutateTaskAndWorkspace({
13078
13208
  taskId: task.data.id,
13079
- expectedTaskRevision: input.expectedRevision,
13209
+ expectedRevision: input.expectedRevision,
13080
13210
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
13081
13211
  now: trustedNow(this.context),
13082
13212
  workspace: (value, context) => success(context.revision, context.revision, value),
@@ -13167,7 +13297,7 @@ var cursorCapabilities = (availability = {}) => {
13167
13297
  }
13168
13298
  ];
13169
13299
  };
13170
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
13300
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
13171
13301
  var eventNames = new Set([
13172
13302
  "sessionStart",
13173
13303
  "preToolUse",
@@ -13178,7 +13308,7 @@ var eventNames = new Set([
13178
13308
  ]);
13179
13309
  var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
13180
13310
  var parseCursorHookInput = (value) => {
13181
- if (!isRecord(value) || !eventNames.has(value.hook_event_name))
13311
+ if (!isRecord2(value) || !eventNames.has(value.hook_event_name))
13182
13312
  return { ok: false, error: "hook_event_name is required" };
13183
13313
  const roots = value.workspace_roots;
13184
13314
  if (!Array.isArray(roots) || roots.length !== 1 || !roots.every(nonEmpty2))
@@ -13325,7 +13455,7 @@ var handleSubagentStop = () => ({});
13325
13455
  var handleCursorHook = (raw) => {
13326
13456
  const parsed = parseCursorHookInput(raw);
13327
13457
  if (!parsed.ok) {
13328
- return isRecord(raw) && raw.hook_event_name === "sessionStart" ? {} : deny(parsed.error);
13458
+ return isRecord2(raw) && raw.hook_event_name === "sessionStart" ? {} : deny(parsed.error);
13329
13459
  }
13330
13460
  const input = parsed.data;
13331
13461
  const root = realpathSync5(input.workspace_roots[0]);
@@ -13370,7 +13500,7 @@ var runCursorHook = async () => {
13370
13500
  const output = handleCursorHook(input);
13371
13501
  process.stdout.write(`${JSON.stringify(output)}
13372
13502
  `);
13373
- const event = isRecord(input) ? input.hook_event_name : undefined;
13503
+ const event = isRecord2(input) ? input.hook_event_name : undefined;
13374
13504
  if (output.permission === "deny" && ["preToolUse", "beforeShellExecution", "subagentStart"].includes(String(event)))
13375
13505
  process.exitCode = 2;
13376
13506
  } catch {