@brainervirus/workit-pi 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/worker.js CHANGED
@@ -8563,6 +8563,7 @@ var resolverInputSchema = object({
8563
8563
  // packages/workit-core/src/core/task-engine.ts
8564
8564
  var verifiedWorkers = new WeakMap;
8565
8565
  var dispatchReservations = new WeakMap;
8566
+ var retryPause = new Int32Array(new SharedArrayBuffer(4));
8566
8567
  // packages/workit-pi/src/worker-protocol.ts
8567
8568
  var MAX_WORKER_LINE_BYTES = 64 * 1024;
8568
8569
  var MAX_WORKER_STDERR_BYTES = 64 * 1024;
package/dist/workit.js CHANGED
@@ -10022,7 +10022,7 @@ class TaskStore {
10022
10022
  if (!current.data)
10023
10023
  return failure2("not_found", "workspace not found");
10024
10024
  if (current.data.revision !== expected)
10025
- return this.conflict(expected, current.data.revision);
10025
+ return this.workspaceConflict(expected, current.data.revision);
10026
10026
  const previousBytes = this.snapshotBytes(this.workspacePath);
10027
10027
  if (!previousBytes)
10028
10028
  return failure2("storage_error", "workspace snapshot disappeared during mutation");
@@ -10065,7 +10065,7 @@ class TaskStore {
10065
10065
  if (task.data.revision !== expected)
10066
10066
  return this.conflict(expected, task.data.revision);
10067
10067
  if (workspace.data.revision !== input.expectedWorkspaceRevision)
10068
- return this.conflict(input.expectedWorkspaceRevision, workspace.data.revision);
10068
+ return this.workspaceConflict(input.expectedWorkspaceRevision, workspace.data.revision);
10069
10069
  const previousWorkspaceBytes = this.snapshotBytes(this.workspacePath);
10070
10070
  const previousTaskBytes = this.snapshotBytes(this.taskPath(input.taskId));
10071
10071
  if (!previousWorkspaceBytes || !previousTaskBytes)
@@ -10875,6 +10875,9 @@ class TaskStore {
10875
10875
  actualRevision: actual
10876
10876
  });
10877
10877
  }
10878
+ workspaceConflict(expected, actual) {
10879
+ return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
10880
+ }
10878
10881
  taskPath(taskId) {
10879
10882
  return path4.join(this.tasksDir, `${taskId}.json`);
10880
10883
  }
@@ -13728,8 +13731,10 @@ values are still concurrency-checked, so never copy revisions between calls.
13728
13731
  A busy result means another live Workit call holds the checkout lock: retry the
13729
13732
  same call; it is not a recovery condition. A lock left by a dead process is
13730
13733
  reclaimed on the next write, and \`workit doctor --fix-lock\` clears it on demand.
13731
- A revision_conflict on a call that omitted expectedRevision is contention too:
13732
- re-read the record and retry the call.
13734
+ An omitted revision absorbs a concurrent write: Workit re-reads, re-checks
13735
+ policy, and reapplies the call, returning busy under persistent contention. A
13736
+ revision_conflict means a revision you passed is stale: re-read the record
13737
+ before deciding whether to retry.
13733
13738
  A solo edit does not need writer acquisition; use it when concurrent checkout
13734
13739
  writers need coordination. Record only observed facts and checks. Evidence can
13735
13740
  become stale when its bound candidate changes; reconcile findings against the
@@ -15113,6 +15118,29 @@ var applyWorkerLifecycle = (input) => {
15113
15118
  return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during lifecycle observation");
15114
15119
  };
15115
15120
  var refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || bindingCovers(scope, { description: "", paths: [ref.path], exclusions: [] }));
15121
+ var REVISION_RETRY_ATTEMPTS = 8;
15122
+ var retryPause = new Int32Array(new SharedArrayBuffer(4));
15123
+ var filledRevisions = (request) => {
15124
+ const value = typeof request === "object" && request !== null ? request : {};
15125
+ return {
15126
+ task: value.expectedRevision === undefined,
15127
+ workspace: value.expectedWorkspaceRevision === undefined
15128
+ };
15129
+ };
15130
+ var filledConflict = (result, filled) => !result.ok && result.code === "revision_conflict" && (("actualRevision" in result.details) && filled.task || ("actualWorkspaceRevision" in result.details) && filled.workspace);
15131
+ var retryFilledRevisions = (filled, run) => {
15132
+ if (!filled.task && !filled.workspace)
15133
+ return run();
15134
+ let result = run();
15135
+ const deadline = Date.now() + defaultLockTimeout();
15136
+ for (let attempt = 1;filledConflict(result, filled); attempt += 1) {
15137
+ if (attempt >= REVISION_RETRY_ATTEMPTS || Date.now() >= deadline)
15138
+ return failure2("busy", "records kept changing under concurrent writers; retry the call");
15139
+ Atomics.wait(retryPause, 0, 0, Math.floor(Math.random() * 4 * attempt) + 1);
15140
+ result = run();
15141
+ }
15142
+ return result;
15143
+ };
15116
15144
  var trustedNow = (context) => typeof context.now === "function" ? context.now() : context.now;
15117
15145
  var mapRef = (ref, ids) => {
15118
15146
  if (ref.kind !== "record")
@@ -15360,6 +15388,9 @@ class WorkitCore {
15360
15388
  if (input.expectedWorkspaceRevision === undefined)
15361
15389
  input.expectedWorkspaceRevision = workspace ? workspace.revision : null;
15362
15390
  }
15391
+ retryOmittedRevisions(request, run) {
15392
+ return retryFilledRevisions(filledRevisions(request), run);
15393
+ }
15363
15394
  helperEntry(task, requireSession = false, requireActive = false) {
15364
15395
  const workerId = this.context.workerId ?? null;
15365
15396
  if (workerId === null)
@@ -15391,6 +15422,9 @@ class WorkitCore {
15391
15422
  return null;
15392
15423
  }
15393
15424
  state(request) {
15425
+ return request?.action === "import" ? this.retryOmittedRevisions(request, () => this.stateOnce(request)) : this.stateOnce(request);
15426
+ }
15427
+ stateOnce(request) {
15394
15428
  const root = this.contextRootError();
15395
15429
  if (!root.ok)
15396
15430
  return root;
@@ -15492,6 +15526,9 @@ class WorkitCore {
15492
15526
  });
15493
15527
  }
15494
15528
  task(request) {
15529
+ return this.retryOmittedRevisions(request, () => this.taskOnce(request));
15530
+ }
15531
+ taskOnce(request) {
15495
15532
  const root = this.contextRootError();
15496
15533
  if (!root.ok)
15497
15534
  return root;
@@ -15591,6 +15628,9 @@ class WorkitCore {
15591
15628
  }
15592
15629
  }
15593
15630
  policy(request) {
15631
+ return this.retryOmittedRevisions(request, () => this.policyOnce(request));
15632
+ }
15633
+ policyOnce(request) {
15594
15634
  const root = this.contextRootError();
15595
15635
  if (!root.ok)
15596
15636
  return root;
@@ -15637,6 +15677,9 @@ class WorkitCore {
15637
15677
  return success(changed.data.revision, null, changed.data.policy);
15638
15678
  }
15639
15679
  evidence(request) {
15680
+ return this.retryOmittedRevisions(request, () => this.evidenceOnce(request));
15681
+ }
15682
+ evidenceOnce(request) {
15640
15683
  const root = this.contextRootError();
15641
15684
  if (!root.ok)
15642
15685
  return root;
@@ -15721,6 +15764,23 @@ class WorkitCore {
15721
15764
  observeStandingDecision(request) {
15722
15765
  return this.recordDecision(request, undefined, true, true);
15723
15766
  }
15767
+ commitTask(taskId, expected, filled, recheck, update) {
15768
+ let revision = expected;
15769
+ let first = true;
15770
+ return retryFilledRevisions({ task: filled, workspace: false }, () => {
15771
+ if (!first) {
15772
+ const fresh = this.store.readTask(taskId);
15773
+ if (!fresh.ok)
15774
+ return fresh;
15775
+ const checked = recheck(fresh.data);
15776
+ if (!checked.ok)
15777
+ return checked;
15778
+ revision = fresh.data.revision;
15779
+ }
15780
+ first = false;
15781
+ return this.store.mutateTask(taskId, revision, update, trustedNow(this.context));
15782
+ });
15783
+ }
15724
15784
  recordDecision(request, nativeObservation, nativeRequired = false, standing = false) {
15725
15785
  const root = this.contextRootError();
15726
15786
  if (!root.ok)
@@ -15745,6 +15805,7 @@ class WorkitCore {
15745
15805
  return task;
15746
15806
  if ((this.context.workerId ?? null) !== null)
15747
15807
  return failure2("permission_denied", "helpers cannot record or revoke decisions");
15808
+ const filled = input.expectedRevision === undefined;
15748
15809
  this.fillRevisions(input, task.data);
15749
15810
  if (input.action === "record") {
15750
15811
  if (task.data.status === "closed")
@@ -15793,7 +15854,20 @@ class WorkitCore {
15793
15854
  const nativeProvenance = native?.ok ? retireNativeAuthority(native.data) : null;
15794
15855
  if (native?.ok && !nativeProvenance)
15795
15856
  return failure2("permission_denied", "native decision authority was retired");
15796
- const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
15857
+ let standingProvenance = standingApproval?.ok === true ? standingApproval.data : null;
15858
+ const recheck = (fresh) => {
15859
+ if (fresh.status === "closed")
15860
+ return failure2("invalid_transition", "closed task cannot record a decision");
15861
+ if (!standingProvenance)
15862
+ return success(null, null, null);
15863
+ const again = verifyStandingApproval(this.store.root, fresh, this.context.caller, input.binding);
15864
+ if (again.ok)
15865
+ standingProvenance = again.data;
15866
+ return again;
15867
+ };
15868
+ const changed = this.commitTask(task.data.id, input.expectedRevision, filled, recheck, (current, mutation) => {
15869
+ if (current.status === "closed")
15870
+ return failure2("invalid_transition", "closed task cannot record a decision");
15797
15871
  const latestContent = verifyDecisionContent(this.store, input.binding);
15798
15872
  if (!latestContent.ok)
15799
15873
  return latestContent;
@@ -15805,14 +15879,14 @@ class WorkitCore {
15805
15879
  const entry = {
15806
15880
  id: newId(),
15807
15881
  recordedAt: mutation.now,
15808
- provenance: standingApproval?.ok === true ? standingApproval.data : nativeProvenance ?? provenance(this.context, "agent_reported"),
15882
+ provenance: standingProvenance ?? nativeProvenance ?? provenance(this.context, "agent_reported"),
15809
15883
  data
15810
15884
  };
15811
15885
  return success(mutation.revision, null, {
15812
15886
  ...current,
15813
15887
  decisions: [...current.decisions, entry]
15814
15888
  });
15815
- }, trustedNow(this.context));
15889
+ });
15816
15890
  if (!changed.ok)
15817
15891
  return changed;
15818
15892
  return success(changed.data.revision, null, changed.data.decisions.at(-1));
@@ -15826,7 +15900,7 @@ class WorkitCore {
15826
15900
  return failure2("invalid_transition", "decision is already revoked");
15827
15901
  if (decision.data.consumption)
15828
15902
  return failure2("permission_denied", "consumed decision cannot be revoked");
15829
- const changed = this.store.mutateTask(task.data.id, input.expectedRevision, (current, mutation) => {
15903
+ const changed = this.commitTask(task.data.id, input.expectedRevision, filled, () => success(null, null, null), (current, mutation) => {
15830
15904
  const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
15831
15905
  if (!entry)
15832
15906
  return failure2("not_found", "decision not found");
@@ -15843,13 +15917,16 @@ class WorkitCore {
15843
15917
  ...current,
15844
15918
  decisions: current.decisions.map((candidate) => candidate.id === input.decisionId ? updated : candidate)
15845
15919
  });
15846
- }, trustedNow(this.context));
15920
+ });
15847
15921
  if (!changed.ok)
15848
15922
  return changed;
15849
15923
  const entry = changed.data.decisions.find((candidate) => candidate.id === input.decisionId);
15850
15924
  return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "decision disappeared");
15851
15925
  }
15852
15926
  finding(request) {
15927
+ return this.retryOmittedRevisions(request, () => this.findingOnce(request));
15928
+ }
15929
+ findingOnce(request) {
15853
15930
  const root = this.contextRootError();
15854
15931
  if (!root.ok)
15855
15932
  return root;
@@ -15973,6 +16050,9 @@ class WorkitCore {
15973
16050
  return entry ? success(changed.data.revision, null, entry) : failure2("recovery_required", "finding disappeared");
15974
16051
  }
15975
16052
  worker(request) {
16053
+ return this.retryOmittedRevisions(request, () => this.workerOnce(request));
16054
+ }
16055
+ workerOnce(request) {
15976
16056
  const root = this.contextRootError();
15977
16057
  if (!root.ok)
15978
16058
  return root;
@@ -16320,6 +16400,9 @@ class WorkitCore {
16320
16400
  return applied;
16321
16401
  }
16322
16402
  writer(request) {
16403
+ return this.retryOmittedRevisions(request, () => this.writerOnce(request));
16404
+ }
16405
+ writerOnce(request) {
16323
16406
  const root = this.contextRootError();
16324
16407
  if (!root.ok)
16325
16408
  return root;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-pi",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Workit native extension for Pi — tools, method skills, and session continuity",
5
5
  "keywords": [
6
6
  "agentic-coding",