@brainervirus/workit-codex 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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);
@@ -10777,6 +10943,44 @@ function shellRouteIntent(command) {
10777
10943
  }
10778
10944
  return null;
10779
10945
  }
10946
+ function hasLiveWorkitTask(dir) {
10947
+ try {
10948
+ if (typeof dir !== "string" || dir === "")
10949
+ return false;
10950
+ const listed = new TaskStore(dir).listTasks();
10951
+ if (!listed.ok)
10952
+ return false;
10953
+ return listed.data.some((task) => task.status === "active" || task.status === "paused");
10954
+ } catch {
10955
+ return false;
10956
+ }
10957
+ }
10958
+ function shouldDenyShellRoute(dir, command) {
10959
+ if (!hasLiveWorkitTask(dir))
10960
+ return null;
10961
+ return shellRouteIntent(command);
10962
+ }
10963
+ // packages/workit-core/src/core/config.ts
10964
+ var PRESETS = {
10965
+ gitflow: {
10966
+ allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
10967
+ protected: ["main", "develop", "master", "prod", "production"]
10968
+ },
10969
+ "github-flow": { allowed: ["*"], protected: ["main"] },
10970
+ "trunk-based": { allowed: ["*"], protected: ["main"] },
10971
+ custom: { allowed: [], protected: [] }
10972
+ };
10973
+ var DEFAULTS = {
10974
+ locale: "en",
10975
+ localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
10976
+ timezone: "America/Santiago",
10977
+ branchPolicy: {
10978
+ preset: "gitflow",
10979
+ allowed: [...PRESETS.gitflow.allowed],
10980
+ protected: [...PRESETS.gitflow.protected]
10981
+ },
10982
+ commitPolicy: { preset: "conventional" }
10983
+ };
10780
10984
  // packages/workit-core/src/core/external-action.ts
