@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.
- package/package.json +1 -1
- package/skills/workit-babysit/SKILL.md +20 -26
- package/skills/workit-behavioral-tdd/SKILL.md +0 -5
- package/skills/workit-blast-radius/SKILL.md +0 -5
- package/skills/workit-challenge/SKILL.md +30 -47
- package/skills/workit-debug/SKILL.md +5 -27
- package/skills/workit-deslop/SKILL.md +0 -5
- package/skills/workit-diagram/SKILL.md +0 -5
- package/skills/workit-green-run/SKILL.md +0 -5
- package/skills/workit-handoff/SKILL.md +2 -8
- package/skills/workit-implement/SKILL.md +14 -20
- package/skills/workit-mockup/SKILL.md +0 -5
- package/skills/workit-plan/SKILL.md +30 -63
- package/skills/workit-review/SKILL.md +0 -5
- package/skills/workit-steer/SKILL.md +27 -27
- package/src/core/authority.ts +31 -8
- package/src/core/auto-approval.ts +44 -32
- package/src/core/branch.ts +293 -42
- package/src/core/config-conversion.ts +4 -4
- package/src/core/config-guard.ts +1 -7
- package/src/core/config.ts +1 -1
- package/src/core/cutover.ts +1207 -140
- package/src/core/doctor.ts +155 -82
- package/src/core/external-action-effects.ts +1102 -209
- package/src/core/external-action.ts +230 -103
- package/src/core/init.ts +25 -115
- package/src/core/legacy-ownership.ts +47 -0
- package/src/core/methods.ts +56 -52
- package/src/core/ports/pr-create.ts +23 -15
- package/src/core/pr-create.ts +374 -84
- package/src/core/registration.ts +0 -15
- package/src/core/repo-tools.ts +0 -1
- package/src/core/route-intent.ts +34 -82
- package/src/core/safe-write.ts +66 -1
- package/src/core/setup-state.ts +7 -0
- package/src/core/setup.ts +175 -82
- package/src/core/task-contract.ts +16 -3
- package/src/core/task-engine.ts +136 -31
- package/src/core/task-evaluation.ts +3 -5
- package/src/core/task-store.ts +159 -1
- package/src/core/tracker-issues.ts +55 -118
- package/src/core/vcs-config.ts +178 -187
- package/src/core/workers.ts +17 -0
- package/src/core/workspaces.ts +548 -46
- package/src/core.ts +1 -2
- package/src/core/ports/vcs-token-create-urls.ts +0 -4
package/src/core/authority.ts
CHANGED
|
@@ -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 (
|
|
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 (!
|
|
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
|
-
!
|
|
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
|
|
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
|
|
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
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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 (
|
|
255
|
-
const resolved = resolveAutoApproval(
|
|
265
|
+
if (!task) return null;
|
|
266
|
+
const resolved = resolveAutoApproval(target);
|
|
256
267
|
if (resolved.status !== "on") return null;
|
|
257
268
|
return {
|
|
258
|
-
task
|
|
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
|
-
|
|
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(
|
|
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)} ` +
|