@brainervirus/workit-cursor 1.2.0 → 1.2.1

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.
@@ -2,7 +2,7 @@
2
2
  "name": "workit",
3
3
  "displayName": "Workit",
4
4
  "description": "Adaptive Workit workflow with shared MCP tools and native Cursor hooks",
5
- "version": "1.2.0",
5
+ "version": "1.2.1",
6
6
  "author": {
7
7
  "name": "Cristhofer Pincetti"
8
8
  },
@@ -7572,7 +7572,7 @@ var SCHEMA_VERSION = 1, POLICY_VERSION = "1.0.0", OPERATION_FAMILIES, uuidPatter
7572
7572
  }, rewriteRecordRefs = (value, schema, map) => {
7573
7573
  const rewritten = rewriteNode(value, schema, map);
7574
7574
  return rewritten.drop ? null : rewritten.value;
7575
- }, provenanceSchema, entrySchema = (data) => object({ id, recordedAt: utc, provenance: provenanceSchema, data }).strict(), factSchema, signalSchema, signalSet, consequenceSchema, verificationSchema, assessmentSchema, constraintSchema, intentSchema, progressSchema, dimensionSchema, policySchema, requirementSchema, policyChangeSchema, candidateSchema, evidenceSchema, evidenceEvaluationSchema, decisionSchema, findingSchema, assignmentSchema, workerReportSchema, workerSchema, ownerSchema, closureSchema, actionProgressSchema, actionProgressListSchema, runtimeSchema, taskRecordSchema, workspaceRecordSchema, capabilitySchema, requirementEvaluationSchema, taskViewSchema, taskSummarySchema, exportBundleSchema, callerSchema, base, revisions, taskId, operation = (shape) => object({ ...base, ...shape }).strict(), taskOperations, policyOperations, evidenceOperations, findingOperations, decisionOperations, workerOperations, writerOperations, stateOperations, operationSchemas, compiledOperationSchemas, success = (revisionValue, workspaceRevisionValue, data) => ({
7575
+ }, provenanceSchema, entrySchema = (data) => object({ id, recordedAt: utc, provenance: provenanceSchema, data }).strict(), factSchema, signalSchema, signalSet, consequenceSchema, verificationSchema, assessmentSchema, constraintSchema, intentSchema, progressSchema, dimensionSchema, policySchema, requirementSchema, policyChangeSchema, candidateSchema, evidenceSchema, evidenceEvaluationSchema, decisionSchema, findingSchema, assignmentSchema, workerReportSchema, workerSchema, ownerSchema, closureSchema, actionProgressSchema, actionProgressListSchema, runtimeSchema, taskRecordSchema, workspaceRecordSchema, capabilitySchema, requirementEvaluationSchema, taskViewSchema, taskSummarySchema, taskListItemSchema, exportBundleSchema, callerSchema, base, revisions, taskId, operation = (shape) => object({ ...base, ...shape }).strict(), taskOperations, policyOperations, evidenceOperations, findingOperations, decisionOperations, workerOperations, writerOperations, stateOperations, operationSchemas, compiledOperationSchemas, success = (revisionValue, workspaceRevisionValue, data) => ({
7576
7576
  ok: true,
7577
7577
  schemaVersion: 1,
7578
7578
  revision: revisionValue,
@@ -7923,6 +7923,7 @@ var init_task_contract = __esm(() => {
7923
7923
  assignment: assignmentSchema,
7924
7924
  state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
7925
7925
  session: refSchema.nullable(),
7926
+ coordinator: refSchema.optional(),
7926
7927
  report: workerReportSchema.nullable()
7927
7928
  }).strict();
7928
7929
  ownerSchema = object({
@@ -8034,6 +8035,7 @@ var init_task_contract = __esm(() => {
8034
8035
  requirements: array(requirementEvaluationSchema),
8035
8036
  writer: workspaceRecordSchema.shape.writer
8036
8037
  }).strict();
8038
+ taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
8037
8039
  exportBundleSchema = object({
8038
8040
  schemaVersion: literal(1),
8039
8041
  exportedAt: utc,
@@ -8054,11 +8056,15 @@ var init_task_contract = __esm(() => {
8054
8056
  expectedWorkspaceRevision: revision.nullable().optional(),
8055
8057
  intent: intentSchema
8056
8058
  }),
8057
- list: operation({ action: literal("list") }),
8059
+ list: operation({
8060
+ action: literal("list"),
8061
+ status: _enum(["open", "closed", "all"]).optional(),
8062
+ limit: number2().int().min(1).max(50).optional()
8063
+ }),
8058
8064
  inspect: operation({
8059
8065
  action: literal("inspect"),
8060
8066
  ...taskId,
8061
- view: _enum(["summary", "full"])
8067
+ view: _enum(["summary", "full"]).optional()
8062
8068
  }),
8063
8069
  revise: operation({
8064
8070
  action: literal("revise"),
@@ -8086,7 +8092,7 @@ var init_task_contract = __esm(() => {
8086
8092
  ...taskId,
8087
8093
  ...revisions,
8088
8094
  expectedWorkspaceRevision: revision.optional(),
8089
- authorityRefs: array(refSchema)
8095
+ authorityRefs: array(refSchema).optional()
8090
8096
  }),
8091
8097
  close: operation({
8092
8098
  action: literal("close"),
@@ -9795,7 +9801,10 @@ var same = (left, right) => {
9795
9801
  }, UNCERTAIN_WORKER_STATES, isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state), workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state), callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor }, validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
9796
9802
  const code = char.charCodeAt(0);
9797
9803
  return code < 32 || code === 127;
9798
- }) && !value.split(/[\\/]/).includes(".."), callerValue = (value) => ({ host: value.host, actor: value.actor }), workerIdOf = (value) => value.workerId ?? null;
9804
+ }) && !value.split(/[\\/]/).includes(".."), callerValue = (value) => ({ host: value.host, actor: value.actor }), workerIdOf = (value) => value.workerId ?? null, currentWriterOwnsTask = (task, workspace, caller) => {
9805
+ const owner = workspace.writer?.owner;
9806
+ return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
9807
+ };
9799
9808
  var init_workers = __esm(() => {
9800
9809
  init_task_contract();
9801
9810
  UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
@@ -10100,9 +10109,11 @@ var AUTO_CLASSES, OPERATION_CLASS, operationAutoClass = (operation) => typeof op
10100
10109
  }, verifyStandingApproval = (root, task, caller, binding) => {
10101
10110
  if (task.status !== "active")
10102
10111
  return failure("invalid_transition", "only active tasks can authorize actions");
10103
- const session = task.intent?.provenance?.session;
10104
- if (!session || session.kind !== "host" || session.host !== caller.host || session.handle !== caller.actor)
10105
- return failure("permission_denied", "standing approval requires the lead session");
10112
+ const workspace = new TaskStore(root).readWorkspace();
10113
+ if (!workspace.ok)
10114
+ return workspace;
10115
+ if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
10116
+ return failure("permission_denied", "standing approval requires the current writer session");
10106
10117
  const standing = binding.standing;
10107
10118
  if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
10108
10119
  return failure("invalid_input", "standing approval needs a workspace and class");
@@ -10141,6 +10152,8 @@ var init_auto_approval = __esm(() => {
10141
10152
  init_config();
10142
10153
  init_branch();
10143
10154
  init_task_contract();
10155
+ init_task_store();
10156
+ init_workers();
10144
10157
  AUTO_CLASSES = ["branch", "commit", "push", "pr", "merge"];
10145
10158
  OPERATION_CLASS = {
10146
10159
  "git.branch_setup": "branch",
@@ -10289,7 +10302,7 @@ function settleAction(input) {
10289
10302
  return failure("not_found", "decision not found");
10290
10303
  if (!authorityDecisionMatches(authority, entry.data))
10291
10304
  return failure("permission_denied", "native settlement authority does not match decision");
10292
- if (!provenanceMatches(entry.provenance, authority.provenance))
10305
+ if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
10293
10306
  return failure("permission_denied", "native settlement caller does not match approval");
10294
10307
  if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
10295
10308
  return failure("permission_denied", "decision binding is invalid");
@@ -10388,7 +10401,7 @@ function reconcileAction(input) {
10388
10401
  const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
10389
10402
  if (!entry || entry.data.purpose !== "action")
10390
10403
  return failure("not_found", "decision not found");
10391
- if (!authorityDecisionMatches(authority, entry.data) || !provenanceMatches(entry.provenance, authority.provenance))
10404
+ if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
10392
10405
  return failure("permission_denied", "native reconciliation caller does not match approval");
10393
10406
  if (entry.data.response !== "approved" || entry.data.revoked !== null || entry.data.digest !== decisionDigest(entry.data) || entry.data.binding.taskId !== current.id || entry.data.binding.workspaceId !== current.workspaceId || entry.data.binding.workspaceId !== workspace.data.id)
10394
10407
  return failure("permission_denied", "reconciliation decision binding is invalid");
@@ -10607,7 +10620,20 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
10607
10620
  }
10608
10621
  }
10609
10622
  return success(null, null, null);
10610
- }, verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding), storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok), validateAction = (store, task, workspaceId, input, authority) => {
10623
+ }, verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding), storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok), approvalAuthorityMatches = (store, task, approval, current) => {
10624
+ if (approval.kind !== "host_observed" || approval.receipts.length === 0)
10625
+ return false;
10626
+ if (provenanceMatches(approval, current))
10627
+ return true;
10628
+ const session = current.kind === "host_observed" ? current.session : null;
10629
+ if (!session || session.kind !== "host")
10630
+ return false;
10631
+ const workspace = store.readWorkspace();
10632
+ return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
10633
+ host: session.host,
10634
+ actor: session.handle
10635
+ });
10636
+ }, validateAction = (store, task, workspaceId, input, authority) => {
10611
10637
  if (task.status !== "active")
10612
10638
  return failure("invalid_transition", "only active tasks can authorize actions");
10613
10639
  if (!refSchema.safeParse(input.actionRef).success)
@@ -10632,7 +10658,7 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
10632
10658
  return failure("permission_denied", "native reservation authority does not match decision");
10633
10659
  if (decision.response !== "approved")
10634
10660
  return failure("permission_denied", "decision was rejected");
10635
- if (entry.provenance.kind !== "host_observed" || entry.provenance.receipts.length === 0 || !provenanceMatches(entry.provenance, authority.provenance))
10661
+ if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
10636
10662
  return failure("permission_denied", "action approval lacks native receipt assurance");
10637
10663
  const receipts = entry.provenance.receipts;
10638
10664
  const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
@@ -10673,6 +10699,7 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
10673
10699
  var init_authority = __esm(() => {
10674
10700
  init_auto_approval();
10675
10701
  init_task_contract();
10702
+ init_workers();
10676
10703
  verifiedAuthorities = new WeakMap;
10677
10704
  bindingCovers = scopeCovers;
10678
10705
  verifyDecisionContent = verifyContentRefs;
@@ -11124,7 +11151,7 @@ var digestBytes3 = (value) => createHash5("sha256").update(value).digest("hex"),
11124
11151
  beforeCandidateId: evidence.beforeCandidateId ?? fallback,
11125
11152
  candidateId: evidence.candidateId ?? fallback
11126
11153
  };
11127
- }, findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId === findingCandidateId), 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"))), 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 })), evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
11154
+ }, findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null), 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"))), 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 })), evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
11128
11155
  if (kind !== "passed")
