@brainervirus/workit-core 2.9.0 → 4.0.0

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/README.md CHANGED
@@ -30,7 +30,8 @@ Adapters map host-native surfaces to `src/core/`; they never re-implement core l
30
30
  ## Exports
31
31
 
32
32
  - Main entry: `./src/core.ts`.
33
- - Subpath exports: `./src/*.ts` (e.g. `@brainervirus/workit-core/src/core/doctor`), plus `./package.json`.
33
+ - Subpath exports: `./src/*.ts` (e.g. `@brainervirus/workit-core/src/core/task-store`), plus `./package.json`.
34
+ - Setup, doctor, upgrade, uninstall and host-install code lives in `@brainervirus/workit-cli` (`src/admin/`), not here, so host hooks and plugins never bundle it.
34
35
 
35
36
  ## Package scripts
36
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-core",
3
- "version": "2.9.0",
3
+ "version": "4.0.0",
4
4
  "private": false,
5
5
  "description": "Workit shared core — task, policy, evidence, review, decision, worker, and writer state for agentic coding workflows",
6
6
  "keywords": [
@@ -56,13 +56,7 @@
56
56
  "yoga-layout",
57
57
  "zod"
58
58
  ],
59
- "workit-opencode": [
60
- "@openclaw/fs-safe",
61
- "@opencode-ai/plugin",
62
- "@opencode-ai/plugin/zod",
63
- "@opencode/plugin",
64
- "zod"
65
- ],
59
+ "workit-opencode": ["@openclaw/fs-safe", "@opencode/plugin", "zod"],
66
60
  "workit-cursor": [
67
61
  "@modelcontextprotocol/sdk",
68
62
  "@openclaw/fs-safe",
@@ -19,10 +19,9 @@ fi
19
19
  mkdir -p "$PLUGIN_DIR"
20
20
  rsync -a --delete "$SOURCE/packages/workit-codex/" "$PLUGIN_DIR/"
21
21
 
22
- if ! bun "$ROOT/packages/workit-core/scripts/doctor-check.ts" cli; then
22
+ if ! bun "$ROOT/packages/workit-cli/scripts/doctor-check.ts" cli; then
23
23
  echo "FATAL: post-install doctor found problems after Codex plugin sync" >&2
24
24
  exit 1
25
25
  fi
26
26
 
27
27
  echo "Codex plugin installed at $PLUGIN_DIR"
28
- echo "Complete an explicit v1 cutover before treating this as a generation switch."
@@ -50,7 +50,7 @@ fi
50
50
  # The check reads only workit-owned files, so a healthy install is byte-untouched;
51
51
  # a registry-unreachable comparison reports registry_unreachable, never stale
52
52
  # (CA-04) — no false stale_install and no install failure.
53
- if bun "$ROOT/packages/workit-core/scripts/doctor-check.ts" cursor --stale; then
53
+ if bun "$ROOT/packages/workit-cli/scripts/doctor-check.ts" cursor --stale; then
54
54
  :
55
55
  elif [ $? -eq 2 ]; then
56
56
  echo "workit: stale Cursor plugin install detected — self-healing (refresh + canonical re-registration)" >&2
@@ -70,7 +70,7 @@ if [ -d "$SKILLS_DIR" ]; then
70
70
  fi
71
71
 
72
72
  PLUGIN_DIR="$HOME/.cursor/plugins/local/workit"
73
- REGISTRATION_TS="$ROOT/packages/workit-core/src/core/registration.ts"
73
+ REGISTRATION_TS="$ROOT/packages/workit-cli/src/admin/registration.ts"
74
74
  CURSOR_SETTINGS="$HOME/.cursor/settings.json" CURSOR_MCP="$HOME/.cursor/mcp.json" PLUGIN_DIR="$PLUGIN_DIR" REGISTRATION_TS="$REGISTRATION_TS" LOCAL_DIST="$LOCAL_DIST" bun -e '
75
75
  import fs from "node:fs";
76
76
  import path from "node:path";
@@ -120,7 +120,7 @@ if (localDist) {
120
120
  '
121
121
 
122
122
  # DG-09: verify the just-written Cursor registration with the shared offline doctor.
123
- if ! bun "$ROOT/packages/workit-core/scripts/doctor-check.ts" cursor; then
123
+ if ! bun "$ROOT/packages/workit-cli/scripts/doctor-check.ts" cursor; then
124
124
  echo "FATAL: post-install doctor found an unhealthy Cursor registration" >&2
125
125
  exit 1
126
126
  fi
@@ -140,6 +140,5 @@ if [ -d "$LEGACY_PLUGIN_DIR" ] && [ "$LEGACY_PLUGIN_DIR" != "$PLUGIN_DIR" ]; the
140
140
  fi
141
141
 
142
142
  echo "Cursor plugin installed + auto-sync enabled (sessionStart)."
143
- echo "Generation switches require an explicit cutover preview/apply — install alone is not a cutover."
144
143
  echo "Share: $SHARE"
145
144
  ls "$HOME/.cursor/plugins/local/workit/skills" | grep '^wk-' || true
@@ -38,7 +38,7 @@ mkdir -p "$(dirname "$CONFIG")"
38
38
 
39
39
  # Merge/dedupe registrations via the shared core helper (RR-06): one dev pin,
40
40
  # no legacy/current duplicates, unrelated user settings preserved.
41
- CONFIG_PATH="$CONFIG" PIN_PATH="$PIN" REGISTRATION_TS="$ROOT/packages/workit-core/src/core/registration.ts" bun -e '
41
+ CONFIG_PATH="$CONFIG" PIN_PATH="$PIN" REGISTRATION_TS="$ROOT/packages/workit-cli/src/admin/registration.ts" bun -e '
42
42
  import fs from "node:fs";
43
43
  const { mergeOpenCodeConfig } = await import(process.env.REGISTRATION_TS!);
44
44
  const path = process.env.CONFIG_PATH!;
@@ -71,10 +71,9 @@ if [ ! -s "$PLUGIN_ENTRY" ]; then
71
71
  fi
72
72
 
73
73
  # DG-09: verify the just-written registration with the shared offline doctor.
74
- if ! bun "$ROOT/packages/workit-core/scripts/doctor-check.ts" opencode; then
74
+ if ! bun "$ROOT/packages/workit-cli/scripts/doctor-check.ts" opencode; then
75
75
  echo "FATAL: post-install doctor found an unhealthy OpenCode registration" >&2
76
76
  exit 1
77
77
  fi
78
78
 
79
79
  echo "OpenCode install done. Fully quit all opencode processes, then restart."
80
- echo "Generation switches require an explicit cutover preview/apply — install alone is not a cutover."
@@ -19,8 +19,8 @@ fi
19
19
  # pi 0.85.1 loads extensions from the settings `packages` list, not from
20
20
  # ~/.pi/config.json below. Register the absolute package dir there (honoring
21
21
  # PI_CODING_AGENT_DIR) so `pi list` picks it up; the config.json write stays
22
- # because uninstall/cutover/detection still key pi state off it — migrating
23
- # those three off config.json is a separate contract change.
22
+ # because uninstall and detection still key pi state off it — migrating
23
+ # those two off config.json is a separate contract change.
24
24
  AGENT_DIR="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
25
25
  SETTINGS="$AGENT_DIR/settings.json"
26
26
  mkdir -p "$AGENT_DIR"
@@ -53,4 +53,3 @@ console.log("Registered Pi extension:", process.env.EXT);
53
53
  '
54
54
 
55
55
  echo "Pi package registered in $SETTINGS (and legacy $PI_CONFIG)"
56
- echo "Complete an explicit v1 cutover before treating this as a generation switch."
@@ -153,19 +153,6 @@ fi
153
153
  # Remove broken TLA live-loader if present (OpenCode ignored it; /wk-* vanished)
154
154
  rm -f "${OPENCODE_PLUGINS}/workflow-toolkit.ts"
155
155
 
156
- # Ensure OpenCode has plugin peer dep
157
- PKG="${HOME}/.config/opencode/package.json"
158
- if [ -f "$PKG" ]; then
159
- PKG_PATH="$PKG" "$BUN_BIN" -e '
160
- import fs from "node:fs";
161
- const path = process.env.PKG_PATH!;
162
- const data = JSON.parse(fs.readFileSync(path, "utf8"));
163
- data.dependencies = data.dependencies ?? {};
164
- data.dependencies["@opencode-ai/plugin"] = data.dependencies["@opencode-ai/plugin"] ?? "1.18.30";
165
- fs.writeFileSync(path, JSON.stringify(data, null, 2) + "\n");
166
- ' || true
167
- fi
168
-
169
156
  # Drop bun package cache so old github/file installs cannot shadow file:// plugin.ts
170
157
  rm -rf "${HOME}/.cache/opencode/packages/workflow-toolkit-opencode@"* 2>/dev/null || true
171
158
 
package/src/core/init.ts CHANGED
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import { spawnSync } from "node:child_process";
4
4
  import { configDir, isConfigObject } from "./config";
5
5
  import { writeFileExclusive } from "./safe-write";
6
- import { applyWorkspaceBranchPolicy } from "./setup";
6
+ import { applyWorkspaceBranchPolicy } from "./workspace-branch-policy";
7
7
  import { youTrackTokenCreateUrl } from "./youtrack";
8
8
 
9
9
  const TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
@@ -106,7 +106,10 @@ PR readiness, a fresh review, or post-commit assessment/closure paperwork.
106
106
 
107
107
  For tracked work, inspect its current state and use the shared operations for
108
108
  task, policy, evidence, finding, decision, worker, writer, and state changes.
109
- Start a record once for an explicit tracked objective; assess or reassess only
109
+ Each branch has one implicit task: an operation without taskId applies to it,
110
+ and the first recording (a note, check, finding or evidence) creates it, so you
111
+ never manage task ids. Start a record only for an explicit tracked objective;
112
+ assess or reassess only
110
113
  when policy selection or changed evidence/constraints requires it. Omitted
111
114
  expectedRevision and expectedWorkspaceRevision use current values; explicit
112
115
  values are still concurrency-checked, so never copy revisions between calls.
@@ -4,9 +4,11 @@ import { hostname } from "node:os";
4
4
  import path from "node:path";
5
5
  import * as z from "zod";
6
6
  import { canonicalJson } from "./task-contract";
7
+ import { checkoutSlug, resolveStore } from "../store/paths";
7
8
 
8
9
  /**
9
- * Ownership rules for a checkout's `.workit/metadata.lock`.
10
+ * Ownership rules for the workit store's locks: a checkout's
11
+ * `checkouts/<slug>/metadata.lock` and each task's `tasks/<id>/lock`.
10
12
  *
11
13
  * The lock is a short mutex around one store mutation (or one managed effect).
12
14
  * A lock whose owner is gone is reclaimed automatically; a lock whose owner is
@@ -197,7 +199,16 @@ const ageOf = (file: string, nowMs: number): number | null => {
197
199
  }
198
200
  };
199
201
 
200
- export const lockPathFor = (root: string) => path.join(root, ".workit", "metadata.lock");
202
+ /** A checkout's lock in the task store (see core/task-store.ts). */
203
+ export const lockPathFor = (root: string): string => {
204
+ let real = path.resolve(root);
205
+ try {
206
+ real = fs.realpathSync(real);
207
+ } catch {}
208
+ const location = resolveStore(real);
209
+ const dir = location instanceof Error ? path.join(real, ".workit") : location.dir;
210
+ return path.join(dir, "checkouts", checkoutSlug(real), "metadata.lock");
211
+ };
201
212
 
202
213
  /** Remove a reclaim guard abandoned by a crashed reclaimer. Returns true when removed. */
203
214
  export const clearAbandonedReclaimGuard = (lockPath: string, nowMs = Date.now()): boolean => {
@@ -7,10 +7,10 @@
7
7
  export const SUPPORT_MATRIX = {
8
8
  bun: "1.4.1",
9
9
  node: { minimum: "24", current: "24.20.0" },
10
- // The object-form dual entry (server() + setup()) only loads from
11
- // 1.18.29+; the supported floor is 1.18.30 so no host can pass doctor while
12
- // being unable to load the entry shape.
13
- opencode: { minimum: "1.18.30", current: "1.18.34" },
10
+ // OpenCode V2 plugin API only: the entry exports `setup()` (Workit 3.0
11
+ // retired the V1 `server()` adapter), so 1.x hosts cannot load it. 2.0.18 is
12
+ // the `@opencode/plugin` contract the adapter is built and tested against.
13
+ opencode: { minimum: "2.0.18", current: "2.0.18" },
14
14
  // Codex is a qualification host, not a runtime dependency: the CLI version
15
15
  // below is the one live qualification evidence covers. The doctor warns when
16
16
  // an installed CLI drifts ahead so a fresh install never silently outruns
@@ -1166,16 +1166,6 @@ const stateOperations = {
1166
1166
  bundle: exportBundleSchema,
1167
1167
  authorityRefs: z.array(refSchema),
1168
1168
  }),
1169
- recover: operation({
1170
- action: z.literal("recover"),
1171
- taskId: id.optional(),
1172
- expectedWorkspaceRevision: revision.optional(),
1173
- target: z.enum(["task", "workspace"]),
1174
- expectedBytes: digest,
1175
- snapshotDigest: digest,
1176
- reason: text,
1177
- authorityRefs: z.array(refSchema),
1178
- }),
1179
1169
  };
1180
1170
 
1181
1171
  export const operationSchemas = {
@@ -1191,16 +1181,25 @@ export const operationSchemas = {
1191
1181
  export type OperationRequest = z.infer<(typeof operationSchemas)[OperationFamily]>;
1192
1182
 
1193
1183
  /**
1194
- * Schemas advertised to hosts. `state.recover` needs host-supplied native
1195
- * recovery authority (`OperationContext.nativeRecovery`), which no shipped
1196
- * host provides, so advertising it only sends agents into a guaranteed
1197
- * permission_denied. parseOperation still accepts it for embedders that do
1198
- * supply that authority.
1184
+ * Schemas advertised to hosts. Every `taskId` is optional there: an operation
1185
+ * without one applies to the implicit task of the caller's branch (D3); the
1186
+ * engine fills it in before parsing against `operationSchemas`.
1199
1187
  */
1200
- const { recover: _unadvertisedRecover, ...advertisedStateOperations } = stateOperations;
1188
+ const withImplicitTask = (options: Record<string, z.ZodObject>) =>
1189
+ Object.values(options).map((option) =>
1190
+ "taskId" in option.shape ? option.extend({ taskId: id.optional() }) : option,
1191
+ );
1192
+ const advertised = (options: Record<string, z.ZodObject>) =>
1193
+ z.discriminatedUnion("action", withImplicitTask(options) as any);
1201
1194
  export const advertisedOperationSchemas = {
1202
- ...operationSchemas,
1203
- state: z.discriminatedUnion("action", Object.values(advertisedStateOperations) as any),
1195
+ task: advertised(taskOperations),
1196
+ policy: advertised(policyOperations),
1197
+ evidence: advertised(evidenceOperations),
1198
+ finding: advertised(findingOperations),
1199
+ decision: advertised(decisionOperations),
1200
+ worker: advertised(workerOperations),
1201
+ writer: advertised(writerOperations),
1202
+ state: advertised(stateOperations),
1204
1203
  } as const;
1205
1204
  export type TaskStartRequest = z.infer<typeof taskOperations.start>;
1206
1205
 
@@ -39,6 +39,7 @@ import {
39
39
  type Utc,
40
40
  type Provenance,
41
41
  type Worker,
42
+ type OperationFamily,
42
43
  type WorkspaceRecord,
43
44
  taskRecordSchema,
44
45
  } from "./task-contract";
@@ -62,7 +63,8 @@ import {
62
63
  } from "./authority";
63
64
  import { diffPolicy, resolvePolicy } from "./policy-resolver";
64
65
  import { verifyStandingApproval } from "./auto-approval";
65
- import { TaskStore, type MetadataLock, type ProcessEvidence } from "./task-store";
66
+ import { sameDirectoryIdentity, TaskStore } from "./task-store";
67
+ import { checkoutRootOf } from "../store/paths";
66
68
  import { defaultLockTimeout } from "./store-lock";
67
69
  import {
68
70
  compactTaskContext,
@@ -108,12 +110,6 @@ export type OperationContext = {
108
110
  now: Utc | (() => Utc);
109
111
  nativeAuthority?: NativeAuthorityVerifier;
110
112
  nativeWorker?: NativeWorkerVerifier;
111
- nativeRecovery?: (input: {
112
- lock: MetadataLock | null;
113
- writer: WorkspaceRecord["writer"];
114
- reason: string;
115
- authorityRefs: Ref[];
116
- }) => Result<ProcessEvidence>;
117
113
  workerId?: string | null;
118
114
  };
119
115
 
@@ -466,6 +462,40 @@ const refsWithinScope = (refs: Ref[], scope: Scope): boolean =>
466
462
  bindingCovers(scope, { description: "", paths: [ref.path], exclusions: [] }),
467
463
  );
468
464
 
465
+ /** Actions that take a taskId; without one they apply to the implicit task. */
466
+ const TASK_SCOPED_ACTIONS: ReadonlySet<string> = new Set([
467
+ "task.inspect",
468
+ "task.revise",
469
+ "task.progress",
470
+ "task.pause",
471
+ "task.resume",
472
+ "task.close",
473
+ "policy.assess",
474
+ "policy.preview",
475
+ "policy.explain",
476
+ "evidence.record",
477
+ "finding.record",
478
+ "finding.resolve",
479
+ "decision.record",
480
+ "decision.revoke",
481
+ "worker.assign",
482
+ "worker.report",
483
+ "worker.cancel",
484
+ "writer.acquire",
485
+ "writer.release",
486
+ "state.export",
487
+ ]);
488
+ /** Recording actions that create the implicit task on first use. */
489
+ const CREATING_ACTIONS: ReadonlySet<string> = new Set([
490
+ "task.revise",
491
+ "task.progress",
492
+ "policy.assess",
493
+ "evidence.record",
494
+ "finding.record",
495
+ "decision.record",
496
+ "worker.assign",
497
+ "writer.acquire",
498
+ ]);
469
499
  /** Commit attempts for a call whose revisions the engine filled (see retryFilledRevisions). */
470
500
  const REVISION_RETRY_ATTEMPTS = 8;
471
501
  const retryPause = new Int32Array(new SharedArrayBuffer(4));
@@ -777,7 +807,10 @@ export class WorkitCore {
777
807
 
778
808
  private contextRootError(): Result<null> {
779
809
  try {
780
- if (realpathSync(this.context.root) !== this.store.root)
810
+ // The same checkout: any directory inside it resolves to its top level.
811
+ realpathSync(this.context.root);
812
+ const root = checkoutRootOf(this.context.root);
813
+ if (root !== this.store.root && !sameDirectoryIdentity(root, this.store.root))
781
814
  return failure(
782
815
  "invalid_input",
783
816
  "operation context root does not match the task store root",
@@ -820,6 +853,36 @@ export class WorkitCore {
820
853
  return retryFilledRevisions(filledRevisions(request), run);
821
854
  }
822
855
 
856
+ /**
857
+ * A task-scoped request without a `taskId` applies to the implicit task of
858
+ * this checkout's branch (D3). Recording actions create that task on first
859
+ * use; reads and lifecycle actions only look it up. Helpers never create.
860
+ */
861
+ private implicitTask(family: OperationFamily, request: unknown): Result<unknown> {
862
+ if (typeof request !== "object" || request === null || Array.isArray(request))
863
+ return success(null, null, request);
864
+ const value = request as { action?: unknown; taskId?: unknown };
865
+ if (value.taskId !== undefined || typeof value.action !== "string")
866
+ return success(null, null, request);
867
+ const action = `${family}.${value.action}`;
868
+ if (!TASK_SCOPED_ACTIONS.has(action)) return success(null, null, request);
869
+ const root = this.contextRootError();
870
+ if (!root.ok) return root;
871
+ const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
872
+ const found = this.store.implicitTask({
873
+ provenance: provenance(this.context),
874
+ create,
875
+ now: trustedNow(this.context),
876
+ });
877
+ if (!found.ok) return found;
878
+ if (!found.data)
879
+ return failure(
880
+ "not_found",
881
+ 'no task for this branch yet; pass taskId, or record something first (`workit task start "<objective>"`, a note, a check) to create it',
882
+ );
883
+ return success(null, null, { ...request, taskId: found.data.task.id });
884
+ }
885
+
823
886
  private helperEntry(
824
887
  task: TaskRecord,
825
888
  requireSession = false,
@@ -859,10 +922,12 @@ export class WorkitCore {
859
922
  }
860
923
 
861
924
  state(request: unknown): Result<ExportBundle | TaskSummary | TaskRecord | WorkspaceRecord> {
862
- // Recovery is an operator CAS over snapshot bytes; only import retries.
863
- return (request as { action?: unknown } | null)?.action === "import"
864
- ? this.retryOmittedRevisions(request, () => this.stateOnce(request))
865
- : this.stateOnce(request);
925
+ const resolved = this.implicitTask("state", request);
926
+ if (!resolved.ok) return resolved;
927
+ const value = resolved.data;
928
+ return (value as { action?: unknown } | null)?.action === "import"
929
+ ? this.retryOmittedRevisions(value, () => this.stateOnce(value))
930
+ : this.stateOnce(value);
866
931
  }
867
932
 
868
933
  private stateOnce(
@@ -932,47 +997,14 @@ export class WorkitCore {
932
997
  if (!imported.ok) return imported;
933
998
  return this.summary(imported.data);
934
999
  }
935
- if (!this.context.nativeRecovery)
936
- return failure("permission_denied", "native recovery authority is unavailable");
937
- const processEvidence = (
938
- lock: MetadataLock | null,
939
- writer: WorkspaceRecord["writer"],
940
- ): Result<ProcessEvidence> =>
941
- this.context.nativeRecovery!({
942
- lock,
943
- writer,
944
- reason: input.reason,
945
- authorityRefs: input.authorityRefs,
946
- });
947
- if (input.target === "workspace")
948
- return this.store.recoverWorkspace({
949
- expectedBytes: input.expectedBytes,
950
- snapshotDigest: input.snapshotDigest,
951
- reason: input.reason,
952
- authorityRefs: input.authorityRefs,
953
- expectedWorkspaceRevision: input.expectedWorkspaceRevision,
954
- processEvidence,
955
- });
956
- const workspace = this.store.readWorkspace();
957
- if (!workspace.ok) return workspace;
958
- if (!workspace.data) return failure("not_found", "workspace not found");
959
- this.fillRevisions(input, undefined, workspace.data);
960
- if (input.target === "workspace" && input.taskId !== undefined)
961
- return failure("invalid_input", "workspace recovery takes no task ID");
962
- if (input.target === "task" && input.taskId === undefined)
963
- return failure("invalid_input", "task recovery requires a task ID");
964
- return this.store.recoverTask(input.taskId, {
965
- expectedBytes: input.expectedBytes,
966
- snapshotDigest: input.snapshotDigest,
967
- reason: input.reason,
968
- authorityRefs: input.authorityRefs,
969
- expectedWorkspaceRevision: input.expectedWorkspaceRevision,
970
- processEvidence,
971
- });
1000
+ return failure("invalid_input", "unknown state action");
972
1001
  }
973
1002
 
974
1003
  task(request: unknown): Result<TaskSummary | TaskListItem[] | TaskView> {
975
- return this.retryOmittedRevisions(request, () => this.taskOnce(request));
1004
+ const resolved = this.implicitTask("task", request);
1005
+ if (!resolved.ok) return resolved;
1006
+ const value = resolved.data;
1007
+ return this.retryOmittedRevisions(value, () => this.taskOnce(value));
976
1008
  }
977
1009
 
978
1010
  private taskOnce(request: unknown): Result<TaskSummary | TaskListItem[] | TaskView> {
@@ -1083,7 +1115,10 @@ export class WorkitCore {
1083
1115
  }
1084
1116
 
1085
1117
  policy(request: unknown): Result<Policy | null> {
1086
- return this.retryOmittedRevisions(request, () => this.policyOnce(request));
1118
+ const resolved = this.implicitTask("policy", request);
1119
+ if (!resolved.ok) return resolved;
1120
+ const value = resolved.data;
1121
+ return this.retryOmittedRevisions(value, () => this.policyOnce(value));
1087
1122
  }
1088
1123
 
1089
1124
  private policyOnce(request: unknown): Result<Policy | null> {
@@ -1139,7 +1174,10 @@ export class WorkitCore {
1139
1174
  }
1140
1175
 
1141
1176
  evidence(request: unknown): Result<Entry<Evidence>> {
1142
- return this.retryOmittedRevisions(request, () => this.evidenceOnce(request));
1177
+ const resolved = this.implicitTask("evidence", request);
1178
+ if (!resolved.ok) return resolved;
1179
+ const value = resolved.data;
1180
+ return this.retryOmittedRevisions(value, () => this.evidenceOnce(value));
1143
1181
  }
1144
1182
 
1145
1183
  private evidenceOnce(request: unknown): Result<Entry<Evidence>> {
@@ -1339,11 +1377,13 @@ export class WorkitCore {
1339
1377
  }
1340
1378
 
1341
1379
  decision(request: unknown): Result<Entry<Decision>> {
1342
- return this.recordDecision(request);
1380
+ const resolved = this.implicitTask("decision", request);
1381
+ return resolved.ok ? this.recordDecision(resolved.data) : resolved;
1343
1382
  }
1344
1383
 
1345
1384
  observeDecision(request: unknown, observation: unknown): Result<Entry<Decision>> {
1346
- return this.recordDecision(request, observation, true);
1385
+ const resolved = this.implicitTask("decision", request);
1386
+ return resolved.ok ? this.recordDecision(resolved.data, observation, true) : resolved;
1347
1387
  }
1348
1388
 
1349
1389
  /**
@@ -1353,7 +1393,8 @@ export class WorkitCore {
1353
1393
  * removal, which fails the next reserve closed.
1354
1394
  */
1355
1395
  observeStandingDecision(request: unknown): Result<Entry<Decision>> {
1356
- return this.recordDecision(request, undefined, true, true);
1396
+ const resolved = this.implicitTask("decision", request);
1397
+ return resolved.ok ? this.recordDecision(resolved.data, undefined, true, true) : resolved;
1357
1398
  }
1358
1399
 
1359
1400
  /**
@@ -1576,7 +1617,10 @@ export class WorkitCore {
1576
1617
  }
1577
1618
 
1578
1619
  finding(request: unknown): Result<Entry<Finding>> {
1579
- return this.retryOmittedRevisions(request, () => this.findingOnce(request));
1620
+ const resolved = this.implicitTask("finding", request);
1621
+ if (!resolved.ok) return resolved;
1622
+ const value = resolved.data;
1623
+ return this.retryOmittedRevisions(value, () => this.findingOnce(value));
1580
1624
  }
1581
1625
 
1582
1626
  private findingOnce(request: unknown): Result<Entry<Finding>> {
@@ -1749,7 +1793,10 @@ export class WorkitCore {
1749
1793
  }
1750
1794
 
1751
1795
  worker(request: unknown): Result<Entry<Worker>> {
1752
- return this.retryOmittedRevisions(request, () => this.workerOnce(request));
1796
+ const resolved = this.implicitTask("worker", request);
1797
+ if (!resolved.ok) return resolved;
1798
+ const value = resolved.data;
1799
+ return this.retryOmittedRevisions(value, () => this.workerOnce(value));
1753
1800
  }
1754
1801
 
1755
1802
  private workerOnce(request: unknown): Result<Entry<Worker>> {
@@ -2188,7 +2235,10 @@ export class WorkitCore {
2188
2235
  }
2189
2236
 
2190
2237
  writer(request: unknown): Result<WorkspaceRecord> {
2191
- return this.retryOmittedRevisions(request, () => this.writerOnce(request));
2238
+ const resolved = this.implicitTask("writer", request);
2239
+ if (!resolved.ok) return resolved;
2240
+ const value = resolved.data;
2241
+ return this.retryOmittedRevisions(value, () => this.writerOnce(value));
2192
2242
  }
2193
2243
 
2194
2244
  private writerOnce(request: unknown): Result<WorkspaceRecord> {