@brainervirus/workit-core 0.10.0 → 0.11.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-core",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "private": false,
5
5
  "description": "Workit — workflow rails for agentic coding: specs, plans, YouTrack, CI-gated commits (shared core)",
6
6
  "keywords": [
@@ -13,7 +13,7 @@ import {
13
13
  unlinkSync,
14
14
  writeFileSync,
15
15
  } from "node:fs";
16
- import { createHash } from "node:crypto";
16
+ import { createHash, randomBytes } from "node:crypto";
17
17
  import path from "node:path";
18
18
  import { docsValidate, parseTasksFromPlan, qualitySpec, stripFences } from "./docs-validate";
19
19
  import { resolveCanonicalLayout } from "./docs-layout";
@@ -64,6 +64,27 @@ export type FlowExecutionState = {
64
64
  * every non-subagent-driven path and for legacy states without the field.
65
65
  */
66
66
  coordinator_session_id: string | null;
67
+ /**
68
+ * Cursor delegation capability state (cursor-subagent-inline CA-01..CA-05):
69
+ * only SHA-256 hashes are persisted — never a raw lease or token. Cursor has
70
+ * no host-visible parentID, so an accepted Cursor `subagent-driven` menu
71
+ * choice stores a coordinator-lease hash and returns the raw lease ONCE;
72
+ * `mintDelegateToken` stores one task-scoped token hash and returns the raw
73
+ * token once. Optional and absent (never serialized) for OpenCode/CLI flows
74
+ * and legacy states so pre-delegation flow.json bytes stay stable.
75
+ */
76
+ delegation?: FlowDelegationState | null;
77
+ };
78
+
79
+ export type FlowDelegationState = {
80
+ coordinator_lease_hash: string | null;
81
+ /** The task id the active token is bound to (one active token per flow). */
82
+ active_task_id: number | null;
83
+ token_hash: string | null;
84
+ /** Workspace root + slug the token hash is bound to (binding check). */
85
+ token_workspace: string | null;
86
+ token_slug: string | null;
87
+ status: "active" | "revoked";
67
88
  };
68
89
 
69
90
  /**
@@ -94,16 +115,6 @@ export const COORDINATOR_RECOVERY_TEXT =
94
115
  "Delegate product mutations to an authenticated delegated worker via `task` / `wk-implement` instead of " +
95
116
  "editing in the coordinator session.";
96
117
 
97
- /**
98
- * Cursor recovery guidance for the unsupported subagent-driven mutation path
99
- * (CA-42): the Cursor MCP has no child sessions, so it cannot run a
100
- * subagent-driven plan and must not enter that flow state.
101
- */
102
- export const CURSOR_SUBAGENT_UNSUPPORTED_TEXT =
103
- "Cursor cannot execute subagent-driven plans: the MCP has no child-session " +
104
- "support. Choose Inline, Handoff, or a review option in this session, or " +
105
- "run the plan in OpenCode with `wk-implement`.";
106
-
107
118
  /**
108
119
  * The only acceptable approval / execution-menu evidence (FG-04, CA-19, AR-12).
109
120
  * Trust comes from HOST CAPABILITIES, never from caller-supplied fields:
@@ -278,19 +289,53 @@ const normalizeState = (parsed: unknown, slug: string): FlowState => {
278
289
  mode: (execution.mode ?? null) as ExecutionMode | null,
279
290
  evidence: (execution.evidence ?? null) as LifecycleEvidence | null,
280
291
  coordinator_session_id: execution.coordinator_session_id ?? null,
292
+ // Optional field: legacy flow.json without the key stays byte-stable
293
+ // (no serialized `delegation` appears unless delegation state exists).
294
+ ...(execution.delegation !== undefined
295
+ ? { delegation: normalizeDelegation(execution.delegation) }
296
+ : {}),
281
297
  },
282
298
  handoff_destination: p.handoff_destination ?? false,
283
299
  updated_at: p.updated_at ?? Date.now(),
284
300
  };
285
301
  };
286
302
 
303
+ const normalizeDelegation = (v: unknown): FlowDelegationState | null => {
304
+ if (v === null || v === undefined) return null;
305
+ if (!isRecord(v)) return null;
306
+ const hash = (x: unknown): string | null =>
307
+ typeof x === "string" && HEX64_RE.test(x) ? x : null;
308
+ return {
309
+ coordinator_lease_hash: hash(v.coordinator_lease_hash),
310
+ active_task_id:
311
+ typeof v.active_task_id === "number" && Number.isSafeInteger(v.active_task_id)
312
+ ? v.active_task_id
313
+ : null,
314
+ token_hash: hash(v.token_hash),
315
+ token_workspace: typeof v.token_workspace === "string" ? v.token_workspace : null,
316
+ token_slug: typeof v.token_slug === "string" ? v.token_slug : null,
317
+ status: v.status === "active" ? "active" : "revoked",
318
+ };
319
+ };
320
+
321
+ // Cursor delegation persistence helper: raw lease/token values are hashed ONCE
322
+ // here and only the hex digest is ever placed on FlowState (CA-01 spec). The
323
+ // raw values are returned to the coordinator in tool results, never written.
324
+ const delegationHash = (raw: string): string => createHash("sha256").update(raw).digest("hex");
325
+
287
326
  const emptyState = (slug: string): FlowState => ({
288
327
  slug,
289
328
  activated: false,
290
329
  spec: { path: "", status: "draft", evidence: null, approved_digest: null },
291
330
  plan: { path: "", status: "draft", evidence: null, approved_digest: null },
292
331
  menu: { presented: false, chosen: "", evidence: null },
293
- execution: { status: "pending", mode: null, evidence: null, coordinator_session_id: null },
332
+ execution: {
333
+ status: "pending",
334
+ mode: null,
335
+ evidence: null,
336
+ coordinator_session_id: null,
337
+ delegation: null,
338
+ },
294
339
  handoff_destination: false,
295
340
  updated_at: Date.now(),
296
341
  });
@@ -478,6 +523,11 @@ const validateState = (
478
523
  evidence: (execRaw?.evidence as LifecycleEvidence | null | undefined) ?? null,
479
524
  coordinator_session_id:
480
525
  (execRaw?.coordinator_session_id as string | null | undefined) ?? null,
526
+ // Optional field: legacy flow.json without the key stays byte-stable
527
+ // (no serialized `delegation` appears unless delegation state exists).
528
+ ...(execRaw?.delegation !== undefined
529
+ ? { delegation: normalizeDelegation(execRaw.delegation) }
530
+ : {}),
481
531
  },
482
532
  handoff_destination: parsed.handoff_destination ?? false,
483
533
  updated_at: parsed.updated_at ?? Date.now(),
@@ -731,7 +781,13 @@ const resetForSpecDrift = (state: FlowState): FlowState => ({
731
781
  spec: { ...state.spec, status: "draft", evidence: null, approved_digest: null },
732
782
  plan: { ...state.plan, status: "draft", evidence: null, approved_digest: null },
733
783
  menu: { presented: false, chosen: "", evidence: null },
734
- execution: { status: "pending", mode: null, evidence: null, coordinator_session_id: null },
784
+ execution: {
785
+ status: "pending",
786
+ mode: null,
787
+ evidence: null,
788
+ coordinator_session_id: null,
789
+ ...(state.execution.delegation !== undefined ? { delegation: null } : {}),
790
+ },
735
791
  handoff_destination: false,
736
792
  updated_at: Date.now(),
737
793
  });
@@ -821,7 +877,12 @@ const deriveLegacyExecution = (
821
877
  coordinator_session_id: null,
822
878
  };
823
879
  }
824
- return { status: "pending", mode: null, evidence: null, coordinator_session_id: null };
880
+ return {
881
+ status: "pending",
882
+ mode: null,
883
+ evidence: null,
884
+ coordinator_session_id: null,
885
+ };
825
886
  };
826
887
 
827
888
  type CompatibilityResult = { state: FlowState; changed: boolean };
@@ -1605,7 +1666,12 @@ export const prepareFlowState = (
1605
1666
  spec: { path: specPath, status: "draft", evidence: null, approved_digest: null },
1606
1667
  plan: { path: planPath, status: "draft", evidence: null, approved_digest: null },
1607
1668
  menu: { presented: false, chosen: "", evidence: null },
1608
- execution: { status: "pending", mode: null, evidence: null, coordinator_session_id: null },
1669
+ execution: {
1670
+ status: "pending",
1671
+ mode: null,
1672
+ evidence: null,
1673
+ coordinator_session_id: null,
1674
+ },
1609
1675
  handoff_destination: false,
1610
1676
  updated_at: Date.now(),
1611
1677
  });
@@ -1753,6 +1819,8 @@ export const transitionPlan = (
1753
1819
  });
1754
1820
  };
1755
1821
 
1822
+ export type MenuChoiceResult = { ok: true; coordinator_lease?: string } | FlowError;
1823
+
1756
1824
  export const recordMenuChoice = (
1757
1825
  root: string,
1758
1826
  slug: string,
@@ -1760,7 +1828,7 @@ export const recordMenuChoice = (
1760
1828
  choice: unknown,
1761
1829
  evidence: unknown,
1762
1830
  ctx?: MutationContext,
1763
- ): FlowGateResult => {
1831
+ ): MenuChoiceResult => {
1764
1832
  const bound = assertMutationWorkspace(root, ctx);
1765
1833
  if (!bound.ok) return bound;
1766
1834
  const recorded = assertEvidenceShape(evidence);
@@ -1768,11 +1836,6 @@ export const recordMenuChoice = (
1768
1836
  if (typeof choice !== "string" || !MENU_CHOICES.includes(choice as MenuChoice)) {
1769
1837
  return err("menu_choice_invalid", `invalid menu choice: ${JSON.stringify(choice)}`);
1770
1838
  }
1771
- // Cursor cannot run subagent-driven plans (no child sessions): entering that
1772
- // flow state on Cursor is rejected with recovery guidance (CA-42).
1773
- if (recorded.evidence.host === "cursor" && choice === "subagent-driven") {
1774
- return err("unsupported_mode", CURSOR_SUBAGENT_UNSUPPORTED_TEXT);
1775
- }
1776
1839
  // The execution-menu evidence must be the label the user selected on the
1777
1840
  // native question; a mismatched choice is fabricated (FG-04). Comparison is
1778
1841
  // case-insensitive: the host presents "Inline", the enum stores "inline"
@@ -1789,7 +1852,13 @@ export const recordMenuChoice = (
1789
1852
  }
1790
1853
  const doc = resolveDoc(root, slug, planPath, "plan");
1791
1854
  if (!doc.ok) return err("path_invalid", doc.error);
1792
- return readModifyWrite(root, slug, (state) => {
1855
+ // The Cursor coordinator lease is generated BEFORE the critical section so
1856
+ // the raw value is returned exactly once and only the hash crosses the
1857
+ // persisted-state boundary.
1858
+ const cursorSubagent = recorded.evidence.host === "cursor" && choice === "subagent-driven";
1859
+ const lease = cursorSubagent ? randomBytes(32).toString("hex") : null;
1860
+ const leaseHash = lease === null ? null : delegationHash(lease);
1861
+ const result = readModifyWrite(root, slug, (state) => {
1793
1862
  if (state.spec.status !== "approved")
1794
1863
  return err("spec_not_approved", "spec must be approved before the execution menu");
1795
1864
  if (state.plan.status !== "approved")
@@ -1808,13 +1877,24 @@ export const recordMenuChoice = (
1808
1877
  // pending. The menu evidence IS the lifecycle evidence — the choice the
1809
1878
  // user selected on the native question. The activating OpenCode
1810
1879
  // coordinator session (CA-12) is persisted ONLY for an accepted
1811
- // subagent-driven activation; inline/handoff/review choices and Cursor's
1812
- // rejected subagent path keep it null.
1880
+ // subagent-driven activation; inline/handoff/review choices keep it null.
1881
+ // Cursor's accepted subagent-driven path keeps it null too (no session
1882
+ // identity): delegation authority comes from the coordinator lease instead.
1813
1883
  const executing = choice === "subagent-driven" || choice === "inline";
1814
1884
  const coordinatorSessionId =
1815
1885
  choice === "subagent-driven" && recorded.evidence.host === "opencode"
1816
1886
  ? (ctx?.sessionId ?? null)
1817
1887
  : null;
1888
+ const delegation = cursorSubagent
1889
+ ? {
1890
+ coordinator_lease_hash: leaseHash,
1891
+ active_task_id: null,
1892
+ token_hash: null,
1893
+ token_workspace: null,
1894
+ token_slug: null,
1895
+ status: "active" as const,
1896
+ }
1897
+ : state.execution.delegation;
1818
1898
  return {
1819
1899
  ok: true,
1820
1900
  next: {
@@ -1830,17 +1910,197 @@ export const recordMenuChoice = (
1830
1910
  mode: choice as ExecutionMode,
1831
1911
  evidence: recorded.evidence,
1832
1912
  coordinator_session_id: coordinatorSessionId,
1913
+ delegation,
1833
1914
  }
1834
1915
  : {
1835
1916
  status: "pending",
1836
1917
  mode: null,
1837
1918
  evidence: recorded.evidence,
1838
1919
  coordinator_session_id: null,
1920
+ delegation,
1839
1921
  },
1840
1922
  updated_at: Date.now(),
1841
1923
  },
1842
1924
  };
1843
1925
  });
1926
+ if (!result.ok) return result;
1927
+ return cursorSubagent && lease !== null ? { ok: true, coordinator_lease: lease } : { ok: true };
1928
+ };
1929
+
1930
+ /**
1931
+ * Cursor delegation capability model (cursor-subagent-inline CA-01..CA-05):
1932
+ * the coordinator lease authorizes token minting; the task-scoped token
1933
+ * authorizes delegated mutations. Only hashes persist — raw values cross the
1934
+ * API boundary exactly once. Token lifecycle: one active token per flow,
1935
+ * reusable within its task, revoked by `revokeDelegateToken` when the task
1936
+ * progress line is recorded, replaced when the next task token mints.
1937
+ */
1938
+ export type DelegateTokenResult = { ok: true; token: string } | FlowError;
1939
+
1940
+ export const mintDelegateToken = (
1941
+ root: string,
1942
+ slug: string,
1943
+ planPath: string,
1944
+ taskId: number,
1945
+ coordinatorLease: string,
1946
+ ): DelegateTokenResult => {
1947
+ if (typeof taskId !== "number" || !Number.isSafeInteger(taskId) || taskId <= 0) {
1948
+ return err(
1949
+ "task_invalid",
1950
+ `task id must be a positive safe integer: ${JSON.stringify(taskId)}`,
1951
+ );
1952
+ }
1953
+ if (typeof coordinatorLease !== "string" || coordinatorLease === "") {
1954
+ return err(
1955
+ "coordinator_lease_invalid",
1956
+ "a coordinator lease is required to mint a delegation token",
1957
+ );
1958
+ }
1959
+ const doc = resolveDoc(root, slug, planPath, "plan");
1960
+ if (!doc.ok) return err("path_invalid", doc.error);
1961
+ const token = randomBytes(32).toString("hex");
1962
+ const tokenHash = delegationHash(token);
1963
+ const result = readModifyWrite(root, slug, (state) => {
1964
+ const exec = state.execution;
1965
+ if (exec.status !== "active" || exec.mode !== "subagent-driven") {
1966
+ return err(
1967
+ "flow_not_active",
1968
+ "a delegation token requires an active subagent-driven execution",
1969
+ );
1970
+ }
1971
+ const delegation = exec.delegation;
1972
+ if (!delegation || delegation.status !== "active" || !delegation.coordinator_lease_hash) {
1973
+ return err(
1974
+ "coordinator_lease_invalid",
1975
+ "this flow has no active coordinator lease — record the execution menu with Cursor subagent-driven first",
1976
+ );
1977
+ }
1978
+ if (delegation.coordinator_lease_hash !== delegationHash(coordinatorLease)) {
1979
+ return err(
1980
+ "coordinator_lease_invalid",
1981
+ "the supplied coordinator lease does not match the flow's recorded lease",
1982
+ );
1983
+ }
1984
+ if (state.plan.status !== "approved") {
1985
+ return err("plan_not_approved", "plan must be approved before delegating a task");
1986
+ }
1987
+ let planText: string;
1988
+ try {
1989
+ planText = readFileSync(doc.path, "utf8");
1990
+ } catch {
1991
+ return err("plan_missing", `plan not found: ${planPath}`);
1992
+ }
1993
+ if (!parseTasksFromPlan(planText).some((t) => t.id === taskId)) {
1994
+ return err("task_invalid", `task ${taskId} does not exist in the approved plan`);
1995
+ }
1996
+ if (ledgerCompletion(root, slug).completed.includes(taskId)) {
1997
+ return err("task_completed", `task ${taskId} is already completed in the SDD ledger`);
1998
+ }
1999
+ return {
2000
+ ok: true,
2001
+ next: {
2002
+ ...state,
2003
+ execution: {
2004
+ ...exec,
2005
+ delegation: {
2006
+ ...delegation,
2007
+ active_task_id: taskId,
2008
+ token_hash: tokenHash,
2009
+ token_workspace: root,
2010
+ token_slug: slug,
2011
+ status: "active",
2012
+ },
2013
+ },
2014
+ updated_at: Date.now(),
2015
+ },
2016
+ };
2017
+ });
2018
+ if (!result.ok) return result;
2019
+ return { ok: true, token };
2020
+ };
2021
+
2022
+ export type DelegateTokenContext = {
2023
+ slug: string;
2024
+ taskId: number;
2025
+ hostWorkspace: string;
2026
+ };
2027
+ export type DelegateTokenValidation = { ok: true; context: DelegateTokenContext } | FlowError;
2028
+
2029
+ export const validateDelegateToken = (root: string, token: string): DelegateTokenValidation => {
2030
+ if (typeof token !== "string" || token === "") {
2031
+ return err("delegation_token_invalid", "a delegation token is required");
2032
+ }
2033
+ const hash = delegationHash(token);
2034
+ let entries: string[] = [];
2035
+ try {
2036
+ entries = readdirSync(path.join(root, "docs"), { withFileTypes: true })
2037
+ .filter((e) => e.isDirectory())
2038
+ .map((e) => e.name);
2039
+ } catch {
2040
+ return err("delegation_token_invalid", `no flows exist under ${JSON.stringify(root)}`);
2041
+ }
2042
+ for (const slug of entries) {
2043
+ try {
2044
+ const state = readFlowState(root, slug);
2045
+ const delegation = state.execution.delegation;
2046
+ if (!delegation || !delegation.token_hash) continue;
2047
+ if (delegation.token_hash !== hash) continue;
2048
+ if (delegation.token_workspace !== root || delegation.token_slug !== slug) continue;
2049
+ if (
2050
+ delegation.status !== "active" ||
2051
+ state.execution.status !== "active" ||
2052
+ state.execution.mode !== "subagent-driven" ||
2053
+ delegation.active_task_id === null
2054
+ ) {
2055
+ return err(
2056
+ "delegation_token_revoked",
2057
+ "the delegation token is no longer active for this flow",
2058
+ );
2059
+ }
2060
+ return {
2061
+ ok: true,
2062
+ context: { slug, taskId: delegation.active_task_id, hostWorkspace: root },
2063
+ };
2064
+ } catch {
2065
+ // unreadable flow state: skip, never throw from validation
2066
+ }
2067
+ }
2068
+ return err(
2069
+ "delegation_token_invalid",
2070
+ "the delegation token does not match any active flow token in this workspace",
2071
+ );
2072
+ };
2073
+
2074
+ export const revokeDelegateToken = (root: string, slug: string, taskId: number): FlowGateResult => {
2075
+ if (typeof taskId !== "number" || !Number.isSafeInteger(taskId) || taskId <= 0) {
2076
+ return err(
2077
+ "task_invalid",
2078
+ `task id must be a positive safe integer: ${JSON.stringify(taskId)}`,
2079
+ );
2080
+ }
2081
+ return readModifyWrite(root, slug, (state) => {
2082
+ const exec = state.execution;
2083
+ const delegation = exec.delegation;
2084
+ if (
2085
+ !delegation ||
2086
+ delegation.status !== "active" ||
2087
+ delegation.active_task_id !== taskId ||
2088
+ !delegation.token_hash
2089
+ ) {
2090
+ return err(
2091
+ "delegation_token_not_active",
2092
+ `no active delegation token for task ${taskId} in ${slug}`,
2093
+ );
2094
+ }
2095
+ return {
2096
+ ok: true,
2097
+ next: {
2098
+ ...state,
2099
+ execution: { ...exec, delegation: { ...delegation, status: "revoked" } },
2100
+ updated_at: Date.now(),
2101
+ },
2102
+ };
2103
+ });
1844
2104
  };
1845
2105
 
1846
2106
  /**
@@ -24,10 +24,10 @@ This session is a handoff destination for a continued plan. The originating sess
24
24
  - Working state, briefs, ledgers, and review diffs live only under gitignored `<SDD_DIR>` in `docs/<slug>/sdd/` and use `workit_sdd_*` tools.
25
25
  - Use native `todowrite` for visible task state as well as the gitignored ledger.
26
26
  - Use native `question` for branch/stash choices and guarded external mutations; call mutation tools only after approval with `confirmed: true` (grounded in the recorded NativeChoiceEvidence).
27
- - Flow-tool confirmations are never agent-typed booleans and never caller-supplied evidence objects: on OpenCode the plugin records the user's native-`question` answer as a host-observed one-use receipt (`attested: true`, `callID`, `selectedLabel`, `recordedAt`) consumed by `workit_spec_approve` / `workit_plan_approve` / `workit_plan_menu` — no evidence argument exists, and delegated worker status comes from host session parentage (`parentID`), never a caller `role` field. On Cursor, confirmations are policy-only (`attested: false`) and subagent-driven execution is rejected as unsupported.
27
+ - Flow-tool confirmations are never agent-typed booleans and never caller-supplied evidence objects: on OpenCode the plugin records the user's native-`question` answer as a host-observed one-use receipt (`attested: true`, `callID`, `selectedLabel`, `recordedAt`) consumed by `workit_spec_approve` / `workit_plan_approve` / `workit_plan_menu` — no evidence argument exists, and delegated worker status comes from host session parentage (`parentID`), never a caller `role` field. On Cursor, confirmations are policy-only (`attested: false`), Subagent-driven execution is supported through the one-time `coordinator_lease` and per-task `delegation_token` minted by `workit_delegate` (fail-closed validation; no `parentID` identity exists on Cursor), and Inline runs single-agent in the current session.
28
28
  - Delegated authority is direct-child-only: a worker is the session whose host `parentID` exactly equals the activating coordinator's recorded `coordinator_session_id`; missing, mismatched, or multi-owner lineage fails closed with `delegation_lineage_denied`, and nested `opencode` launches are denied during active delegated work. An authorized child receives only the compact worker contract (execute the supplied brief, follow TDD, land one contiguous non-empty commit range, report results) — never coordinator guidance, `wk-implement`, or ledger management; coordinator bookkeeping via `workit_sdd_*` stays with the coordinator session.
29
29
  - On Cursor, for every repository-scoped `workit_*` call, pass the active Cursor workspace as `workspace_root`; never rely on the MCP process default.
30
- - Use native `task` with only the built-in `explore` and `general` agents.
30
+ - Use native `task` with only the built-in `explore` and `general` agents (OpenCode); on Cursor, Subagent-driven dispatches Cursor-native subagents with a task `delegation_token`, Inline runs single-agent in the current session.
31
31
 
32
32
  ## Flow gates (HARD)
33
33
 
@@ -51,7 +51,7 @@ For each top-level task absent from `completed_task_ids`:
51
51
 
52
52
  1. Mark it `in_progress` with `todowrite`.
53
53
  2. Create a working-state brief with `workit_sdd_task_brief` and `confirmed: true`.
54
- 3. Delegate read-only discovery, when needed, to an `explore` agent. Delegate implementation to a fresh `general` agent. Product changes follow TDD.
54
+ 3. Route by the approved execution mode: **Subagent-driven** delegates read-only discovery to an `explore` (OpenCode) agent, delegates implementation to a fresh `general` (OpenCode) agent (on Cursor, the coordinator mints a `delegation_token` with `workit_delegate` and the subagent prompt carries the raw token — the worker passes it as `delegation_token` on its mutation calls, and appending progress revokes it), and the coordinator never edits product code; **Inline** executes every task in the current agent with no dispatch and no token minting. Product changes follow TDD.
55
55
  4. Create a working-state diff with `workit_sdd_review_package` and `confirmed: true`.
56
56
  5. Delegate spec-compliance review and code-quality review to separate `general` agents.
57
57
  6. **Blocking** findings (Critical, Important, or spec-compliance) may trigger at most **two** fix+re-review rounds per task. **Advisory** findings (Minor, style, YAGNI, taste) never pause the loop — append them with `workit_sdd_append_advisory` (`--task <id> --text <text>`, `confirmed: true`) instead of an unrestricted file edit.
@@ -1,6 +1,6 @@
1
1
  # <Feature> Implementation Plan
2
2
 
3
- > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. On Cursor the two paths are: **Subagent-driven** — Cursor-native subagents dispatched by the coordinator, each carrying a task-scoped `delegation_token` minted with `workit_delegate` from the one-time `coordinator_lease`; **Inline** — `executing-plans` in the current session, single-agent, no dispatch, no token minting. Steps use checkbox (`- [ ]`) syntax for tracking.
4
4
 
5
5
  **Spec:** `docs/<slug>/spec.md`
6
6
  **Branch:** `feature/<slug>`
@@ -1,6 +1,6 @@
1
1
  # Superpowers document contract
2
2
 
3
- Use OpenCode's native `question` for every bounded user choice. Give concise choices and allow a custom answer; if the tool is unavailable, ask one concise plain-text question. Use `skill` to load workflow and Superpowers skills, `todowrite` for task state, and `task` for delegated work.
3
+ Use OpenCode's native `question` for every bounded user choice. Give concise choices and allow a custom answer; if the tool is unavailable, ask one concise plain-text question. Use `skill` to load workflow and Superpowers skills, `todowrite` for task state, and `task` for delegated work (OpenCode) or the host-native equivalent — on Cursor, Subagent-driven dispatches Cursor-native subagents with a task `delegation_token`, Inline runs single-agent in the current session.
4
4
 
5
5
  ## Tracked document layout
6
6
 
@@ -35,7 +35,7 @@ Before writing **Branch:** into a new spec or plan, call `workit_docs_branch` an
35
35
  - Commits use `wk-commit` after its native `question` confirmation.
36
36
  - Continuation uses `wk-handoff`, whose `workit_handoff_session` creates and seeds the OpenCode session automatically.
37
37
  - Never use worktrees. Resolve the declared branch with `workit_resolve_branch`, preview dirty-tree stash choices with `question`, and apply an approved in-place checkout through `workit_branch_setup` with `confirmed: true` (grounded in the recorded NativeChoiceEvidence).
38
- - Flow-tool confirmations are never agent-typed booleans and never caller-supplied evidence objects: on OpenCode the plugin records the user's native-`question` answer as a host-observed one-use receipt (`attested: true`, `callID`, `selectedLabel`, `recordedAt`) consumed by `workit_spec_approve` / `workit_plan_approve` / `workit_plan_menu` — no evidence argument exists, and delegated worker status comes from host session parentage (`parentID`), never a caller `role` field. On Cursor, confirmations are policy-only (`attested: false`) and subagent-driven execution is rejected as unsupported.
38
+ - Flow-tool confirmations are never agent-typed booleans and never caller-supplied evidence objects: on OpenCode the plugin records the user's native-`question` answer as a host-observed one-use receipt (`attested: true`, `callID`, `selectedLabel`, `recordedAt`) consumed by `workit_spec_approve` / `workit_plan_approve` / `workit_plan_menu` — no evidence argument exists, and delegated worker status comes from host session parentage (`parentID`), never a caller `role` field. On Cursor, confirmations are policy-only (`attested: false`), Subagent-driven execution is supported through the one-time `coordinator_lease` and per-task `delegation_token` minted by `workit_delegate` (fail-closed validation; no `parentID` identity exists on Cursor), and Inline runs single-agent in the current session.
39
39
  - Keep all SDD state under the gitignored `docs/<slug>/sdd/`; use `workit_sdd_context` and the registered `workit_sdd_*` tools.
40
40
  - After implementation, use `question` before an approved stash reapply through `workit_branch_setup` with `confirmed: true`.
41
41
 
@@ -7,11 +7,11 @@ description: Use when you have a written implementation plan to execute in a sep
7
7
 
8
8
  ## Overview
9
9
 
10
- Load plan, review critically, execute all tasks, report when complete.
10
+ Load plan, review critically, execute all tasks in the current session, report when complete. This is the single-agent Inline execution path.
11
11
 
12
12
  **Announce at start:** "I'm using the executing-plans skill to implement this plan."
13
13
 
14
- **Note:** Tell your human partner that Superpowers works much better with access to subagents. The quality of its work will be significantly higher if run on a platform with subagent support (Claude Code, Codex CLI, Codex App, and Copilot CLI all qualify; see the per-platform tool refs in `../using-superpowers/references/`). If subagents are available, use superpowers:subagent-driven-development instead of this skill.
14
+ **Note:** This skill is the single-agent Inline execution path: every task runs in the current session — no token minting, no subagent dispatching. Tell your human partner that Superpowers works much better with access to subagents. The quality of its work will be significantly higher if run on a platform with subagent support (Claude Code, Codex CLI, Codex App, and Copilot CLI all qualify; see the per-platform tool refs in `../using-superpowers/references/`). If subagents are available and the plan's approved execution mode is Subagent-driven, use superpowers:subagent-driven-development instead of this skill.
15
15
 
16
16
  ## The Process
17
17
 
@@ -11,6 +11,8 @@ Execute plan by dispatching a fresh implementer subagent per task, a task review
11
11
 
12
12
  **Core principle:** Fresh subagent per task + task review (spec + quality) + broad final review = high quality, fast iteration
13
13
 
14
+ On Cursor, the coordinator mints a task-scoped delegation token via `workit_delegate` (from the `workit_plan_menu` coordinator lease) and the subagent prompt must carry it as `delegation_token` for mutation calls.
15
+
14
16
  **Narration:** between tool calls, narrate at most one short line — the
15
17
  ledger and the tool results carry the record.
16
18