@brainervirus/workit-core 1.2.0 → 1.3.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.
Files changed (46) hide show
  1. package/package.json +1 -1
  2. package/skills/workit-babysit/SKILL.md +20 -26
  3. package/skills/workit-behavioral-tdd/SKILL.md +0 -5
  4. package/skills/workit-blast-radius/SKILL.md +0 -5
  5. package/skills/workit-challenge/SKILL.md +30 -47
  6. package/skills/workit-debug/SKILL.md +5 -27
  7. package/skills/workit-deslop/SKILL.md +0 -5
  8. package/skills/workit-diagram/SKILL.md +0 -5
  9. package/skills/workit-green-run/SKILL.md +0 -5
  10. package/skills/workit-handoff/SKILL.md +2 -8
  11. package/skills/workit-implement/SKILL.md +14 -20
  12. package/skills/workit-mockup/SKILL.md +0 -5
  13. package/skills/workit-plan/SKILL.md +30 -63
  14. package/skills/workit-review/SKILL.md +0 -5
  15. package/skills/workit-steer/SKILL.md +27 -27
  16. package/src/core/authority.ts +31 -8
  17. package/src/core/auto-approval.ts +44 -32
  18. package/src/core/branch.ts +293 -42
  19. package/src/core/config-conversion.ts +4 -4
  20. package/src/core/config-guard.ts +1 -7
  21. package/src/core/config.ts +1 -1
  22. package/src/core/cutover.ts +1207 -140
  23. package/src/core/doctor.ts +155 -82
  24. package/src/core/external-action-effects.ts +1102 -209
  25. package/src/core/external-action.ts +230 -103
  26. package/src/core/init.ts +25 -115
  27. package/src/core/legacy-ownership.ts +47 -0
  28. package/src/core/methods.ts +56 -52
  29. package/src/core/ports/pr-create.ts +23 -15
  30. package/src/core/pr-create.ts +374 -84
  31. package/src/core/registration.ts +0 -15
  32. package/src/core/repo-tools.ts +0 -1
  33. package/src/core/route-intent.ts +34 -82
  34. package/src/core/safe-write.ts +66 -1
  35. package/src/core/setup-state.ts +7 -0
  36. package/src/core/setup.ts +175 -82
  37. package/src/core/task-contract.ts +16 -3
  38. package/src/core/task-engine.ts +136 -31
  39. package/src/core/task-evaluation.ts +3 -5
  40. package/src/core/task-store.ts +159 -1
  41. package/src/core/tracker-issues.ts +55 -118
  42. package/src/core/vcs-config.ts +178 -187
  43. package/src/core/workers.ts +17 -0
  44. package/src/core/workspaces.ts +548 -46
  45. package/src/core.ts +1 -2
  46. package/src/core/ports/vcs-token-create-urls.ts +0 -4
@@ -23,6 +23,7 @@ import {
23
23
  type Utc,
24
24
  } from "./task-contract";
25
25
  import { TaskStore } from "./task-store";
26
+ import { currentWriterOwnsTask } from "./workers";
26
27
 
