@brainervirus/workit-core 1.2.1 → 1.3.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.
Files changed (45) 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 +6 -1
  17. package/src/core/auto-approval.ts +34 -14
  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 +886 -172
  25. package/src/core/external-action.ts +222 -64
  26. package/src/core/init.ts +25 -115
  27. package/src/core/legacy-ownership.ts +47 -0
  28. package/src/core/methods.ts +65 -52
  29. package/src/core/policy-resolver.ts +13 -5
  30. package/src/core/ports/pr-create.ts +23 -15
  31. package/src/core/pr-create.ts +374 -84
  32. package/src/core/registration.ts +0 -15
  33. package/src/core/repo-tools.ts +0 -1
  34. package/src/core/route-intent.ts +34 -82
  35. package/src/core/safe-write.ts +66 -1
  36. package/src/core/setup-state.ts +7 -0
  37. package/src/core/setup.ts +175 -82
  38. package/src/core/task-contract.ts +66 -6
  39. package/src/core/task-engine.ts +47 -2
  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/workspaces.ts +554 -46
  44. package/src/core.ts +3 -2
  45. package/src/core/ports/vcs-token-create-urls.ts +0 -4
package/src/core/init.ts CHANGED
@@ -6,7 +6,7 @@ import { PLUGIN_ROOT } from "./scripts";
6
6
  import { writeFileExclusive } from "./safe-write";
7
7
  import { resolveWorkspace, workspacesPath } from "./workspaces";
8
8
  import { applyWorkspaceBranchPolicy } from "./setup";
9
- import { vcsTokenCreateUrls, vcsVerifyToken } from "./vcs-config";
9
+ import { vcsConfig, vcsVerifyToken } from "./vcs-config";
10
10
  import { youTrackTokenCreateUrl, youTrackVerifyToken } from "./youtrack";
11
11
 
12
12
  const TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
@@ -181,32 +181,28 @@ export function initStatusData(configDirPath = configDir()): Record<string, any>
181
181
  items.push(youtrackTokenItem);
182
182
 
183
183
  let vcsCfg: Record<string, any> | null = null;
184
- let tokenCreateUrls: Record<string, any> | null = null;
185
184
  const vcsParsed = readJson(vcsJson);
186
185
  if (vcsParsed) {
187
186
  const rawProvider = vcsParsed.provider;
187
+ const effective = vcsConfig("resolve", process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd());
188
188
  const provider =
189
- typeof rawProvider === "string" && rawProvider.trim() ? rawProvider.toLowerCase() : null;
190
- const tokenFiles: Record<string, string> = {};
191
- for (const k of ["gitlab", "github"]) {
192
- tokenFiles[k] = String(vcsParsed[k]?.tokenFile ?? path.join(configDirPath, `${k}.token`));
193
- }
189
+ effective?.ok && typeof effective.provider === "string"
190
+ ? effective.provider
191
+ : typeof rawProvider === "string" && rawProvider.trim()
192
+ ? rawProvider.toLowerCase()
193
+ : null;
194
194
  vcsCfg = {
195
195
  config_edit_path: resolvePath(vcsJson),
196
196
  provider,
197
- defaultTargetBranch: vcsParsed.defaultTargetBranch ?? "develop",
197
+ defaultTargetBranch: effective.ok
198
+ ? effective.defaultTargetBranch
199
+ : (vcsParsed.defaultTargetBranch ?? "develop"),
198
200
  pr: vcsParsed.pr ?? {},
199
- tokenDefaults: vcsParsed.tokenDefaults,
200
201
  gitlab: vcsParsed.gitlab,
201
202
  github: vcsParsed.github,
202
203
  skill: "/wk-pr",
203
204
  switchHint: 'Set "provider" to "gitlab" or "github" in vcs.json',
204
205
  };
205
- tokenCreateUrls = vcsTokenCreateUrls();
206
- if (!("error" in vcsCfg)) {
207
- vcsCfg.tokenCreate = tokenCreateUrls.active;
208
- vcsCfg.tokenCreateUrls = { gitlab: tokenCreateUrls.gitlab, github: tokenCreateUrls.github };
209
- }
210
206
  } else if (fs.existsSync(vcsJson)) {
211
207
  vcsCfg = { config_edit_path: resolvePath(vcsJson), error: "invalid vcs.json" };
212
208
  }