10781
10985
  var externalActionSchema = discriminatedUnion("operation", [
10782
10986
  object({
@@ -11314,6 +11518,8 @@ var applyWorkerLifecycle = (input) => {
11314
11518
  if (task.data.status === "paused" && (input.state === "running" || input.state === "cancelling"))
11315
11519
  return failure("invalid_transition", "paused task cannot run a worker");
11316
11520
  const notStarted = input.dispatch === true;
11521
+ if (notStarted && entry.data.state === "stopped" && input.session === null)
11522
+ return success(task.data.revision, workspace.data.revision, entry);
11317
11523
  if (notStarted && (input.state !== "stopped" || input.session !== null))
11318
11524
  return failure("invalid_input", "a never-dispatched worker stops with no session");
11319
11525
  if (notStarted && entry.data.session !== null)
@@ -11345,7 +11551,7 @@ var applyWorkerLifecycle = (input) => {
11345
11551
  return success(task.data.revision, workspace.data.revision, entry);
11346
11552
  const changed = input.store.mutateTaskAndWorkspace({
11347
11553
  taskId: task.data.id,
11348
- expectedTaskRevision: input.expectedRevision,
11554
+ expectedRevision: input.expectedRevision,
11349
11555
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
11350
11556
  now: input.now,
11351
11557
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
@@ -11393,90 +11599,22 @@ var importedProvenance = (context) => ({
11393
11599
  receipts: []
11394
11600
  });
11395
11601
  var portableTask = (task) => {
11396
- const clone = structuredClone(task);
11397
- const portableProvenance = (value) => ({
11398
- ...value,
11399
- session: null,
11400
- receipts: []
11401
- });
11402
- const entries = [
11403
- clone.intent,
11404
- ...clone.assessments,
11405
- ...clone.evidence,
11406
- ...clone.decisions,
11407
- ...clone.findings,
11408
- ...clone.workers
11409
- ];
11410
- for (const entry of entries)
11411
- entry.provenance = portableProvenance(entry.provenance);
11412
- for (const worker of clone.workers)
11413
- worker.data.session = null;
11414
- clone.intent = {
11415
- ...clone.intent,
11416
- data: { ...clone.intent.data, authorityRefs: portableRefs(clone.intent.data.authorityRefs) }
11417
- };
11418
- clone.constraints = clone.constraints.flatMap((constraint) => {
11419
- const source = portableRef(constraint.source);
11420
- return source ? [
11421
- {
11422
- ...constraint,
11423
- source,
11424
- requires: constraint.requires.map((requirement) => ({
11425
- ...requirement,
11426
- refs: portableRefs(requirement.refs)
11427
- }))
11428
- }
11429
- ] : [];
11430
- });
11431
- clone.assessments = clone.assessments.map((entry) => ({
11432
- ...entry,
11433
- data: {
11434
- ...entry.data,
11435
- facts: entry.data.facts.map(portableFact),
11436
- signals: Object.fromEntries(Object.entries(entry.data.signals).map(([name, signal]) => [name, portableSignal(signal)])),
11437
- consequences: entry.data.consequences.map((consequence) => ({
11438
- ...consequence,
11439
- fact: portableFact(consequence.fact)
11440
- })),
11441
- verification: entry.data.verification.map((verification) => ({
11442
- ...verification,
11443
- availableChecks: portableRefs(verification.availableChecks)
11444
- }))
11445
- }
11446
- }));
11447
- clone.progress = {
11448
- ...clone.progress,
11449
- blockers: clone.progress.blockers.map((blocker) => ({
11450
- ...blocker,
11451
- refs: portableRefs(blocker.refs)
11452
- }))
11602
+ const stripped = rewriteRecordRefs(task, taskRecordSchema, portableRef);
11603
+ if (typeof stripped !== "object" || stripped === null || Array.isArray(stripped))
11604
+ throw new TypeError("portable task rewrite produced a non-record");
11605
+ const clone = structuredClone(stripped);
11606
+ const demote = (facts, signals, consequences) => {
11607
+ for (const fact of facts)
11608
+ Object.assign(fact, portableFact(fact));
11609
+ for (const name of Object.keys(signals)) {
11610
+ const signal = signals[name];
11611
+ Object.assign(signal, portableSignal(signal));
11612
+ }
11613
+ for (const consequence of consequences)
11614
+ Object.assign(consequence.fact, portableFact(consequence.fact));
11453
11615
  };
11454
- clone.evidence = clone.evidence.map((entry) => ({
11455
- ...entry,
11456
- data: {
11457
- ...entry.data,
11458
- refs: portableRefs(entry.data.refs),
11459
- reviewContext: portableRef(entry.data.reviewContext)
11460
- }
11461
- }));
11462
- clone.decisions = clone.decisions.map((entry) => ({
11463
- ...entry,
11464
- data: {
11465
- ...entry.data,
11466
- consumption: entry.data.consumption ? portableRef(entry.data.consumption.actionRef) ? {
11467
- ...entry.data.consumption,
11468
- actionRef: portableRef(entry.data.consumption.actionRef)
11469
- } : null : null,
11470
- binding: {
11471
- ...entry.data.binding,
11472
- contentRefs: portableRefs(entry.data.binding.contentRefs)
11473
- }
11474
- }
11475
- }));
11476
- clone.findings = clone.findings.map((entry) => ({
11477
- ...entry,
11478
- data: { ...entry.data, refs: portableRefs(entry.data.refs) }
11479
- }));
11616
+ for (const entry of clone.assessments)
11617
+ demote(entry.data.facts, entry.data.signals, entry.data.consequences);
11480
11618
  clone.candidates = [];
11481
11619
  return clone;
11482
11620
  };
@@ -11501,15 +11639,13 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11501
11639
  allocate(entry.id);
11502
11640
  const fresh = (id) => allocate(id);
11503
11641
  const provenance = importedProvenance(context);
11642
+ const remap = (ref) => mapRef(ref, ids);
11504
11643
  const intent = {
11505
11644
  ...source.intent,
11506
11645
  id: fresh(source.intent.id),
11507
11646
  recordedAt: timestamp,
11508
11647
  provenance,
11509
- data: {
11510
- ...source.intent.data,
11511
- authorityRefs: source.intent.data.authorityRefs.map((ref) => mapRef(ref, ids))
11512
- }
11648
+ data: rewriteRecordRefs(source.intent.data, intentSchema, remap)
11513
11649
  };
11514
11650
  const assessments = source.assessments.map((entry) => {
11515
11651
  const data = entry.data;
@@ -11518,28 +11654,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11518
11654
  id: fresh(entry.id),
11519
11655
  recordedAt: timestamp,
11520
11656
  provenance,
11521
- data: {
11522
- ...data,
11523
- facts: data.facts.map((fact) => ({
11524
- ...fact,
11525
- refs: fact.refs.map((ref) => mapRef(ref, ids))
11526
- })),
11527
- signals: Object.fromEntries(Object.entries(data.signals).map(([name, signal]) => [
11528
- name,
11529
- { ...signal, refs: signal.refs.map((ref) => mapRef(ref, ids)) }
11530
- ])),
11531
- consequences: data.consequences.map((consequence) => ({
11532
- ...consequence,
11533
- fact: {
11534
- ...consequence.fact,
11535
- refs: consequence.fact.refs.map((ref) => mapRef(ref, ids))
11536
- }
11537
- })),
11538
- verification: data.verification.map((verification) => ({
11539
- ...verification,
11540
- availableChecks: verification.availableChecks.map((ref) => mapRef(ref, ids))
11541
- }))
11542
- }
11657
+ data: rewriteRecordRefs(data, assessmentSchema, remap)
11543
11658
  };
11544
11659
  });
11545
11660
  const evidence = source.evidence.map((entry) => ({
@@ -11547,11 +11662,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11547
11662
  id: fresh(entry.id),
11548
11663
  recordedAt: timestamp,
11549
11664
  provenance,
11550
- data: {
11551
- ...entry.data,
11552
- refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
11553
- reviewContext: entry.data.reviewContext ? mapRef(entry.data.reviewContext, ids) : null
11554
- }
11665
+ data: rewriteRecordRefs(entry.data, evidenceSchema, remap)
11555
11666
  }));
11556
11667
  const decisions = source.decisions.map((entry) => ({
11557
11668
  ...entry,
@@ -11559,21 +11670,23 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11559
11670
  recordedAt: timestamp,
11560
11671
  provenance
11561
11672
  }));
11562
- const findings = source.findings.map((entry) => ({
11563
- ...entry,
11564
- id: fresh(entry.id),
11565
- recordedAt: timestamp,
11566
- provenance,
11567
- data: {
11568
- ...entry.data,
11569
- refs: entry.data.refs.map((ref) => mapRef(ref, ids)),
11570
- resolution: entry.data.resolution ? {
11571
- ...entry.data.resolution,
11572
- evidenceIds: entry.data.resolution.evidenceIds.map((id) => ids.get(id) ?? id),
11573
- decisionIds: entry.data.resolution.decisionIds.map((id) => ids.get(id) ?? id)
11574
- } : null
11575
- }
11576
- }));
11673
+ const findings = source.findings.map((entry) => {
11674
+ const data = rewriteRecordRefs(entry.data, findingSchema, remap);
11675
+ return {
11676
+ ...entry,
11677
+ id: fresh(entry.id),
11678
+ recordedAt: timestamp,
11679
+ provenance,
11680
+ data: {
11681
+ ...data,
11682
+ resolution: data.resolution ? {
11683
+ ...data.resolution,
11684
+ evidenceIds: data.resolution.evidenceIds.map((id) => ids.get(id) ?? id),
11685
+ decisionIds: data.resolution.decisionIds.map((id) => ids.get(id) ?? id)
11686
+ } : null
11687
+ }
11688
+ };
11689
+ });
11577
11690
  const workers = source.workers.map((entry) => ({
11578
11691
  ...entry,
11579
11692
  id: fresh(entry.id),
@@ -11616,19 +11729,21 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
11616
11729
  closure: null,
11617
11730
  assessments,
11618
11731
  evidence,
11619
- decisions: decisions.map((entry) => ({
11620
- ...entry,
11621
- data: {
11622
- ...entry.data,
11623
- binding: {
11624
- ...entry.data.binding,
11625
- taskId: "",
11626
- workspaceId: destinationWorkspaceId,
11627
- contentRefs: entry.data.binding.contentRefs.map((ref) => mapRef(ref, ids))
11628
- },
11629
- consumption: null
11630
- }
11631
- })),
11732
+ decisions: decisions.map((entry) => {
11733
+ const data = rewriteRecordRefs(entry.data, decisionSchema, remap);
11734
+ return {
11735
+ ...entry,
11736
+ data: {
11737
+ ...data,
11738
+ binding: {
11739
+ ...data.binding,
11740
+ taskId: "",
11741
+ workspaceId: destinationWorkspaceId
11742
+ },
11743
+ consumption: null
11744
+ }
11745
+ };
11746
+ }),
11632
11747
  ...actionProgress ? { actionProgress } : {},
11633
11748
  findings,
11634
11749
  workers,
@@ -11711,7 +11826,7 @@ class WorkitCore {
11711
11826
  activeWorkerBlocker(task, workspace) {
11712
11827
  if (workspace.writer)
11713
11828
  return failure("recovery_required", "writer ownership must be released first");
11714
- if (task.workers.some((entry) => isUncertainWorker(entry.data.state)))
11829
+ if (task.workers.some((entry) => workerBlocksTransition("close", entry.data.state)))
11715
11830
  return failure("recovery_required", "worker state requires reconciliation");
11716
11831
  return null;
11717
11832
  }
@@ -11957,37 +12072,33 @@ class WorkitCore {
11957
12072
  const currentCandidate = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
11958
12073
  if (!currentCandidate.ok)
11959
12074
  return currentCandidate;
11960
- if (evidence.kind === "check" || evidence.kind === "review") {
11961
- evidence.beforeCandidateId ??= currentCandidate.data.id;
11962
- evidence.candidateId ??= currentCandidate.data.id;
11963
- } else {
11964
- evidence.beforeCandidateId ??= null;
11965
- evidence.candidateId ??= null;
11966
- }
12075
+ const pin = helper && helper.ok ? helper.data.data.assignment.candidateId : null;
12076
+ const bound = resolveBinding(evidence, pin, currentCandidate.data.id);
11967
12077
  if (helper?.ok) {
11968
12078
  const assignment = helper.data.data.assignment;
11969
- if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || assignment.candidateId !== null && evidence.candidateId !== assignment.candidateId && evidence.beforeCandidateId !== assignment.candidateId || !refsWithinScope(evidence.refs, assignment.scope) || !refsWithinScope(evidence.refs, task.data.intent.data.scope))
12079
+ if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || !checkPin(assignment, bound.beforeCandidateId, bound.candidateId) || !refsWithinScope(evidence.refs, assignment.scope) || !refsWithinScope(evidence.refs, task.data.intent.data.scope))
11970
12080
  return failure("permission_denied", "evidence is outside the worker assignment");
11971
12081
  }
11972
12082
  const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
11973
- for (const candidateId of [evidence.beforeCandidateId, evidence.candidateId]) {
12083
+ for (const candidateId of [bound.beforeCandidateId, bound.candidateId]) {
11974
12084
  if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
11975
12085
  return failure("invalid_input", "evidence candidate is not a captured candidate");
11976
12086
  }
12087
+ const boundEvidence = { ...evidence, ...bound };
11977
12088
  const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
11978
12089
  const candidates = current.candidates.some((item) => item.id === currentCandidate.data.id) ? current.candidates : [...current.candidates, currentCandidate.data];
11979
12090
  const entry = {
11980
12091
  id: newId(),
11981
12092
  recordedAt: mutation.now,
11982
12093
  provenance: provenance(this.context, "agent_reported"),
11983
- data: evidence
12094
+ data: boundEvidence
11984
12095
  };
11985
12096
  return success(mutation.revision, null, {
11986
12097
  ...current,
11987
12098
  candidates,
11988
12099
  evidence: [...current.evidence, entry],
11989
12100
  findings: current.findings.map((finding) => {
11990
- if (finding.data.disposition === "open" || evidence.result === "skipped")
12101
+ if (finding.data.disposition === "open" || boundEvidence.result === "skipped")
11991
12102
  return finding;
11992
12103
  if (finding.data.disposition !== "fixed")
11993
12104
  return finding;
@@ -11996,8 +12107,12 @@ class WorkitCore {
11996
12107
  item.evidenceId,
11997
12108
  item.status
11998
12109
  ]));
11999
- const verified = withEntry.evidence.some((item) => statuses.get(item.id) === "passed" && (item.data.kind === "check" || item.data.kind === "review") && (finding.data.candidateId === null || item.data.candidateId === finding.data.candidateId));
12000
- const contradicted = evidence.result === "failed" && finding.data.refs.some((left) => evidence.refs.some((right) => JSON.stringify(left) === JSON.stringify(right)));
12110
+ const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
12111
+ kind: item.data.kind,
12112
+ candidateId: item.data.candidateId,
12113
+ status: statuses.get(item.id) ?? "missing"
12114
+ }));
12115
+ const contradicted = boundEvidence.result === "failed" && finding.data.refs.some((left) => boundEvidence.refs.some((right) => sameValue(left, right)));
12001
12116
  return verified && !contradicted ? finding : { ...finding, data: { ...finding.data, disposition: "open", resolution: null } };
12002
12117
  })
12003
12118
  });
@@ -12022,7 +12137,12 @@ class WorkitCore {
12022
12137
  const input = parsed.data;
12023
12138
  if (nativeRequired && input.action !== "record")
12024
12139
  return failure("permission_denied", "native observation is only valid for recording decisions");
12025
- if (nativeRequired && nativeObservation === undefined)
12140
+ const stated = input.action === "record" && input.response === "stated";
12141
+ if (stated && input.purpose === "action")
12142
+ return failure("permission_denied", "stated choices cannot authorize mutating actions");
12143
+ if (stated && !input.binding?.statedChoice)
12144
+ return failure("invalid_input", "stated choices require binding.statedChoice");
12145
+ if (nativeRequired && nativeObservation === undefined && !stated)
12026
12146
  return failure("permission_denied", "native decision observation is required");
12027
12147
  const task = this.store.readTask(input.taskId);
12028
12148
  if (!task.ok)
@@ -12055,7 +12175,7 @@ class WorkitCore {
12055
12175
  consumption: null
12056
12176
  };
12057
12177
  const data = { ...base, digest: decisionDigest(base) };
12058
- const native = nativeRequired ? verifyNativeDecision(this.context.nativeAuthority, {
12178
+ const native = nativeRequired && !stated ? verifyNativeDecision(this.context.nativeAuthority, {
12059
12179
  observation: nativeObservation,
12060
12180
  expected: {
12061
12181
  taskId: task.data.id,
@@ -12150,7 +12270,7 @@ class WorkitCore {
12150
12270
  if (input.action === "record") {
12151
12271
  if (helper?.ok) {
12152
12272
  const assignment = helper.data.data.assignment;
12153
- if (!bindingCovers(assignment.scope, input.scope) || !bindingCovers(task.data.intent.data.scope, input.scope) || assignment.candidateId !== null && input.candidateId !== assignment.candidateId || !refsWithinScope(input.refs, assignment.scope) || !refsWithinScope(input.refs, task.data.intent.data.scope))
12273
+ if (!bindingCovers(assignment.scope, input.scope) || !bindingCovers(task.data.intent.data.scope, input.scope) || !checkPin(assignment, null, input.candidateId ?? null) || !refsWithinScope(input.refs, assignment.scope) || !refsWithinScope(input.refs, task.data.intent.data.scope))
12154
12274
  return failure("permission_denied", "finding is outside the worker assignment");
12155
12275
  }
12156
12276
  if (input.candidateId && !task.data.candidates.some((candidate) => candidate.id === input.candidateId))
@@ -12197,7 +12317,11 @@ class WorkitCore {
12197
12317
  const evaluations = evaluateEvidence(task.data, current.data);
12198
12318
  const verified = evidence.some((entry) => {
12199
12319
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
12200
- return evaluation?.status === "passed" && (entry.data.kind === "check" || entry.data.kind === "review") && (finding.data.candidateId === null || entry.data.candidateId === finding.data.candidateId);
12320
+ return findingVerificationPasses(finding.data.candidateId, {
12321
+ kind: entry.data.kind,
12322
+ candidateId: entry.data.candidateId,
12323
+ status: evaluation?.status ?? "missing"
12324
+ });
12201
12325
  });
12202
12326
  if (!verified)
12203
12327
  return failure("permission_denied", "fixed findings require passing verification evidence");
@@ -12209,7 +12333,7 @@ class WorkitCore {
12209
12333
  if (entry.data.result === "missing" || entry.data.result === "skipped")
12210
12334
  return false;
12211
12335
  const candidateMatches = finding.data.candidateId === null ? entry.data.candidateId === null : entry.data.candidateId === finding.data.candidateId || entry.data.beforeCandidateId === finding.data.candidateId;
12212
- const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) => JSON.stringify(left) === JSON.stringify(right)));
12336
+ const referenceMatches = finding.data.refs.some((left) => entry.data.refs.some((right) => sameValue(left, right)));
12213
12337
  return candidateMatches && (referenceMatches || entry.data.claim === finding.data.claim);
12214
12338
  });
12215
12339
  if (!relevant)
@@ -12287,7 +12411,7 @@ class WorkitCore {
12287
12411
  const workerId = newId();
12288
12412
  const changed = this.store.mutateTaskAndWorkspace({
12289
12413
  taskId: task.data.id,
12290
- expectedTaskRevision: input.expectedRevision,
12414
+ expectedRevision: input.expectedRevision,
12291
12415
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12292
12416
  now: trustedNow(this.context),
12293
12417
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -12325,7 +12449,7 @@ class WorkitCore {
12325
12449
  return failure("permission_denied", "worker report is outside the worker assignment");
12326
12450
  const changed = this.store.mutateTaskAndWorkspace({
12327
12451
  taskId: task.data.id,
12328
- expectedTaskRevision: input.expectedRevision,
12452
+ expectedRevision: input.expectedRevision,
12329
12453
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12330
12454
  now: trustedNow(this.context),
12331
12455
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -12349,24 +12473,25 @@ class WorkitCore {
12349
12473
  return failure("invalid_transition", "closed task cannot cancel workers");
12350
12474
  if (entry.data.state === "unknown")
12351
12475
  return failure("recovery_required", "worker state requires recovery");
12352
- if (entry.data.state === "stopped")
12353
- return failure("invalid_transition", "stopped worker cannot be cancelled");
12354
- const settles = entry.data.report !== null || entry.data.state === "cancelling";
12476
+ if (entry.data.state === "stopped") {
12477
+ const stopped = task.data.workers.find((candidate) => candidate.id === input.workerId);
12478
+ return success(task.data.revision, workspace.data.revision, stopped);
12479
+ }
12355
12480
  const changed = this.store.mutateTaskAndWorkspace({
12356
12481
  taskId: task.data.id,
12357
- expectedTaskRevision: input.expectedRevision,
12482
+ expectedRevision: input.expectedRevision,
12358
12483
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12359
12484
  now: trustedNow(this.context),
12360
- workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
12485
+ workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
12486
+ ...current,
12487
+ writer: current.writer?.owner.workerId === input.workerId ? null : current.writer
12488
+ }),
12361
12489
  task: (current, mutation) => success(mutation.revision, null, {
12362
12490
  ...current,
12363
12491
  workers: current.workers.map((candidate) => candidate.id === input.workerId ? {
12364
12492
  ...candidate,
12365
12493
  recordedAt: mutation.now,
12366
- data: {
12367
- ...candidate.data,
12368
- state: settles ? "stopped" : "cancelling"
12369
- }
12494
+ data: { ...candidate.data, state: "stopped" }
12370
12495
  } : candidate)
12371
12496
  })
12372
12497
  });
@@ -12463,7 +12588,7 @@ class WorkitCore {
12463
12588
  return attested;
12464
12589
  const changed = this.store.mutateTaskAndWorkspace({
12465
12590
  taskId: task.data.id,
12466
- expectedTaskRevision: input.expectedRevision,
12591
+ expectedRevision: input.expectedRevision,
12467
12592
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12468
12593
  now: trustedNow(this.context),
12469
12594
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, current),
@@ -12629,6 +12754,12 @@ class WorkitCore {
12629
12754
  if (!sameSession2(helper.data.data.session, this.context))
12630
12755
  return failure("permission_denied", "worker session does not match the caller");
12631
12756
  }
12757
+ const gate = this.view(task.data);
12758
+ if (gate.ok) {
12759
+ const writeGates = (task.data.policy?.requirements ?? []).filter((requirement) => requirement.before === "write" && gate.data.requirements.some((evaluation) => evaluation.requirementId === requirement.id && (evaluation.status === "unsatisfied" || evaluation.status === "unavailable")));
12760
+ if (writeGates.length)
12761
+ return failure("requirements_unsatisfied", "writer ownership is gated by unsatisfied requirements; record their evidence or an approved limitation waiver first", { requirementIds: writeGates.map((requirement) => requirement.id) });
12762
+ }
12632
12763
  const owner = {
12633
12764
  taskId: task.data.id,
12634
12765
  workerId: helperId,
@@ -12640,7 +12771,7 @@ class WorkitCore {
12640
12771
  };
12641
12772
  const changed = this.store.mutateTaskAndWorkspace({
12642
12773
  taskId: task.data.id,
12643
- expectedTaskRevision: input.expectedRevision,
12774
+ expectedRevision: input.expectedRevision,
12644
12775
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12645
12776
  now: trustedNow(this.context),
12646
12777
  workspace: (current, mutation) => success(mutation.revision, mutation.revision, {
@@ -12671,7 +12802,7 @@ class WorkitCore {
12671
12802
  }
12672
12803
  const changed = this.store.mutateTaskAndWorkspace({
12673
12804
  taskId: task.data.id,
12674
- expectedTaskRevision: input.expectedRevision,
12805
+ expectedRevision: input.expectedRevision,
12675
12806
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12676
12807
  now: trustedNow(this.context),
12677
12808
  workspace: (currentWorkspace, mutation) => success(mutation.revision, mutation.revision, { ...currentWorkspace, writer: null }),
@@ -12920,7 +13051,15 @@ class WorkitCore {
12920
13051
  if (seen.has(observation.workerId))
12921
13052
  return failure("invalid_input", "worker observations must be unique");
12922
13053
  seen.add(observation.workerId);
12923
- if (observation.taskId !== view.task.id || observation.expectedRevision !== view.task.revision || observation.expectedWorkspaceRevision !== view.workspace.revision)
13054
+ const freshTask = this.store.readTask(view.task.id);
13055
+ const freshWorkspace = this.store.readWorkspace();
13056
+ if (!freshTask.ok)
13057
+ return freshTask;
13058
+ if (!freshWorkspace.ok)
13059
+ return freshWorkspace;
13060
+ if (!freshWorkspace.data)
13061
+ return failure("not_found", "workspace not found");
13062
+ if (observation.taskId !== view.task.id || observation.expectedRevision !== freshTask.data.revision || observation.expectedWorkspaceRevision !== freshWorkspace.data.revision)
12924
13063
  return failure("revision_conflict", "worker observation is stale");
12925
13064
  if (!view.task.workers.some((entry) => entry.id === observation.workerId))
12926
13065
  return failure("permission_denied", "worker observation is not assigned");
@@ -13006,19 +13145,26 @@ class WorkitCore {
13006
13145
  return failure("permission_denied", "imported resume requires native destination approval");
13007
13146
  resumeCandidate = current.data.candidate;
13008
13147
  }
13009
- const blocker = this.activeWorkerBlocker(task.data, workspace.data);
13148
+ const blocker = status === "paused" ? workspace.data.writer ? failure("recovery_required", "writer ownership must be released first") : null : this.activeWorkerBlocker(task.data, workspace.data);
13010
13149
  if (blocker)
13011
13150
  return blocker;
13151
+ let pauseCandidate = null;
13152
+ if (status === "paused") {
13153
+ const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
13154
+ if (!captured.ok)
13155
+ return captured;
13156
+ pauseCandidate = captured.data;
13157
+ }
13012
13158
  const changed = this.store.mutateTaskAndWorkspace({
13013
13159
  taskId: task.data.id,
13014
- expectedTaskRevision: input.expectedRevision,
13160
+ expectedRevision: input.expectedRevision,
13015
13161
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
13016
13162
  now: trustedNow(this.context),
13017
13163
  workspace: (workspace, context) => success(context.revision, context.revision, workspace),
13018
13164
  task: (current, context) => success(context.revision, null, {
13019
13165
  ...current,
13020
13166
  status,
13021
- candidates: resumeCandidate && !current.candidates.some((candidate) => candidate.id === resumeCandidate.id) ? [...current.candidates, resumeCandidate] : current.candidates,
13167
+ candidates: (resumeCandidate ?? pauseCandidate) && !current.candidates.some((candidate) => candidate.id === (resumeCandidate ?? pauseCandidate).id) ? [...current.candidates, resumeCandidate ?? pauseCandidate] : current.candidates,
13022
13168
  progress: current.progress,
13023
13169
  pauseReason: status === "paused" ? input.reason ?? null : null
13024
13170
  })
@@ -13056,12 +13202,13 @@ class WorkitCore {
13056
13202
  return workspace;
13057
13203
  if (!workspace.data)
13058
13204
  return failure("not_found", "workspace not found");
13059
- if (workspace.data.writer || task.data.workers.some((entry) => isUncertainWorker(entry.data.state)))
13060
- return failure("recovery_required", "scope revision requires worker ownership reconciliation");
13205
+ const revisionBlocker = this.activeWorkerBlocker(task.data, workspace.data);
13206
+ if (revisionBlocker)
13207
+ return revisionBlocker;
13061
13208
  this.fillRevisions(input, task.data, workspace.data);
13062
13209
  const changed = this.store.mutateTaskAndWorkspace({
13063
13210
  taskId: task.data.id,
13064
- expectedTaskRevision: input.expectedRevision,
13211
+ expectedRevision: input.expectedRevision,
13065
13212
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
13066
13213
  now: trustedNow(this.context),
13067
13214
  workspace: (workspace, context) => success(context.revision, context.revision, workspace),
@@ -13117,7 +13264,7 @@ class WorkitCore {
13117
13264
  return closure;
13118
13265
  const changed = this.store.mutateTaskAndWorkspace({
13119
13266
  taskId: task.data.id,
13120
- expectedTaskRevision: input.expectedRevision,
13267
+ expectedRevision: input.expectedRevision,
13121
13268
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
13122
13269
  now: trustedNow(this.context),
13123
13270
  workspace: (value, context) => success(context.revision, context.revision, value),
@@ -13386,7 +13533,7 @@ var handleCodexHook = (raw) => {
13386
13533
  if (input.hook_event_name === "PreToolUse") {
13387
13534
  if (["bash", "unified-exec"].includes(String(input.tool_name).toLowerCase())) {
13388
13535
  const command = record2(input.tool_input) ? input.tool_input.command : undefined;
13389
- const route = typeof command === "string" ? shellRouteIntent(command) : null;
13536
+ const route = typeof command === "string" ? shouldDenyShellRoute(input.cwd, command) : null;
13390
13537
  if (route)
13391
13538
  return denied("PreToolUse", `direct branch or PR creation bypasses the Workit route; ${route.guidance}`);
13392
13539
  }