27
28
  export type NativeAuthorityContext = {
28
29
  now: Utc;
@@ -542,6 +543,27 @@ export const storedDecisionApplicable = (
542
543
  verifyDecisionContentAtRoot(store.root, entry.data.binding).ok,
543
544
  );
544
545
 
546
+ const approvalAuthorityMatches = (
547
+ store: TaskStore,
548
+ task: TaskRecord,
549
+ approval: Provenance,
550
+ current: Provenance,
551
+ ): boolean => {
552
+ if (approval.kind !== "host_observed" || approval.receipts.length === 0) return false;
553
+ if (provenanceMatches(approval, current)) return true;
554
+ const session = current.kind === "host_observed" ? current.session : null;
555
+ if (!session || session.kind !== "host") return false;
556
+ const workspace = store.readWorkspace();
557
+ return (
558
+ workspace.ok &&
559
+ workspace.data !== null &&
560
+ currentWriterOwnsTask(task, workspace.data, {
561
+ host: session.host,
562
+ actor: session.handle,
563
+ })
564
+ );
565
+ };
566
+
545
567
  const validateAction = (
546
568
  store: TaskStore,
547
569
  task: TaskRecord,
@@ -574,11 +596,7 @@ const validateAction = (
574
596
  return failure("permission_denied", "native reservation authority does not match decision");
575
597
  if (decision.response !== "approved")
576
598
  return failure("permission_denied", "decision was rejected");
577
- if (
578
- entry.provenance.kind !== "host_observed" ||
579
- entry.provenance.receipts.length === 0 ||
580
- !provenanceMatches(entry.provenance, authority.provenance)
581
- )
599
+ if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
582
600
  return failure("permission_denied", "action approval lacks native receipt assurance");
583
601
  // Standing auto-approval receipts carry no live question. They authorize
584
602
  // only while the referenced workspace rule still covers the operation —
@@ -607,7 +625,12 @@ const validateAction = (
607
625
  operation = null;
608
626
  }
609
627
  const cls = operationAutoClass(operation);
610
- if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls)))
628
+ if (
629
+ !cls ||
630
+ !standing.some((receipt) =>
631
+ standingApprovalLive(store.root, receipt, cls, undefined, decision.binding.approvedContent),
632
+ )
633
+ )
611
634
  return failure("permission_denied", "standing auto-approval is not live for this operation");
612
635
  }
613
636
  if (decision.revoked) return failure("permission_denied", "decision is revoked");
@@ -793,7 +816,7 @@ export function settleAction(input: SettleActionInput): Result<Entry<Decision>>
793
816
  if (!entry) return failure("not_found", "decision not found");
794
817
  if (!authorityDecisionMatches(authority, entry.data))
795
818
  return failure("permission_denied", "native settlement authority does not match decision");
796
- if (!provenanceMatches(entry.provenance, authority.provenance))
819
+ if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
797
820
  return failure("permission_denied", "native settlement caller does not match approval");
798
821
  if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
799
822
  return failure("permission_denied", "decision binding is invalid");
@@ -931,7 +954,7 @@ export function reconcileAction(input: ReconcileActionInput): Result<Entry<Decis
931
954
  return failure("not_found", "decision not found");
932
955
  if (
933
956
  !authorityDecisionMatches(authority, entry.data) ||
934
- !provenanceMatches(entry.provenance, authority.provenance)
957
+ !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance)
935
958
  )
936
959
  return failure("permission_denied", "native reconciliation caller does not match approval");