@@ -220,54 +216,6 @@ export function initStatusData(configDirPath = configDir()): Record<string, any>
220
216
  fix: "workit_init_apply action=vcs_scaffold",
221
217
  });
222
218
 
223
- const provActive = vcsCfg && !("error" in vcsCfg) ? vcsCfg.provider : null;
224
- const tokenItem = (tid: string, label: string, rawPath: string, providerKey: string) => {
225
- const t = path.isAbsolute(rawPath) ? rawPath : path.resolve(configDirPath, rawPath);
226
- const abs = fs.existsSync(t) ? resolvePath(t) : path.resolve(t);
227
- const text = fs.existsSync(t) ? fs.readFileSync(t, "utf8").trim() : "";
228
- const ph = isPlaceholder(text);
229
- const ok = fs.existsSync(t) && modeOk(t) && Boolean(text) && !ph;
230
- const item: Record<string, any> = {
231
- id: tid,
232
- label,
233
- ok,
234
- path: abs,
235
- token_edit_path: abs,
236
- placeholder: ph,
237
- fix: `Open ${abs} — replace ${TOKEN_PLACEHOLDER}, save, then /wk-status`,
238
- required: provActive === providerKey,
239
- };
240
- if (tokenCreateUrls) {
241
- const block = tokenCreateUrls[providerKey] ?? {};
242
- if (block.createUrl) item.token_create_url = block.createUrl;
243
- if (block.createUrlClassic) item.token_create_url_classic = block.createUrlClassic;
244
- if (block.scopes) item.token_scopes = block.scopes;
245
- if (block.permissions) item.token_permissions = block.permissions;
246
- if (block.name) item.token_name = block.name;
247
- }
248
- return item;
249
- };
250
- const vcsTokenFiles: Record<string, string> = {};
251
- for (const k of ["gitlab", "github"]) {
252
- vcsTokenFiles[k] = String(vcsParsed?.[k]?.tokenFile ?? path.join(configDirPath, `${k}.token`));
253
- }
254
- items.push(
255
- tokenItem(
256
- "gitlab_token",
257
- "GitLab token (mode 600, not placeholder)",
258
- vcsTokenFiles.gitlab,
259
- "gitlab",
260
- ),
261
- );
262
- items.push(
263
- tokenItem(
264
- "github_token",
265
- "GitHub token (mode 600, not placeholder)",
266
- vcsTokenFiles.github,
267
- "github",
268
- ),
269
- );
270
-
271
219
  return {
272
220
  config_dir: configDirPath,
273
221
  plugin_root: PLUGIN_ROOT,
@@ -290,11 +238,6 @@ export async function toolkitStatusData(configDirPath = configDir()): Promise<Re
290
238
  typeof vcsCfg.provider === "string" && vcsCfg.provider.trim()
291
239
  ? vcsCfg.provider.toLowerCase()
292
240
  : null;
293
- const vcsTokenId =
294
- provider === "gitlab" ? "gitlab_token" : provider === "github" ? "github_token" : null;
295
- const vcsTokenItem =
296
- (vcsTokenId ? status.items.find((i: Record<string, any>) => i.id === vcsTokenId) : null) ?? {};
297
- const vcsPlaceholder = vcsTokenId ? (vcsTokenItem.placeholder ?? true) : true;
298
241
  const vcsJsonOk = Boolean(status.items.find((i: Record<string, any>) => i.id === "vcs_json")?.ok);
299
242
 
300
243
  const verify = placeholder
@@ -306,16 +249,13 @@ export async function toolkitStatusData(configDirPath = configDir()): Promise<Re
306
249
  ok: false,
307
250
  error: "vcs provider unconfigured — set provider to gitlab or github in vcs.json",
308
251
  }
309
- : vcsPlaceholder
310
- ? { ok: false, error: "vcs token still placeholder YOUR_TOKEN_HERE" }
311
- : await vcsVerifyToken();
252
+ : await vcsVerifyToken();
312
253
  const youTrackHealth = ("data" in verify ? verify.data : verify) as Record<string, any>;