11129
11156
  return false;
11130
11157
  if (dimension === "testing" || dimension === "verification")
@@ -11367,11 +11394,16 @@ var init_route_intent = __esm(() => {
11367
11394
  var init_runtime_identity = () => {};
11368
11395
 
11369
11396
  // packages/workit-core/src/core/external-action.ts
11370
- var externalActionSchema;
11397
+ var chainStepSchema, externalActionSchema;
11371
11398
  var init_external_action = __esm(() => {
11372
11399
  init_task_contract();
11373
11400
  init_authority();
11374
11401
  init_zod();
11402
+ chainStepSchema = union([
11403
+ string2().min(1),
11404
+ object({ branch: string2().min(1) }).strict(),
11405
+ object({ pr: literal(true) }).strict()
11406
+ ]);
11375
11407
  externalActionSchema = discriminatedUnion("operation", [
11376
11408
  object({
11377
11409
  operation: literal("git.branch_setup"),
@@ -11386,7 +11418,7 @@ var init_external_action = __esm(() => {
11386
11418
  operation: literal("git.commit"),
11387
11419
  payload: object({
11388
11420
  message: string2().min(1).optional(),
11389
- plan_steps: array(string2().min(1)).optional(),
11421
+ plan_steps: array(chainStepSchema).min(1).max(32).optional(),
11390
11422
  plan_branch: string2().min(1).optional()
11391
11423
  }).strict()
11392
11424
  }).strict(),
@@ -11990,14 +12022,30 @@ class WorkitCore {
11990
12022
  const listed = this.store.listTasks();
11991
12023
  if (!listed.ok)
11992
12024
  return listed;
11993
- const summaries = [];
11994
- for (const task of listed.data) {
11995
- const summary = this.summary(task);
11996
- if (!summary.ok)
11997
- return summary;
11998
- summaries.push(summary.data);
11999
- }
12000
- return success(null, null, summaries);
12025
+ const status = input.status ?? "open";
12026
+ const limit = input.limit ?? 20;
12027
+ const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
12028
+ if (selected.length === 0)
12029
+ return success(null, null, []);
12030
+ const workspace = this.store.readWorkspace();
12031
+ if (!workspace.ok)
12032
+ return workspace;
12033
+ if (!workspace.data)
12034
+ return failure("not_found", "workspace not found");
12035
+ const tasks = selected.map((task) => ({
12036
+ id: task.id,
12037
+ revision: task.revision,
12038
+ workspaceRevision: workspace.data.revision,
12039
+ createdAt: task.createdAt,
12040
+ updatedAt: task.updatedAt,
12041
+ runtime: task.runtime ?? null,
12042
+ objective: task.intent.data.objective,
12043
+ status: task.status,
12044
+ closure: task.closure,
12045
+ progress: task.progress,
12046
+ writer: task.status === "closed" ? null : workspace.data.writer
12047
+ }));
12048
+ return success(null, null, tasks);
12001
12049
  }
12002
12050
  case "inspect": {
12003
12051
  const task = this.store.readTask(input.taskId);
@@ -12006,7 +12054,7 @@ class WorkitCore {
12006
12054
  const helper = this.helperTaskGuard(task.data, true);
12007
12055
  if (!helper.ok)
12008
12056
  return helper;
12009
- if (input.view === "summary")
12057
+ if ((input.view ?? "summary") === "summary")
12010
12058
  return this.summary(task.data);
12011
12059
  return this.view(task.data);
12012
12060
  }
@@ -12350,7 +12398,7 @@ class WorkitCore {
12350
12398
  const evaluations = evaluateEvidence(task.data, current.data);
12351
12399
  const verified = evidence.some((entry) => {
12352
12400
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
12353
- return findingVerificationPasses(finding.data.candidateId, {
12401
+ return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
12354
12402
  kind: entry.data.kind,
12355
12403
  candidateId: entry.data.candidateId,
12356
12404
  status: evaluation?.status ?? "missing"
@@ -12430,7 +12478,18 @@ class WorkitCore {
12430
12478
  if (task.data.status !== "active")
12431
12479
  return failure("invalid_transition", "paused or closed tasks cannot assign workers");
12432
12480
  if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
12433
- return failure("permission_denied", "worker assignment is outside task scope");
12481
+ return failure("permission_denied", "worker assignment is outside task scope", {
12482
+ fields: [
12483
+ {
12484
+ path: "assignment.scope",
12485
+ reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
12486
+ },
12487
+ {
12488
+ path: "task.intent.scope",
12489
+ reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
12490
+ }
12491
+ ]
12492
+ });
12434
12493
  if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
12435
12494
  return failure("invalid_input", "worker assignment references an unknown decision");
12436
12495
  if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
@@ -12637,7 +12696,15 @@ class WorkitCore {
12637
12696
  ...item,
12638
12697
  recordedAt: mutation.now,
12639
12698
  provenance: attested.data,
12640
- data: { ...item.data, state: "dispatching" }
12699
+ data: {
12700
+ ...item.data,
12701
+ state: "dispatching",
12702
+ coordinator: {
12703
+ kind: "host",
12704
+ host: this.context.caller.host,
12705
+ handle: this.context.caller.actor
12706
+ }
12707
+ }
12641
12708
  } : item)
12642
12709
  });
12643
12710
  }
@@ -12713,6 +12780,7 @@ class WorkitCore {
12713
12780
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12714
12781
  state: "stopped",
12715
12782
  session: null,
12783
+ report: null,
12716
12784
  provenance: attested.data,
12717
12785
  owner: this.authorityOwner,
12718
12786
  store: this.store,
@@ -13020,10 +13088,12 @@ class WorkitCore {
13020
13088
  return workspace;
13021
13089
  if (!workspace.data)
13022
13090
  return failure("not_found", "workspace not found");
13023
- const current = captureCandidate(this.store.root, task.intent.data.scope, environment());
13091
+ const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
13092
+ const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
13024
13093
  if (!current.ok)
13025
13094
  return current;
13026
- const requirements = evaluateRequirements(task, workspace.data, this.context.capabilities, current.data, this.store.root, this.context.caller);
13095
+ const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
13096
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
13027
13097
  return success(task.revision, workspace.data.revision, {
13028
13098
  id: task.id,
13029
13099
  revision: task.revision,
@@ -13037,7 +13107,7 @@ class WorkitCore {
13037
13107
  progress: task.progress,
13038
13108
  policy: task.policy,
13039
13109
  requirements,
13040
- writer: workspace.data.writer
13110
+ writer: evaluationWorkspace.writer
13041
13111
  });
13042
13112
  }
13043
13113
  view(task) {
@@ -13046,13 +13116,15 @@ class WorkitCore {
13046
13116
  return workspace;
13047
13117
  if (!workspace.data)
13048
13118
  return failure("not_found", "workspace not found");
13049
- const current = captureCandidate(this.store.root, task.intent.data.scope, environment());
13119
+ const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
13120
+ const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
13050
13121
  if (!current.ok)
13051
13122
  return current;
13052
- const requirements = evaluateRequirements(task, workspace.data, this.context.capabilities, current.data, this.store.root, this.context.caller);
13123
+ const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
13124
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
13053
13125
  return success(task.revision, workspace.data.revision, {
13054
13126
  task,
13055
- workspace: workspace.data,
13127
+ workspace: evaluationWorkspace,
13056
13128
  capabilities: this.context.capabilities,
13057
13129
  requirements,
13058
13130
  evidence: evaluateEvidence(task, current.data)
@@ -13143,8 +13215,6 @@ class WorkitCore {
13143
13215
  return helper;
13144
13216
  if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
13145
13217
  return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
13146
- if (status === "active" && input.authorityRefs.length === 0)
13147
- return failure("permission_denied", "resuming a task requires authority references");
13148
13218
  const workspace = this.store.readWorkspace();
13149
13219
  if (!workspace.ok)
13150
13220
  return workspace;
@@ -13174,7 +13244,7 @@ class WorkitCore {
13174
13244
  return failure("needs_input", "imported task requires fresh destination evidence");
13175
13245
  if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
13176
13246
  return failure("recovery_required", "imported task requires resume reconciliation");
13177
- if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
13247
+ if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
13178
13248
  return failure("permission_denied", "imported resume requires native destination approval");
13179
13249
  resumeCandidate = current.data.candidate;
13180
13250
  }
@@ -13304,7 +13374,7 @@ class WorkitCore {
13304
13374
  task: (value, context) => success(context.revision, null, {
13305
13375
  ...value,
13306
13376
  status: "closed",
13307
- candidates: value.candidates.some((item) => item.id === withCandidate.id) ? value.candidates : [...value.candidates, withCandidate],
13377
+ candidates: taskForView.candidates,
13308
13378
  closure: {
13309
13379
  outcome: closure.data.outcome,
13310
13380
  at: context.now,
@@ -13381,6 +13451,7 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
13381
13451
  expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
13382
13452
  state: expected.state,
13383
13453
  session: expected.session,
13454
+ report: expected.report ?? null,
13384
13455
  provenance: result.data,
13385
13456
  ...binding
13386
13457
  });
@@ -13390,7 +13461,7 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
13390
13461
  verifiedWorkers.delete(token);
13391
13462
  if (!value)
13392
13463
  return null;
13393
- if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session))
13464
+ if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session) || !sameValue(value.report, expected.report ?? null))
13394
13465
  return null;
13395
13466
  return value;
13396
13467
  }, applyWorkerLifecycle = (input) => {
@@ -13430,6 +13501,8 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
13430
13501
  return failure("invalid_transition", "worker already has an observed session");
13431
13502
  if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
13432
13503
  return failure("invalid_transition", "worker launch was not claimed");
13504
+ if (input.report && input.state !== "stopped")
13505
+ return failure("invalid_input", "worker reports require a stopped worker");
13433
13506
  if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
13434
13507
  return failure("invalid_input", "running worker observations require a session");
13435
13508
  if (entry.data.session && !sameValue(entry.data.session, input.session))
@@ -13446,12 +13519,13 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
13446
13519
  data: {
13447
13520
  ...entry.data,
13448
13521
  state: terminalCancel ? "cancelling" : input.state,
13449
- session: input.session
13522
+ session: input.session,
13523
+ report: entry.data.report ?? input.report ?? null
13450
13524
  }
13451
13525
  };
13452
13526
  const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
13453
13527
  const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
13454
- if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
13528
+ if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
13455
13529
  return success(task.data.revision, workspace.data.revision, entry);
13456
13530
  const changed = input.store.mutateTaskAndWorkspace({
13457
13531
  taskId: task.data.id,
@@ -20404,6 +20404,7 @@ var workerSchema = object4({
20404
20404
  assignment: assignmentSchema,
20405
20405
  state: _enum2(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
20406
20406
  session: refSchema.nullable(),
20407
+ coordinator: refSchema.optional(),
20407
20408
  report: workerReportSchema.nullable()
20408
20409
  }).strict();
20409
20410
  var ownerSchema = object4({
@@ -20515,6 +20516,7 @@ var taskSummarySchema = object4({
20515
20516
  requirements: array3(requirementEvaluationSchema),
20516
20517
  writer: workspaceRecordSchema.shape.writer
20517
20518
  }).strict();
20519
+ var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
20518
20520
  var exportBundleSchema = object4({
20519
20521
  schemaVersion: literal3(1),
20520
20522
  exportedAt: utc,
@@ -20536,11 +20538,15 @@ var taskOperations = {
20536
20538
  expectedWorkspaceRevision: revision.nullable().optional(),
20537
20539
  intent: intentSchema
20538
20540
  }),
20539
- list: operation({ action: literal3("list") }),
20541
+ list: operation({
20542
+ action: literal3("list"),
20543
+ status: _enum2(["open", "closed", "all"]).optional(),
20544
+ limit: number5().int().min(1).max(50).optional()
20545
+ }),
20540
20546
  inspect: operation({
20541
20547
  action: literal3("inspect"),
20542
20548
  ...taskId,
20543
- view: _enum2(["summary", "full"])
20549
+ view: _enum2(["summary", "full"]).optional()
20544
20550
  }),
20545
20551
  revise: operation({
20546
20552
  action: literal3("revise"),
@@ -20568,7 +20574,7 @@ var taskOperations = {
20568
20574
  ...taskId,
20569
20575
  ...revisions,
20570
20576
  expectedWorkspaceRevision: revision.optional(),
20571
- authorityRefs: array3(refSchema)
20577
+ authorityRefs: array3(refSchema).optional()
20572
20578
  }),
20573
20579
  close: operation({
20574
20580
  action: literal3("close"),
@@ -22401,6 +22407,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
22401
22407
  }) && !value.split(/[\\/]/).includes("..");
22402
22408
  var callerValue = (value) => ({ host: value.host, actor: value.actor });
22403
22409
  var workerIdOf = (value) => value.workerId ?? null;
22410
+ var currentWriterOwnsTask = (task, workspace, caller) => {
22411
+ const owner = workspace.writer?.owner;
22412
+ return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
22413
+ };
22404
22414
  function assertProductWriteAllowed(input) {
22405
22415
  if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
22406
22416
  return failure("invalid_input", "invalid product write path");
@@ -23117,9 +23127,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
23117
23127
  var verifyStandingApproval = (root, task, caller, binding) => {
23118
23128
  if (task.status !== "active")
23119
23129
  return failure("invalid_transition", "only active tasks can authorize actions");
23120
- const session = task.intent?.provenance?.session;
23121
- if (!session || session.kind !== "host" || session.host !== caller.host || session.handle !== caller.actor)
23122
- return failure("permission_denied", "standing approval requires the lead session");
23130
+ const workspace = new TaskStore(root).readWorkspace();
23131
+ if (!workspace.ok)
23132
+ return workspace;
23133
+ if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
23134
+ return failure("permission_denied", "standing approval requires the current writer session");
23123
23135
  const standing = binding.standing;
23124
23136
  if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
23125
23137
  return failure("invalid_input", "standing approval needs a workspace and class");
@@ -23356,6 +23368,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
23356
23368
  };
23357
23369
  var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
23358
23370
  var storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok);
23371
+ var approvalAuthorityMatches = (store, task, approval, current) => {
23372
+ if (approval.kind !== "host_observed" || approval.receipts.length === 0)
23373
+ return false;
23374
+ if (provenanceMatches(approval, current))
23375
+ return true;
23376
+ const session = current.kind === "host_observed" ? current.session : null;
23377
+ if (!session || session.kind !== "host")
23378
+ return false;
23379
+ const workspace = store.readWorkspace();
23380
+ return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
23381
+ host: session.host,
23382
+ actor: session.handle
23383
+ });
23384
+ };
23359
23385
  var validateAction = (store, task, workspaceId, input, authority) => {
23360
23386
  if (task.status !== "active")
23361
23387
  return failure("invalid_transition", "only active tasks can authorize actions");
@@ -23381,7 +23407,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
23381
23407
  return failure("permission_denied", "native reservation authority does not match decision");
23382
23408
  if (decision.response !== "approved")
23383
23409
  return failure("permission_denied", "decision was rejected");
23384
- if (entry.provenance.kind !== "host_observed" || entry.provenance.receipts.length === 0 || !provenanceMatches(entry.provenance, authority.provenance))
23410
+ if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
23385
23411
  return failure("permission_denied", "action approval lacks native receipt assurance");
23386
23412
  const receipts = entry.provenance.receipts;
23387
23413
  const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
@@ -23548,7 +23574,7 @@ function settleAction(input) {
23548
23574
  return failure("not_found", "decision not found");
23549
23575
  if (!authorityDecisionMatches(authority, entry.data))
23550
23576
  return failure("permission_denied", "native settlement authority does not match decision");
23551
- if (!provenanceMatches(entry.provenance, authority.provenance))
23577
+ if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
23552
23578
  return failure("permission_denied", "native settlement caller does not match approval");
23553
23579
  if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
23554
23580
  return failure("permission_denied", "decision binding is invalid");
@@ -23647,7 +23673,7 @@ function reconcileAction(input) {
23647
23673
  const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
23648
23674
  if (!entry || entry.data.purpose !== "action")
23649
23675
  return failure("not_found", "decision not found");
23650
- if (!authorityDecisionMatches(authority, entry.data) || !provenanceMatches(entry.provenance, authority.provenance))
23676
+ if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
23651
23677
  return failure("permission_denied", "native reconciliation caller does not match approval");
23652
23678
  if (entry.data.response !== "approved" || entry.data.revoked !== null || entry.data.digest !== decisionDigest(entry.data) || entry.data.binding.taskId !== current.id || entry.data.binding.workspaceId !== current.workspaceId || entry.data.binding.workspaceId !== workspace.data.id)
23653
23679
  return failure("permission_denied", "reconciliation decision binding is invalid");
@@ -23984,7 +24010,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
23984
24010
  candidateId: evidence.candidateId ?? fallback
23985
24011
  };
23986
24012
  };
23987
- var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId === findingCandidateId);
24013
+ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
23988
24014
  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")));
23989
24015
  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 }));
23990
24016
  var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
@@ -24219,6 +24245,11 @@ var exportDigest = (bundle) => sha256({
24219
24245
  task: bundle.task
24220
24246
  });
24221
24247
  // packages/workit-core/src/core/external-action.ts
24248
+ var chainStepSchema = union3([
24249
+ string5().min(1),
24250
+ object4({ branch: string5().min(1) }).strict(),
24251
+ object4({ pr: literal3(true) }).strict()
24252
+ ]);
24222
24253
  var externalActionSchema = discriminatedUnion2("operation", [
24223
24254
  object4({
24224
24255
  operation: literal3("git.branch_setup"),
@@ -24233,7 +24264,7 @@ var externalActionSchema = discriminatedUnion2("operation", [
24233
24264
  operation: literal3("git.commit"),
24234
24265
  payload: object4({
24235
24266
  message: string5().min(1).optional(),
24236
- plan_steps: array3(string5().min(1)).optional(),
24267
+ plan_steps: array3(chainStepSchema).min(1).max(32).optional(),
24237
24268
  plan_branch: string5().min(1).optional()
24238
24269
  }).strict()
24239
24270
  }).strict(),
@@ -24719,6 +24750,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
24719
24750
  expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
24720
24751
  state: expected.state,
24721
24752
  session: expected.session,
24753
+ report: expected.report ?? null,
24722
24754
  provenance: result.data,
24723
24755
  ...binding
24724
24756
  });
@@ -24729,7 +24761,7 @@ var takeWorker = (token, binding, expected) => {
24729
24761
  verifiedWorkers.delete(token);
24730
24762
  if (!value)
24731
24763
  return null;
24732
- if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session))
24764
+ if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session) || !sameValue(value.report, expected.report ?? null))
24733
24765
  return null;
24734
24766
  return value;
24735
24767
  };
@@ -24770,6 +24802,8 @@ var applyWorkerLifecycle = (input) => {
24770
24802
  return failure("invalid_transition", "worker already has an observed session");
24771
24803
  if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
24772
24804
  return failure("invalid_transition", "worker launch was not claimed");
24805
+ if (input.report && input.state !== "stopped")
24806
+ return failure("invalid_input", "worker reports require a stopped worker");
24773
24807
  if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
24774
24808
  return failure("invalid_input", "running worker observations require a session");
24775
24809
  if (entry.data.session && !sameValue(entry.data.session, input.session))
@@ -24786,12 +24820,13 @@ var applyWorkerLifecycle = (input) => {
24786
24820
  data: {
24787
24821
  ...entry.data,
24788
24822
  state: terminalCancel ? "cancelling" : input.state,
24789
- session: input.session
24823
+ session: input.session,
24824
+ report: entry.data.report ?? input.report ?? null
24790
24825
  }
24791
24826
  };
24792
24827
  const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
24793
24828
  const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
24794
- if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
24829
+ if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
24795
24830
  return success(task.data.revision, workspace.data.revision, entry);
24796
24831
  const changed = input.store.mutateTaskAndWorkspace({
24797
24832
  taskId: task.data.id,
@@ -25209,14 +25244,30 @@ class WorkitCore {
25209
25244
  const listed = this.store.listTasks();
25210
25245
  if (!listed.ok)
25211
25246
  return listed;
25212
- const summaries = [];
25213
- for (const task of listed.data) {
25214
- const summary = this.summary(task);
25215
- if (!summary.ok)
25216
- return summary;
25217
- summaries.push(summary.data);
25218
- }
25219
- return success(null, null, summaries);
25247
+ const status = input.status ?? "open";
25248
+ const limit = input.limit ?? 20;
25249
+ const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
25250
+ if (selected.length === 0)
25251
+ return success(null, null, []);
25252
+ const workspace = this.store.readWorkspace();
25253
+ if (!workspace.ok)
25254
+ return workspace;
25255
+ if (!workspace.data)
25256
+ return failure("not_found", "workspace not found");
25257
+ const tasks = selected.map((task) => ({
25258
+ id: task.id,
25259
+ revision: task.revision,
25260
+ workspaceRevision: workspace.data.revision,
25261
+ createdAt: task.createdAt,
25262
+ updatedAt: task.updatedAt,
25263
+ runtime: task.runtime ?? null,
25264
+ objective: task.intent.data.objective,
25265
+ status: task.status,
25266
+ closure: task.closure,
25267
+ progress: task.progress,
25268
+ writer: task.status === "closed" ? null : workspace.data.writer
25269
+ }));
25270
+ return success(null, null, tasks);
25220
25271
  }
25221
25272
  case "inspect": {
25222
25273
  const task = this.store.readTask(input.taskId);
@@ -25225,7 +25276,7 @@ class WorkitCore {
25225
25276
  const helper = this.helperTaskGuard(task.data, true);
25226
25277
  if (!helper.ok)
25227
25278
  return helper;
25228
- if (input.view === "summary")
25279
+ if ((input.view ?? "summary") === "summary")
25229
25280
  return this.summary(task.data);
25230
25281
  return this.view(task.data);
25231
25282
  }
@@ -25569,7 +25620,7 @@ class WorkitCore {
25569
25620
  const evaluations = evaluateEvidence(task.data, current.data);
25570
25621
  const verified = evidence.some((entry) => {
25571
25622
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
25572
- return findingVerificationPasses(finding.data.candidateId, {
25623
+ return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
25573
25624
  kind: entry.data.kind,
25574
25625
  candidateId: entry.data.candidateId,
25575
25626
  status: evaluation?.status ?? "missing"
@@ -25649,7 +25700,18 @@ class WorkitCore {
25649
25700
  if (task.data.status !== "active")
25650
25701
  return failure("invalid_transition", "paused or closed tasks cannot assign workers");
25651
25702
  if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
25652
- return failure("permission_denied", "worker assignment is outside task scope");
25703
+ return failure("permission_denied", "worker assignment is outside task scope", {
25704
+ fields: [
25705
+ {
25706
+ path: "assignment.scope",
25707
+ reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
25708
+ },
25709
+ {
25710
+ path: "task.intent.scope",
25711
+ reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
25712
+ }
25713
+ ]
25714
+ });
25653
25715
  if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
25654
25716
  return failure("invalid_input", "worker assignment references an unknown decision");
25655
25717
  if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
@@ -25856,7 +25918,15 @@ class WorkitCore {
25856
25918
  ...item,
25857
25919
  recordedAt: mutation.now,
25858
25920
  provenance: attested.data,
25859
- data: { ...item.data, state: "dispatching" }
25921
+ data: {
25922
+ ...item.data,
25923
+ state: "dispatching",
25924
+ coordinator: {
25925
+ kind: "host",
25926
+ host: this.context.caller.host,
25927
+ handle: this.context.caller.actor
25928
+ }
25929
+ }
25860
25930
  } : item)
25861
25931
  });
25862
25932
  }
@@ -25932,6 +26002,7 @@ class WorkitCore {
25932
26002
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
25933
26003
  state: "stopped",
25934
26004
  session: null,
26005
+ report: null,
25935
26006
  provenance: attested.data,
25936
26007
  owner: this.authorityOwner,
25937
26008
  store: this.store,
@@ -26239,10 +26310,12 @@ class WorkitCore {
26239
26310
  return workspace;
26240
26311
  if (!workspace.data)
26241
26312
  return failure("not_found", "workspace not found");
26242
- const current = captureCandidate(this.store.root, task.intent.data.scope, environment());
26313
+ const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
26314
+ const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
26243
26315
  if (!current.ok)
26244
26316
  return current;
26245
- const requirements = evaluateRequirements(task, workspace.data, this.context.capabilities, current.data, this.store.root, this.context.caller);
26317
+ const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
26318
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
26246
26319
  return success(task.revision, workspace.data.revision, {
26247
26320
  id: task.id,
26248
26321
  revision: task.revision,
@@ -26256,7 +26329,7 @@ class WorkitCore {
26256
26329
  progress: task.progress,
26257
26330
  policy: task.policy,
26258
26331
  requirements,
26259
- writer: workspace.data.writer
26332
+ writer: evaluationWorkspace.writer
26260
26333
  });
26261
26334
  }
26262
26335
  view(task) {
@@ -26265,13 +26338,15 @@ class WorkitCore {
26265
26338
  return workspace;
26266
26339
  if (!workspace.data)
26267
26340
  return failure("not_found", "workspace not found");
26268
- const current = captureCandidate(this.store.root, task.intent.data.scope, environment());
26341
+ const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
26342
+ const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
26269
26343
  if (!current.ok)
26270
26344
  return current;
26271
- const requirements = evaluateRequirements(task, workspace.data, this.context.capabilities, current.data, this.store.root, this.context.caller);
26345
+ const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
26346
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
26272
26347
  return success(task.revision, workspace.data.revision, {
26273
26348
  task,
26274
- workspace: workspace.data,
26349
+ workspace: evaluationWorkspace,
26275
26350
  capabilities: this.context.capabilities,
26276
26351
  requirements,
26277
26352
  evidence: evaluateEvidence(task, current.data)
@@ -26362,8 +26437,6 @@ class WorkitCore {
26362
26437
  return helper;
26363
26438
  if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
26364
26439
  return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
26365
- if (status === "active" && input.authorityRefs.length === 0)
26366
- return failure("permission_denied", "resuming a task requires authority references");
26367
26440
  const workspace = this.store.readWorkspace();
26368
26441
  if (!workspace.ok)
26369
26442
  return workspace;
@@ -26393,7 +26466,7 @@ class WorkitCore {
26393
26466
  return failure("needs_input", "imported task requires fresh destination evidence");
26394
26467
  if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
26395
26468
  return failure("recovery_required", "imported task requires resume reconciliation");
26396
- if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
26469
+ if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
26397
26470
  return failure("permission_denied", "imported resume requires native destination approval");
26398
26471
  resumeCandidate = current.data.candidate;
26399
26472
  }
@@ -26523,7 +26596,7 @@ class WorkitCore {
26523
26596
  task: (value, context) => success(context.revision, null, {
26524
26597
  ...value,
26525
26598
  status: "closed",
26526
- candidates: value.candidates.some((item) => item.id === withCandidate.id) ? value.candidates : [...value.candidates, withCandidate],
26599
+ candidates: taskForView.candidates,
26527
26600
  closure: {
26528
26601
  outcome: closure.data.outcome,
26529
26602
  at: context.now,
@@ -7705,6 +7705,7 @@ var workerSchema = object({
7705
7705
  assignment: assignmentSchema,
7706
7706
  state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
7707
7707
  session: refSchema.nullable(),
7708
+ coordinator: refSchema.optional(),
7708
7709
  report: workerReportSchema.nullable()
7709
7710
  }).strict();
7710
7711
  var ownerSchema = object({
@@ -7816,6 +7817,7 @@ var taskSummarySchema = object({
7816
7817
  requirements: array(requirementEvaluationSchema),
7817
7818
  writer: workspaceRecordSchema.shape.writer
7818
7819
  }).strict();
7820
+ var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true });
7819
7821
  var exportBundleSchema = object({
7820
7822
  schemaVersion: literal(1),
7821
7823
  exportedAt: utc,
@@ -7837,11 +7839,15 @@ var taskOperations = {
7837
7839
  expectedWorkspaceRevision: revision.nullable().optional(),
7838
7840
  intent: intentSchema
7839
7841
  }),
7840
- list: operation({ action: literal("list") }),
7842
+ list: operation({
7843
+ action: literal("list"),
7844
+ status: _enum(["open", "closed", "all"]).optional(),
7845
+ limit: number2().int().min(1).max(50).optional()
7846
+ }),
7841
7847
  inspect: operation({
7842
7848
  action: literal("inspect"),
7843
7849
  ...taskId,
7844
- view: _enum(["summary", "full"])
7850
+ view: _enum(["summary", "full"]).optional()
7845
7851
  }),
7846
7852
  revise: operation({
7847
7853
  action: literal("revise"),
@@ -7869,7 +7875,7 @@ var taskOperations = {
7869
7875
  ...taskId,
7870
7876
  ...revisions,
7871
7877
  expectedWorkspaceRevision: revision.optional(),
7872
- authorityRefs: array(refSchema)
7878
+ authorityRefs: array(refSchema).optional()
7873
7879
  }),
7874
7880
  close: operation({
7875
7881
  action: literal("close"),
@@ -9641,6 +9647,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
9641
9647
  }) && !value.split(/[\\/]/).includes("..");
9642
9648
  var callerValue = (value) => ({ host: value.host, actor: value.actor });
9643
9649
  var workerIdOf = (value) => value.workerId ?? null;
9650
+ var currentWriterOwnsTask = (task, workspace, caller) => {
9651
+ const owner = workspace.writer?.owner;
9652
+ return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
9653
+ };
9644
9654
  function assertProductWriteAllowed(input) {
9645
9655
  if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
9646
9656
  return failure("invalid_input", "invalid product write path");
@@ -9998,9 +10008,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
9998
10008
  var verifyStandingApproval = (root, task, caller, binding) => {
9999
10009
  if (task.status !== "active")
10000
10010
  return failure("invalid_transition", "only active tasks can authorize actions");
10001
- const session = task.intent?.provenance?.session;
10002
- if (!session || session.kind !== "host" || session.host !== caller.host || session.handle !== caller.actor)
10003
- return failure("permission_denied", "standing approval requires the lead session");
10011
+ const workspace = new TaskStore(root).readWorkspace();
10012
+ if (!workspace.ok)
10013
+ return workspace;
10014
+ if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
10015
+ return failure("permission_denied", "standing approval requires the current writer session");
10004
10016
  const standing = binding.standing;
10005
10017
  if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
10006
10018
  return failure("invalid_input", "standing approval needs a workspace and class");
@@ -10237,6 +10249,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
10237
10249
  };
10238
10250
  var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
10239
10251
  var storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok);
10252
+ var approvalAuthorityMatches = (store, task, approval, current) => {
10253
+ if (approval.kind !== "host_observed" || approval.receipts.length === 0)
10254
+ return false;
10255
+ if (provenanceMatches(approval, current))
10256
+ return true;
10257
+ const session = current.kind === "host_observed" ? current.session : null;
10258
+ if (!session || session.kind !== "host")
10259
+ return false;
10260
+ const workspace = store.readWorkspace();
10261
+ return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
10262
+ host: session.host,
10263
+ actor: session.handle
10264
+ });
10265
+ };
10240
10266
  var validateAction = (store, task, workspaceId, input, authority) => {
10241
10267
  if (task.status !== "active")
10242
10268
  return failure("invalid_transition", "only active tasks can authorize actions");
@@ -10262,7 +10288,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
10262
10288
  return failure("permission_denied", "native reservation authority does not match decision");
10263
10289
  if (decision.response !== "approved")
10264
10290
  return failure("permission_denied", "decision was rejected");
10265
- if (entry.provenance.kind !== "host_observed" || entry.provenance.receipts.length === 0 || !provenanceMatches(entry.provenance, authority.provenance))
10291
+ if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
10266
10292
  return failure("permission_denied", "action approval lacks native receipt assurance");
10267
10293
  const receipts = entry.provenance.receipts;
10268
10294
  const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
@@ -10429,7 +10455,7 @@ function settleAction(input) {
10429
10455
  return failure("not_found", "decision not found");
10430
10456
  if (!authorityDecisionMatches(authority, entry.data))
10431
10457
  return failure("permission_denied", "native settlement authority does not match decision");
10432
- if (!provenanceMatches(entry.provenance, authority.provenance))
10458
+ if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
10433
10459
  return failure("permission_denied", "native settlement caller does not match approval");
10434
10460
  if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
10435
10461
  return failure("permission_denied", "decision binding is invalid");
@@ -10528,7 +10554,7 @@ function reconcileAction(input) {
10528
10554
  const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
10529
10555
  if (!entry || entry.data.purpose !== "action")
10530
10556
  return failure("not_found", "decision not found");
10531
- if (!authorityDecisionMatches(authority, entry.data) || !provenanceMatches(entry.provenance, authority.provenance))
10557
+ if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
10532
10558
  return failure("permission_denied", "native reconciliation caller does not match approval");
10533
10559
  if (entry.data.response !== "approved" || entry.data.revoked !== null || entry.data.digest !== decisionDigest(entry.data) || entry.data.binding.taskId !== current.id || entry.data.binding.workspaceId !== current.workspaceId || entry.data.binding.workspaceId !== workspace.data.id)
10534
10560
  return failure("permission_denied", "reconciliation decision binding is invalid");
@@ -10865,7 +10891,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
10865
10891
  candidateId: evidence.candidateId ?? fallback
10866
10892
  };
10867
10893
  };
10868
- var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId === findingCandidateId);
10894
+ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
10869
10895
  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")));
10870
10896
  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 }));
10871
10897
  var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
@@ -11271,6 +11297,11 @@ var exportDigest = (bundle) => sha256({
11271
11297
  task: bundle.task
11272
11298
  });
11273
11299
  // packages/workit-core/src/core/external-action.ts
11300
+ var chainStepSchema = union([
11301
+ string2().min(1),
11302
+ object({ branch: string2().min(1) }).strict(),
11303
+ object({ pr: literal(true) }).strict()
11304
+ ]);
11274
11305
  var externalActionSchema = discriminatedUnion("operation", [
11275
11306
  object({
11276
11307
  operation: literal("git.branch_setup"),
@@ -11285,7 +11316,7 @@ var externalActionSchema = discriminatedUnion("operation", [
11285
11316
  operation: literal("git.commit"),
11286
11317
  payload: object({
11287
11318
  message: string2().min(1).optional(),
11288
- plan_steps: array(string2().min(1)).optional(),
11319
+ plan_steps: array(chainStepSchema).min(1).max(32).optional(),
11289
11320
  plan_branch: string2().min(1).optional()
11290
11321
  }).strict()
11291
11322
  }).strict(),
@@ -11771,6 +11802,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
11771
11802
  expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
11772
11803
  state: expected.state,
11773
11804
  session: expected.session,
11805
+ report: expected.report ?? null,
11774
11806
  provenance: result.data,
11775
11807
  ...binding
11776
11808
  });
@@ -11781,7 +11813,7 @@ var takeWorker = (token, binding, expected) => {
11781
11813
  verifiedWorkers.delete(token);
11782
11814
  if (!value)
11783
11815
  return null;
11784
- if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session))
11816
+ if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session) || !sameValue(value.report, expected.report ?? null))
11785
11817
  return null;
11786
11818
  return value;
11787
11819
  };
@@ -11822,6 +11854,8 @@ var applyWorkerLifecycle = (input) => {
11822
11854
  return failure("invalid_transition", "worker already has an observed session");
11823
11855
  if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
11824
11856
  return failure("invalid_transition", "worker launch was not claimed");
11857
+ if (input.report && input.state !== "stopped")
11858
+ return failure("invalid_input", "worker reports require a stopped worker");
11825
11859
  if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
11826
11860
  return failure("invalid_input", "running worker observations require a session");
11827
11861
  if (entry.data.session && !sameValue(entry.data.session, input.session))
@@ -11838,12 +11872,13 @@ var applyWorkerLifecycle = (input) => {
11838
11872
  data: {
11839
11873
  ...entry.data,
11840
11874
  state: terminalCancel ? "cancelling" : input.state,
11841
- session: input.session
11875
+ session: input.session,
11876
+ report: entry.data.report ?? input.report ?? null
11842
11877
  }
11843
11878
  };
11844
11879
  const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
11845
11880
  const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
11846
- if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
11881
+ if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
11847
11882
  return success(task.data.revision, workspace.data.revision, entry);
11848
11883
  const changed = input.store.mutateTaskAndWorkspace({
11849
11884
  taskId: task.data.id,
@@ -12261,14 +12296,30 @@ class WorkitCore {
12261
12296
  const listed = this.store.listTasks();
12262
12297
  if (!listed.ok)
12263
12298
  return listed;
12264
- const summaries = [];
12265
- for (const task of listed.data) {
12266
- const summary = this.summary(task);
12267
- if (!summary.ok)
12268
- return summary;
12269
- summaries.push(summary.data);
12270
- }
12271
- return success(null, null, summaries);
12299
+ const status = input.status ?? "open";
12300
+ const limit = input.limit ?? 20;
12301
+ const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
12302
+ if (selected.length === 0)
12303
+ return success(null, null, []);
12304
+ const workspace = this.store.readWorkspace();
12305
+ if (!workspace.ok)
12306
+ return workspace;
12307
+ if (!workspace.data)
12308
+ return failure("not_found", "workspace not found");
12309
+ const tasks = selected.map((task) => ({
12310
+ id: task.id,
12311
+ revision: task.revision,
12312
+ workspaceRevision: workspace.data.revision,
12313
+ createdAt: task.createdAt,
12314
+ updatedAt: task.updatedAt,
12315
+ runtime: task.runtime ?? null,
12316
+ objective: task.intent.data.objective,
12317
+ status: task.status,
12318
+ closure: task.closure,
12319
+ progress: task.progress,
12320
+ writer: task.status === "closed" ? null : workspace.data.writer
12321
+ }));
12322
+ return success(null, null, tasks);
12272
12323
  }
12273
12324
  case "inspect": {
12274
12325
  const task = this.store.readTask(input.taskId);
@@ -12277,7 +12328,7 @@ class WorkitCore {
12277
12328
  const helper = this.helperTaskGuard(task.data, true);
12278
12329
  if (!helper.ok)
12279
12330
  return helper;
12280
- if (input.view === "summary")
12331
+ if ((input.view ?? "summary") === "summary")
12281
12332
  return this.summary(task.data);
12282
12333
  return this.view(task.data);
12283
12334
  }
@@ -12621,7 +12672,7 @@ class WorkitCore {
12621
12672
  const evaluations = evaluateEvidence(task.data, current.data);
12622
12673
  const verified = evidence.some((entry) => {
12623
12674
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
12624
- return findingVerificationPasses(finding.data.candidateId, {
12675
+ return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
12625
12676
  kind: entry.data.kind,
12626
12677
  candidateId: entry.data.candidateId,
12627
12678
  status: evaluation?.status ?? "missing"
@@ -12701,7 +12752,18 @@ class WorkitCore {
12701
12752
  if (task.data.status !== "active")
12702
12753
  return failure("invalid_transition", "paused or closed tasks cannot assign workers");
12703
12754
  if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
12704
- return failure("permission_denied", "worker assignment is outside task scope");
12755
+ return failure("permission_denied", "worker assignment is outside task scope", {
12756
+ fields: [
12757
+ {
12758
+ path: "assignment.scope",
12759
+ reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
12760
+ },
12761
+ {
12762
+ path: "task.intent.scope",
12763
+ reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
12764
+ }
12765
+ ]
12766
+ });
12705
12767
  if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
12706
12768
  return failure("invalid_input", "worker assignment references an unknown decision");
12707
12769
  if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
@@ -12908,7 +12970,15 @@ class WorkitCore {
12908
12970
  ...item,
12909
12971
  recordedAt: mutation.now,
12910
12972
  provenance: attested.data,
12911
- data: { ...item.data, state: "dispatching" }
12973
+ data: {
12974
+ ...item.data,
12975
+ state: "dispatching",
12976
+ coordinator: {
12977
+ kind: "host",
12978
+ host: this.context.caller.host,
12979
+ handle: this.context.caller.actor
12980
+ }
12981
+ }
12912
12982
  } : item)
12913
12983
  });
12914
12984
  }
@@ -12984,6 +13054,7 @@ class WorkitCore {
12984
13054
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
12985
13055
  state: "stopped",
12986
13056
  session: null,
13057
+ report: null,
12987
13058
  provenance: attested.data,
12988
13059
  owner: this.authorityOwner,
12989
13060
  store: this.store,
@@ -13291,10 +13362,12 @@ class WorkitCore {
13291
13362
  return workspace;
13292
13363
  if (!workspace.data)
13293
13364
  return failure("not_found", "workspace not found");
13294
- const current = captureCandidate(this.store.root, task.intent.data.scope, environment());
13365
+ const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
13366
+ const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
13295
13367
  if (!current.ok)
13296
13368
  return current;
13297
- const requirements = evaluateRequirements(task, workspace.data, this.context.capabilities, current.data, this.store.root, this.context.caller);
13369
+ const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
13370
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
13298
13371
  return success(task.revision, workspace.data.revision, {
13299
13372
  id: task.id,
13300
13373
  revision: task.revision,
@@ -13308,7 +13381,7 @@ class WorkitCore {
13308
13381
  progress: task.progress,
13309
13382
  policy: task.policy,
13310
13383
  requirements,
13311
- writer: workspace.data.writer
13384
+ writer: evaluationWorkspace.writer
13312
13385
  });
13313
13386
  }
13314
13387
  view(task) {
@@ -13317,13 +13390,15 @@ class WorkitCore {
13317
13390
  return workspace;
13318
13391
  if (!workspace.data)
13319
13392
  return failure("not_found", "workspace not found");
13320
- const current = captureCandidate(this.store.root, task.intent.data.scope, environment());
13393
+ const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
13394
+ const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
13321
13395
  if (!current.ok)
13322
13396
  return current;
13323
- const requirements = evaluateRequirements(task, workspace.data, this.context.capabilities, current.data, this.store.root, this.context.caller);
13397
+ const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
13398
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
13324
13399
  return success(task.revision, workspace.data.revision, {
13325
13400
  task,
13326
- workspace: workspace.data,
13401
+ workspace: evaluationWorkspace,
13327
13402
  capabilities: this.context.capabilities,
13328
13403
  requirements,
13329
13404
  evidence: evaluateEvidence(task, current.data)
@@ -13414,8 +13489,6 @@ class WorkitCore {
13414
13489
  return helper;
13415
13490
  if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
13416
13491
  return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
13417
- if (status === "active" && input.authorityRefs.length === 0)
13418
- return failure("permission_denied", "resuming a task requires authority references");
13419
13492
  const workspace = this.store.readWorkspace();
13420
13493
  if (!workspace.ok)
13421
13494
  return workspace;
@@ -13445,7 +13518,7 @@ class WorkitCore {
13445
13518
  return failure("needs_input", "imported task requires fresh destination evidence");
13446
13519
  if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
13447
13520
  return failure("recovery_required", "imported task requires resume reconciliation");
13448
- if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
13521
+ if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
13449
13522
  return failure("permission_denied", "imported resume requires native destination approval");
13450
13523
  resumeCandidate = current.data.candidate;
13451
13524
  }
@@ -13575,7 +13648,7 @@ class WorkitCore {
13575
13648
  task: (value, context) => success(context.revision, null, {
13576
13649
  ...value,
13577
13650
  status: "closed",
13578
- candidates: value.candidates.some((item) => item.id === withCandidate.id) ? value.candidates : [...value.candidates, withCandidate],
13651
+ candidates: taskForView.candidates,
13579
13652
  closure: {
13580
13653
  outcome: closure.data.outcome,
13581
13654
  at: context.now,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-cursor",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "private": false,
5
5
  "description": "Workit Cursor plugin — shared MCP transport, native hooks, and fourteen method skills",
6
6
  "keywords": [
@@ -44,8 +44,8 @@
44
44
  "build": "bun scripts/build.ts"
45
45
  },
46
46
  "dependencies": {
47
- "@brainervirus/workit-core": "^1.2.0",
48
- "@brainervirus/workit-mcp": "^1.2.0"
47
+ "@brainervirus/workit-core": "^1.2.1",
48
+ "@brainervirus/workit-mcp": "^1.2.1"
49
49
  },
50
50
  "engines": {
51
51
  "node": ">=24"