937
960
  if (
@@ -1,5 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
- import { readFileSync } from "node:fs";
2
+ import { readFileSync, realpathSync } from "node:fs";
3
+ import path from "node:path";
3
4
  import { resolveWorkspaceFrom, type WorkspaceConfig } from "./workspaces";
4
5
  import { configDir } from "./config";
5
6
  import { verifyPushIdentity } from "./branch";
@@ -13,9 +14,10 @@ import {
13
14
  type Revision,
14
15
  type TaskRecord,
15
16
  } from "./task-contract";
16
- import type { TaskStore } from "./task-store";
17
+ import { TaskStore } from "./task-store";
17
18
  import type { WorkitCore } from "./task-engine";
18
19
  import type { Decision } from "./task-contract";
20
+ import { currentWriterOwnsTask } from "./workers";
19
21
 
20
22
  /** Action classes auto-approval can cover. Unknown classes never match. */
21
23
  export const AUTO_CLASSES = ["branch", "commit", "push", "pr", "merge"] as const;
@@ -100,6 +102,18 @@ export const autoApproves = (root: string, cls: unknown, dir?: string): boolean
100
102
  const isStandingReceipt = (value: unknown): value is StandingReceipt =>
101
103
  typeof value === "object" && value !== null && (value as { kind?: unknown }).kind === "standing";
102
104
 
105
+ const targetOf = (root: string, descriptor: unknown): string | null => {
106
+ try {
107
+ const parsed = typeof descriptor === "string" ? JSON.parse(descriptor) : descriptor;
108
+ const cwd = (parsed as { payload?: { cwd?: unknown } })?.payload?.cwd;
109
+ if (cwd === undefined) return root;
110
+ if (typeof cwd !== "string" || !path.isAbsolute(cwd)) return null;
111
+ return realpathSync(cwd);
112
+ } catch {
113
+ return null;
114
+ }
115
+ };
116
+
103
117
  /**
104
118
  * A standing receipt authorizes only while the referenced workspace rule
105
119
  * still covers the class. Removing the flag (or narrowing the list) fails
@@ -110,11 +124,14 @@ export const standingApprovalLive = (
110
124
  receipt: unknown,
111
125
  cls: unknown,
112
126
  dir?: string,
127
+ descriptor?: unknown,
113
128
  ): boolean => {
114
129
  if (!isStandingReceipt(receipt)) return false;
115
130
  if (typeof cls !== "string" || !(AUTO_CLASSES as readonly string[]).includes(cls)) return false;
116
131
  if (receipt.class !== cls) return false;
117
- const resolved = resolveAutoApproval(root, dir);
132
+ const target = targetOf(root, descriptor);
133
+ if (!target) return false;
134
+ const resolved = resolveAutoApproval(target, dir);
118
135
  if (resolved.status !== "on") return false;
119
136
  if (resolved.workspace !== receipt.workspace) return false;
120
137
  return resolved.classes.includes(cls as AutoClass);
@@ -154,14 +171,10 @@ export const verifyStandingApproval = (
154
171
  ): Result<Provenance> => {
155
172
  if (task.status !== "active")
156
173
  return failure("invalid_transition", "only active tasks can authorize actions");
157
- const session = task.intent?.provenance?.session;
158
- if (
159
- !session ||
160
- session.kind !== "host" ||
161
- session.host !== caller.host ||
162
- session.handle !== caller.actor
163
- )
164
- return failure("permission_denied", "standing approval requires the lead session");
174
+ const workspace = new TaskStore(root).readWorkspace();
175
+ if (!workspace.ok) return workspace as Result<never>;
176
+ if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
177
+ return failure("permission_denied", "standing approval requires the current writer session");
165
178
  const standing = binding.standing;
166
179
  if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
167
180
  return failure("invalid_input", "standing approval needs a workspace and class");
@@ -177,7 +190,9 @@ export const verifyStandingApproval = (
177
190
  const cls = operationAutoClass(operation);
178
191
  if (!cls || cls !== standing.class)
179
192
  return failure("permission_denied", "standing approval does not cover this operation");
180
- const resolved = resolveAutoApproval(root);
193
+ const target = targetOf(root, binding.approvedContent);
194
+ if (!target) return failure("permission_denied", "standing action target is invalid");
195
+ const resolved = resolveAutoApproval(target);
181
196
  if (
182
197
  resolved.status !== "on" ||
183
198
  resolved.workspace !== standing.workspace ||
@@ -185,7 +200,7 @@ export const verifyStandingApproval = (
185
200
  )
186
201
  return failure("permission_denied", "standing auto-approval is not live for this operation");
187
202
  if (cls === "push") {
188
- const workspace = resolveWorkspaceFrom(root, configDir());
203
+ const workspace = resolveWorkspaceFrom(target, configDir());
189
204
  const provider = workspace?.vcs?.provider;
190
205
  const account = resolved.account;
191
206
  if (!provider || !account)
@@ -193,7 +208,7 @@ export const verifyStandingApproval = (
193
208
  "permission_denied",
194
209
  "auto-push requires a workspace provider and area account",
195
210
  );
196
- const identity = verifyPushIdentity(root, provider, account);
211
+ const identity = verifyPushIdentity(target, provider, account);
197
212
  if (!identity.ok) return failure("permission_denied", identity.error);
198
213
  }
199
214
  return success(null, null, {
@@ -201,7 +216,7 @@ export const verifyStandingApproval = (
201
216
  host: caller.host,
202
217
  session: { kind: "host", host: caller.host, handle: caller.actor },
203
218
  workerId: null,
204
- receipts: [standingReceiptFor(root, standing.workspace, cls)],
219
+ receipts: [standingReceiptFor(target, standing.workspace, cls)],
205
220
  } as Provenance);
206
221
  };
207
222
 
@@ -232,30 +247,26 @@ export const standingAutoApplies = (
232
247
  actor: string,
233
248
  operation: string,
234
249
  ): StandingMatch | null => {
235
- let descriptor: { operation?: unknown };
250
+ let descriptor: { operation?: unknown; payload?: unknown };
236
251
  try {
237
- descriptor = JSON.parse(operation) as { operation?: unknown };
252
+ descriptor = JSON.parse(operation) as { operation?: unknown; payload?: unknown };
238
253
  } catch {
239
254
  return null;
240
255
  }
241
256
  const cls = operationAutoClass(descriptor.operation);
242
- if (!cls || !autoApproves(store.root, cls)) return null;
257
+ const target = targetOf(store.root, descriptor);
258
+ if (!cls || !target || !autoApproves(target, cls)) return null;
243
259
  const listed = store.listTasks();
244
260
  const workspace = store.readWorkspace();
245
261
  if (!listed.ok || !workspace.ok || !workspace.data) return null;
246
- const matches = listed.data.filter(
247
- (task) =>
248
- task.status === "active" &&
249
- task.workspaceId === workspace.data!.id &&
250
- task.intent?.provenance?.session?.kind === "host" &&
251
- (task.intent.provenance.session as { host?: unknown; handle?: unknown }).host === host &&
252
- (task.intent.provenance.session as { host?: unknown; handle?: unknown }).handle === actor,
262
+ const task = listed.data.find((task) =>
263
+ currentWriterOwnsTask(task, workspace.data!, { host, actor }),
253
264
  );
254
- if (matches.length !== 1) return null;
255
- const resolved = resolveAutoApproval(store.root);
265
+ if (!task) return null;
266
+ const resolved = resolveAutoApproval(target);
256
267
  if (resolved.status !== "on") return null;
257
268
  return {
258
- task: matches[0],
269
+ task,
259
270
  workspaceId: workspace.data.id,
260
271
  workspaceRevision: workspace.data.revision,
261
272
  cls,
@@ -279,18 +290,19 @@ export const standingAutoBinding = (
279
290
  actor: string,
280
291
  operation: string,
281
292
  ): StandingAutoBinding | null => {
282
- let descriptor: { operation?: unknown };
293
+ let descriptor: { operation?: unknown; payload?: unknown };
283
294
  try {
284
- descriptor = JSON.parse(operation) as { operation?: unknown };
295
+ descriptor = JSON.parse(operation) as { operation?: unknown; payload?: unknown };
285
296
  } catch {
286
297
  return null;
287
298
  }
288
299
  const cls = operationAutoClass(descriptor.operation);
289
- if (!cls || !autoApproves(store.root, cls)) return null;
300
+ const target = targetOf(store.root, descriptor);
301
+ if (!cls || !target || !autoApproves(target, cls)) return null;
290
302
  const applies = standingAutoApplies(store, host, actor, operation);
291
303
  if (!applies) return null;
292
304
  const { task } = applies;
293
- const resolved = resolveAutoApproval(store.root);
305
+ const resolved = resolveAutoApproval(target);
294
306
  if (resolved.status !== "on") return null;
295
307
  const presented =
296
308
  `Workit decision: action — auto-approved ${String(descriptor.operation)} ` +