313
254
 
314
255
  status.youtrack_verify = verify;
315
256
  status.youtrack_ok = placeholder ? false : Boolean(youTrackHealth.ok);
316
257
  status.vcs_verify = vcsVerify;
317
- status.vcs_ok =
318
- vcsJsonOk && !vcsPlaceholder ? Boolean((vcsVerify as Record<string, any>).ok) : false;
258
+ status.vcs_ok = vcsJsonOk && Boolean((vcsVerify as Record<string, any>).ok);
319
259
 
320
260
  const fsReady = status.items.every((i: Record<string, any>) => i.required === false || i.ok);
321
261
  status.ready = fsReady && Boolean(status.youtrack_ok) && (!vcsJsonOk || Boolean(status.vcs_ok));
@@ -328,13 +268,10 @@ export async function toolkitStatusData(configDirPath = configDir()): Promise<Re
328
268
  status.next_step = createUrl
329
269
  ? "Open the YouTrack create-token URL, New token (name workit, scope YouTrack), paste into the token file, save, then run /wk-status"
330
270
  : "Open the YouTrack token file, replace YOUR_TOKEN_HERE, save, then run /wk-status";
331
- } else if (vcsJsonOk && vcsPlaceholder) {
332
- status.vcs_token_edit_path = vcsTokenItem.token_edit_path ?? "";
333
- status.next_step = `Open ${vcsTokenItem.token_edit_path ?? ""}, paste your ${provider} token, save, then run /wk-status`;
334
271
  } else if (!status.youtrack_ok) {
335
272
  status.next_step = "Fix YouTrack token or re-run /wk-init";
336
273
  } else if (vcsJsonOk && !status.vcs_ok) {
337
- status.next_step = `Fix ${provider} token in vcs config or re-run /wk-init`;
274
+ status.next_step = `Run ${provider === "gitlab" ? "glab" : "gh"} auth login/switch, then /wk-status`;
338
275
  } else if (status.ready) {
339
276
  status.next_step = "All checks passed";
340
277
  }
@@ -376,31 +313,22 @@ const youtrackJsonContent = (dir: string): Record<string, any> => ({
376
313
  },
377
314
  });
378
315
 
379
- const vcsJsonContent = (dir: string, cwd?: string): Record<string, any> => {
316
+ const vcsJsonContent = (): Record<string, any> => {
380
317
  // Explicit provider at init: env wins, else the checkout's origin remote
381
318
  // (same RL-03b rule as vcs-config). Unresolvable means the field is
382
319
  // omitted — never a silent assumption downstream.
383
- const provider = resolveInitProvider(cwd);
320
+ const provider = resolveInitProvider();
384
321
  return {
385
322
  ...(provider ? { provider } : {}),
386
323
  defaultTargetBranch: process.env.WORKFLOW_VCS_TARGET_BRANCH ?? "develop",
387
324
  gitlab: {
388
325
  host: process.env.WORKFLOW_GITLAB_HOST ?? "gitlab.com",
389
326
  apiUrl: process.env.WORKFLOW_GITLAB_API_URL ?? "https://gitlab.com/api/v4",
390
- tokenFile: path.join(dir, "gitlab.token"),
391
327
  },
392
328
  github: {
393
329
  host: process.env.WORKFLOW_GITHUB_HOST ?? "github.com",
394
- tokenFile: path.join(dir, "github.token"),
395
330
  },
396
331
  pr: { squashOnMerge: true, removeSourceBranch: true, pushBranch: true, confirmSkip: true },
397
- tokenDefaults: {
398
- name: "workit",
399
- description: "OpenCode workit — /wk-pr and glab/gh",
400
- gitlabScopes: ["api"],
401
- githubPermissions: { pull_requests: "write", contents: "write", metadata: "read" },
402
- githubClassicScopes: ["repo"],
403
- },
404
332
  };
405
333
  };
406
334
 
@@ -494,15 +422,7 @@ export function initApplyData(
494
422
  }
495
423
  case "vcs_scaffold": {
496
424
  const jsonOut = path.join(dir, "vcs.json");
497
- fs.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(dir), null, 2) + "\n", "utf8");
498
- const glPath = path.join(dir, "gitlab.token");
499
- const ghPath = path.join(dir, "github.token");
500
- const preservedTokens: string[] = [];
501
- for (const p of [glPath, ghPath]) {
502
- if (writeFileExclusive(p, TOKEN_PLACEHOLDER + "\n", 0o600) === "preserved") {
503
- preservedTokens.push(path.resolve(p));
504
- }
505
- }
425
+ fs.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + "\n", "utf8");
506
426
  const configPath = path.resolve(jsonOut);
507
427
  const prev = process.env.WORKFLOW_VCS_CONFIG;
508
428
  process.env.WORKFLOW_VCS_CONFIG = configPath;
@@ -510,39 +430,23 @@ export function initApplyData(
510
430
  const cfg = readJson(configPath) ?? {};
511
431
  const rawProvider = cfg.provider;
512
432
  const provider = typeof rawProvider === "string" && rawProvider.trim() ? rawProvider : null;
513
- const tokenUrls = vcsTokenCreateUrls();
514
- const active = tokenUrls.active ?? {};
515
- const activePath =
516
- provider === "gitlab"
517
- ? path.resolve(glPath)
518
- : provider === "github"
519
- ? path.resolve(ghPath)
520
- : null;
521
433
  return {
522
434
  action,
523
435
  ok: true,
524
436
  vcs_json: configPath,
525
437
  config_edit_path: configPath,
526
- gitlab_token: path.resolve(glPath),
527
- github_token: path.resolve(ghPath),
528
- token_edit_path: activePath,
529
- token_create_url: active.createUrl,
530
- token_create_urls: tokenUrls,
531
- preserved_tokens: preservedTokens,
532
438
  vcs_config: {
533
439
  config_edit_path: configPath,
534
440
  provider,
535
441
  defaultTargetBranch: cfg.defaultTargetBranch,
536
442
  pr: cfg.pr,
537
- tokenCreate: active,
538
- tokenCreateUrls: { gitlab: tokenUrls.gitlab, github: tokenUrls.github },
539
- switchHint: 'Change "provider" to "github" when you migrate — token files are separate',
443
+ switchHint: 'Change "provider" to "github" when you migrate; authenticate with gh/glab',
540
444
  skill: "/wk-pr",
541
445
  },
542
446
  instruction:
543
447
  provider === null
544
448
  ? `No provider resolved — set provider to gitlab or github in ${configPath}, then /wk-status.`
545
- : `Open the create-token URL for ${provider}, click Create, paste into ${activePath}, then /wk-status.`,
449
+ : `Run ${provider === "gitlab" ? "glab" : "gh"} auth login, then /wk-status.`,
546
450
  };
547
451
  } finally {
548
452
  if (prev === undefined) delete process.env.WORKFLOW_VCS_CONFIG;
@@ -565,9 +469,15 @@ export function initApplyData(
565
469
 
566
470
  export function initStatus(): Record<string, any> {
567
471
  const data = initStatusData();
472
+ const workspaces: Record<string, any> = { path: workspacesPath(), resolved: null };
473
+ try {
474
+ workspaces.resolved = resolveWorkspace(process.cwd());
475
+ } catch (error) {
476
+ workspaces.error = error instanceof Error ? error.message : String(error);
477
+ }
568
478
  return {
569
479
  ...data,
570
- workspaces: { resolved: resolveWorkspace(process.cwd()), path: workspacesPath() },
480
+ workspaces,
571
481
  };
572
482
  }
573
483
 
@@ -0,0 +1,47 @@
1
+ // SHA-256 of generated Cursor assets in the published 1.2.1 package. These
2
+ // hashes prove ownership for a narrow, known upgrade source; filenames alone
3
+ // never authorize replacing an installed file.
4
+ export const LEGACY_CURSOR_ASSET_DIGESTS: Readonly<Record<string, string>> = {
5
+ "commands/wk-babysit.md": "002b00bba340ace6bf64a605f7e26c56b6224010529dbf5fd8966102f0065553",
6
+ "commands/wk-blast-radius.md": "474030101d592fe61931baffd50158bf58987eb65902a594df26e53bd62e1d57",
7
+ "commands/wk-challenge.md": "65f3e3bf5ce2d2ec0b77d2fe061894a53c3de98ee5d00cba8cf6a8259b4c6f46",
8
+ "commands/wk-debug.md": "8a67a77da27df3e6416ec685f360c79537feac10c324baf4a2d6f3a0329d7623",
9
+ "commands/wk-deslop.md": "8d31b708225fcc8b6de12a8ce8b3e42c74a20821f1b86d347d969e0906bc1397",
10
+ "commands/wk-diagram.md": "d83a8b4b08f2e9ae30e7ee372b89ed400193ca6cc5fc918087b47283efe2b4a6",
11
+ "commands/wk-green-run.md": "0de645228cedb650ef24ade84d1800891a83ce39a568bc5f04f036542e1c0a4a",
12
+ "commands/wk-handoff.md": "525e3ea4717593826d6a2daf72bfdd35602b33dd77c6d03166e865ad29c65592",
13
+ "commands/wk-implement.md": "9e74766ba1829dbf04cc95e0c4d96fbeaafef57708ce6ff0d027a9ffad6de4c5",
14
+ "commands/wk-mockup.md": "9c441ada81c2b90510dd57804d25fc6b9ccc4f28870d98d1a3f2327e2b6564a4",
15
+ "commands/wk-plan.md": "8b3014450cdb1099f13628e983ce3b3d1566d8594c4bde8b31d74e8587b918ea",
16
+ "commands/wk-review.md": "4260e80e556a1b3042a3990cf1a53a4cb8ea74078667e5c24269f10c58f25f0e",
17
+ "commands/wk-steer.md": "251795b24df1395a4786d1565a2ab32a1f1d5321d33fbc3175bbf917a9c4ea1a",
18
+ "commands/wk-tdd.md": "b3e00a37d9ea1c0c133fc86aaf3c56b8d38bb443aded2ad05a06a9cc762c86f5",
19
+ "rules/workit-contract.mdc": "a7425e888f19c8984f784cc6395800f33ac094525863a894e5fba516ff7ff450",
20
+ "skills/workit-babysit/SKILL.md":
21
+ "a8958f7042be13048756a0c6ad7415feaf356132431b1d8b6e0f6617e34281a3",
22
+ "skills/workit-behavioral-tdd/SKILL.md":
23
+ "78207d684f9f50fa9c6d3ab68e425f71aa12975414c874975ce2daf427b9cf9d",
24
+ "skills/workit-blast-radius/SKILL.md":
25
+ "e829d3106a2b65c3f8aeceb53a1dbd3fadacbf1b1e1380a432e5e5f997e8a5a5",
26
+ "skills/workit-challenge/SKILL.md":
27
+ "60273fc7bdf7920e88e19c9d448f8d74a3e881dcce5f726f6aa947e4fc3881cf",
28
+ "skills/workit-debug/SKILL.md":
29
+ "e2776294e5ed3dc3c5c9d6c0d6321e53204cc4f0a7206424d32dca862d47d748",
30
+ "skills/workit-deslop/SKILL.md":
31
+ "e917dabad0fade7ee29bb4de36fd0bfeb9235d063951c3adad2f501c2d880f15",
32
+ "skills/workit-diagram/SKILL.md":
33
+ "d4a46c95d38b77e05f2246a3ab20b6ce8a5c5a2125df7c4ed5d4b79398307f9e",
34
+ "skills/workit-green-run/SKILL.md":
35
+ "f1bf1af81acd33c72f23a3f803a40c737bdd9e1a1889fb2c2b41ab9336679c4f",
36
+ "skills/workit-handoff/SKILL.md":
37
+ "855fc35166bb8886a1adca17f638ddb85508f36671bb034e00e1e0e2b403614a",
38
+ "skills/workit-implement/SKILL.md":
39
+ "7ba5adb7d6cf2acc34d25b10164e2af6b9f99d3bc5ef20ff5409b75e03e98bd4",
40
+ "skills/workit-mockup/SKILL.md":
41
+ "56fc4046599150874b498b269873d1522e671e72b17257fe312a4786205e15c3",
42
+ "skills/workit-plan/SKILL.md": "6283fdcc6c11c0267e679ce4eefbdd67e7fb5b819521dd86d9908a93bf78482e",
43
+ "skills/workit-review/SKILL.md":
44
+ "0f7c662abedd167589d3974013ff805df650dfc881d9709dfcfa4124c0dbe58d",
45
+ "skills/workit-steer/SKILL.md":
46
+ "5e7142010675d55c21ef84fc2b077f06163c27f95ff4d9ae22a7fda13c7ca543",
47
+ };
@@ -87,56 +87,69 @@ export function selectMethods(policy: Policy, capabilities: Capability[]): Selec
87
87
 
88
88
  export const invariantBootstrap = (): string =>
89
89
  `
90
- Workit keeps one accountable lead and one shared task state. Inspect current task
91
- state before acting. An empty task.list means no session yet, not permission to
92
- skip Workit; for user-requested product, debug, or behavior work with no active
93
- or paused task, run task.start then policy.assess before other product mutations.
94
- Omitted expectedRevision, expectedWorkspaceRevision, and writer workerId default
95
- to the current records; explicit values are still concurrency-checked, so never
96
- copy revisions between calls. Happy path: task.list, task.start {intent} with no
97
- revisions, policy.assess {assessment: {facts, signals, consequences,
98
- verification}} where facts are inferred or observed with file refs,
99
- writer.acquire {taskId} before product writes, evidence.record {evidence} where
100
- check and review kinds auto-bind the current tree and need no digests. Record
101
- self-review requirements with kind review and reviewContext matching your own
102
- host session; the lead may also record checks. Fresh-context review requires a
103
- session distinct from the task creator and every other evidence recorder. Record
104
- verification evidence last: later evidence or commits can reopen fixed findings
105
- for re-verification, so verify, resolve, then close with nothing in between.
106
- decision.record {binding with taskId/workspaceId from inspect plus presented and
107
- approvedContent} only through a native approval question asked receipt-shaped:
108
- header Workit decision: <purpose>, the same label repeated in the question
109
- text (some host UIs do not render headers), exactly two options approved and
110
- rejected; ask once, never re-ask to mint a receipt. Before asking, present the
111
- item in the conversation (durable artifacts: complete digest plus exact path;
112
- inline plans: the content) and keep the question to one short scoped sentence;
113
- never ask to approve something unseen. An answer with custom text is steering,
114
- not approval: adjust, re-present, then ask a new scoped question. After the user
115
- approves a spec or plan, execute the remaining tasks continuously with one
116
- atomic commit per task and do not ask whether to continue; stop only for a new
117
- product decision, a failed safety or verification gate, a conflicting edit, or
118
- missing authority. Record the plan's commit list once through the plan-scoped
119
- action approval (git.commit with plan_steps and plan_branch) so each listed
120
- commit executes without a new question; an unlisted message needs a fresh
121
- approval. task.close {outcome,
122
- summary, decisionIds}. A completed native subagent run stops its bound worker
123
- by itself; if a worker strands in cancelling with its run verifiably over,
124
- repeat worker.cancel {reason} to confirm the stop. Check and review evidence
125
- without a bound candidate goes stale; findings close only as fixed with passing
126
- verification, dismissed with supporting evidence, or deferred under an approved
127
- limitation. Use only the
128
- shared operations for task, policy, evidence, finding, decision, worker, writer,
129
- and state changes. Authority is bounded by the requested scope, current
130
- revision, caller/session provenance, and observed capabilities. Never claim host
131
- enforcement or evidence that the host cannot provide. Shell-executed writes are
132
- unattested agent-guided work even when a writer is held. Preserve unresolved
133
- requirements, gaps, and uncertain workers. Skill routing: slash aliases /wk-*
134
- always load on demand. Model-invoked: new instructions, interruptions, or
135
- forgotten items mid-task → load workit-steer before responding; about to open
136
- a PR or fresh from implementing → load workit-deslop; CI red → load
137
- workit-green-run; small-looking change before close or merge → load
138
- workit-blast-radius; ambiguous, consequential, or disputed proposal before
139
- recommending → load workit-challenge; spec or plan needs a flow diagram →
140
- load workit-diagram, a UI sketch → load workit-mockup. Load the skill; never
141
- act from memory of it.
90
+ Workit is optional coordination and repository-policy tooling around the host,
91
+ not a permission system or a mandatory workflow. Native host allow/ask/deny,
92
+ sandbox, plan/read-only mode, and organization rules remain authoritative.
93
+ Ordinary investigation, questions, non-Git work, and routine reversible edits
94
+ need zero Workit task, assessment, or writer calls. Use one compact tracked
95
+ record only when handoff, dependent steps, concurrent actors, or meaningful
96
+ decisions make continuity useful; similar titles alone never merge tasks.
97
+
98
+ For a routine user-authorized branch or commit, prefer native host Git/shell
99
+ tools from the outset when managed coordination or outcome reconciliation is
100
+ not needed. Inspect the actual target checkout and its configured conventions;
101
+ use Workit's read-only context/policy tools when needed. Do not start a task,
102
+ acquire a writer, or mint a decision receipt solely for a native Git action.
103
+ Native permissions still apply. Never switch execution paths to evade a denial
104
+ or repeat an uncertain managed effect. A local-commit endpoint does not imply
105
+ PR readiness, a fresh review, or post-commit assessment/closure paperwork.
106
+
107
+ For tracked work, inspect its current state and use the shared operations for
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
110
+ when policy selection or changed evidence/constraints requires it. Omitted
111
+ expectedRevision and expectedWorkspaceRevision use current values; explicit
112
+ values are still concurrency-checked, so never copy revisions between calls.
113
+ A solo edit does not need writer acquisition; use it when concurrent checkout
114
+ writers need coordination. Record only observed facts and checks. Evidence can
115
+ become stale when its bound candidate changes; reconcile findings against the
116
+ current candidate before recording completion.
117
+
118
+ Workit validates domain policy against the actual action target, configured
119
+ account, branch and commit conventions, protected refs, and current repository
120
+ state. The task directory is coordination state, not a boundary on which
121
+ repository may be changed. Use an action-time cwd target where a managed
122
+ Git/hosting action supports it; a non-Git directory remains valid for OS work.
123
+ GitHub/GitLab use the active gh/glab CLI identity; YouTrack uses its own token.
124
+ Preserve uncertain external outcomes and reconcile repository/provider state
125
+ before retrying. Internal reservations prevent duplicate or ambiguous effects;
126
+ they are not permission tickets for every edit.
127
+
128
+ Use host-native authorization through the host's supported path. A native
129
+ question receipt records an actual question interaction; it is not automatic
130
+ host permission. Record a meaningful user choice once with provenance when
131
+ future retrieval helps. Never fabricate a receipt, re-ask only to mint one, or
132
+ let imported decisions grant authority. Never turn a host deny into allow or
133
+ claim enforcement/evidence a host cannot provide. A precise request with settled
134
+ constraints does not need an interview. Present genuine unresolved options with
135
+ evidence and a recommendation, then continue toward the requested delivery
136
+ endpoint: investigation, implementation, PR/MR-ready, merge, or release. Run
137
+ applicable checks and safe repairs without repeated continuation questions;
138
+ stop for missing host authority, a new consequential choice, a conflicting edit,
139
+ or an unresolved blocker. A tracked record may close when actual evidence
140
+ supports its outcome; no final human closure ceremony is required.
141
+
142
+ A completed native subagent run stops its bound worker by itself; if a worker
143
+ strands in cancelling with its run verifiably over, repeat worker.cancel to
144
+ confirm the stop. Preserve unresolved requirements, gaps, and uncertain workers.
145
+ When context changes, distinguish a quick question, same-task adjustment, and
146
+ separate request. Answer a quick question without pausing/resuming task state;
147
+ park a concise checkpoint only when substantial work needs to continue later.
148
+
149
+ Skill routing: slash aliases /wk-* load on demand. Use workit-steer for a
150
+ substantial interruption or change of direction, workit-deslop when relevant
151
+ to a PR-ready endpoint, workit-green-run for failing CI, workit-blast-radius
152
+ when impact is uncertain, and workit-challenge for genuinely open consequential
153
+ choices. Load workit-plan when dependencies or handoff need durable next actions.
154
+ Load the skill; never act from memory of it.
142
155
  `.trim();
@@ -326,6 +326,11 @@ function resolveRequirements(input: NormalizedResolverInput): Requirement[] {
326
326
  }),
327
327
  );
328
328
 
329
+ const independentBehaviorReview =
330
+ input.preferences?.thorough === true ||
331
+ input.assessment.consequences.some((consequence) =>
332
+ ["security", "data", "public_contract", "operations"].includes(consequence.area),
333
+ );
329
334
  if (signals.behaviorChange.value === true)
330
335
  requirements.push(
331
336
  requirement({
@@ -340,15 +345,18 @@ function resolveRequirements(input: NormalizedResolverInput): Requirement[] {
340
345
  acceptanceAllowed: false,
341
346
  }),
342
347
  requirement({
343
- ruleId: "fresh-context-review",
348
+ ruleId: independentBehaviorReview ? "fresh-context-review" : "self-review",
344
349
  dimension: "review",
345
350
  scope,
346
- reason: "A non-mechanical behavior change requires fresh-context review.",
347
- satisfaction:
348
- "A separate review context examines the current candidate and records its result.",
351
+ reason: independentBehaviorReview
352
+ ? "Consequential behavior or the thorough preference requires fresh-context review."
353
+ : "Bounded behavior changes use self-review unless consequential risks or trusted rules require more.",
354
+ satisfaction: independentBehaviorReview
355
+ ? "A separate review context examines the current candidate and records its result."
356
+ : "The lead reviews the resulting diff and records the relevant scope and checks.",
349
357
  before: "close",
350
358
  dependentAction: null,
351
- acceptanceAllowed: false,
359
+ acceptanceAllowed: !independentBehaviorReview,
352
360
  }),
353
361
  );
354
362
  else if (signals.mechanicalLowRisk.value === true)
@@ -1,22 +1,30 @@
1
1
  // CLI port of scripts/pr-create.sh — create | --build-body.
2
- import { prBuildBody, prCreate } from "../pr-create";
2
+ //
3
+ // Decision ae03c569 re-enabled hosted PR/MR creation with pre/post provider SHA
4
+ // verification and accepted the residual non-atomic source-SHA race. The legacy
5
+ // create mode still cannot mint the native action receipt, the one-time action
6
+ // reservation, or the target writer lease the shared contract requires, so it
7
+ // never performs a hosted create itself. It returns the same needs_input shape
8
+ // as the headless `workit action` route and points at the reserved,
9
+ // TTY-confirmed shared path.
10
+ import { prBuildBody } from "../pr-create";
3
11
 
4
12
  const mode = process.argv[2] ?? "create";
5
13
  if (mode === "--build-body") {
6
14
  console.log(JSON.stringify({ body: prBuildBody(process.env, process.cwd()) }));
7
15
  process.exit(0);
8
16
  }
9
- if (process.env.WF_PR_CONFIRMED !== "true") {
10
- console.error("ERROR: confirmed=true required");
11
- process.exit(1);
12
- }
13
- if (!process.env.WF_PR_TITLE) {
14
- console.error("ERROR: title required");
15
- process.exit(1);
16
- }
17
- const out = prCreate(process.env, process.cwd());
18
- if (out.error || out.ok === false) {
19
- console.log(JSON.stringify(out));
20
- process.exit(1);
21
- }
22
- console.log(JSON.stringify(out));
17
+ console.log(
18
+ JSON.stringify({
19
+ ok: false,
20
+ schemaVersion: 1,
21
+ code: "needs_input",
22
+ error: "hosted PR/MR creation requires the reserved shared action path with TTY confirmation",
23
+ details: {
24
+ operation: "hosting.pull_request",
25
+ guidance:
26
+ "Run `workit action hosting.pull_request --payload <file> --confirm` from the target checkout: it records the one-time action reservation behind the terminal confirmation and verifies the provider PR head against the approved source SHA before reporting success.",
27
+ },
28
+ }),
29
+ );
30
+ process.exit(1);