@brainervirus/workit-core 4.0.0 → 5.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.
Files changed (39) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/skills/workit-bdd/SKILL.md +35 -29
  4. package/skills/workit-continue/SKILL.md +53 -0
  5. package/skills/workit-debug/SKILL.md +41 -36
  6. package/skills/workit-deslop/SKILL.md +35 -25
  7. package/skills/workit-fanout/SKILL.md +62 -0
  8. package/skills/workit-fanout/references/brief.md +56 -0
  9. package/skills/workit-implement/SKILL.md +48 -44
  10. package/skills/workit-review/SKILL.md +42 -51
  11. package/skills/workit-review/references/impact.md +24 -0
  12. package/skills/workit-shape/SKILL.md +71 -0
  13. package/skills/workit-shape/references/diagrams.md +17 -0
  14. package/skills/workit-shape/references/knowledge.md +58 -0
  15. package/skills/workit-shape/references/mockups.md +15 -0
  16. package/skills/workit-shape/references/slicing.md +42 -0
  17. package/skills/workit-ship/SKILL.md +52 -0
  18. package/skills/workit-test-audit/SKILL.md +10 -2
  19. package/skills/workit-verify-app/SKILL.md +63 -0
  20. package/skills/workit-verify-app/references/template.md +49 -0
  21. package/src/core/external-action-effects.ts +2 -2
  22. package/src/core/methods.ts +68 -98
  23. package/src/core/skill-manifests.ts +49 -25
  24. package/src/hooks/handle.ts +29 -5
  25. package/src/hooks/hosts/codex.ts +1 -1
  26. package/src/ledger.ts +19 -10
  27. package/templates/plan-template.md +17 -18
  28. package/templates/spec-template.md +4 -3
  29. package/skills/workit-babysit/SKILL.md +0 -37
  30. package/skills/workit-behavioral-tdd/SKILL.md +0 -56
  31. package/skills/workit-blast-radius/SKILL.md +0 -26
  32. package/skills/workit-challenge/SKILL.md +0 -47
  33. package/skills/workit-diagram/SKILL.md +0 -27
  34. package/skills/workit-green-run/SKILL.md +0 -24
  35. package/skills/workit-handoff/SKILL.md +0 -37
  36. package/skills/workit-mockup/SKILL.md +0 -23
  37. package/skills/workit-plan/SKILL.md +0 -45
  38. package/skills/workit-steer/SKILL.md +0 -39
  39. package/templates/execution-contract.md +0 -40
@@ -1,13 +1,13 @@
1
1
  import type { Assurance, Capability, Dimension, Policy } from "./task-contract";
2
2
 
3
3
  export type MethodId =
4
- | "workit-challenge"
5
- | "workit-behavioral-tdd"
4
+ | "workit-shape"
5
+ | "workit-bdd"
6
6
  | "workit-review"
7
- | "workit-plan"
8
7
  | "workit-implement"
8
+ | "workit-fanout"
9
9
  | "workit-debug"
10
- | "workit-handoff"
10
+ | "workit-continue"
11
11
  | "workit-deslop";
12
12
 
13
13
  type MethodDefinition = {
@@ -16,15 +16,16 @@ type MethodDefinition = {
16
16
  };
17
17
 
18
18
  export const METHODS: Readonly<Record<MethodId, MethodDefinition>> = {
19
- "workit-challenge": { dimensions: ["challenge", "decisions"] },
20
- "workit-behavioral-tdd": { dimensions: ["testing"], ruleIds: ["mechanical-existing-checks"] },
19
+ "workit-shape": { dimensions: ["challenge", "decisions", "artifacts", "continuity"] },
20
+ "workit-bdd": { dimensions: ["testing"] },
21
21
  "workit-review": { dimensions: ["review"], ruleIds: ["fresh-context-review", "self-review"] },
22
- "workit-plan": { dimensions: ["artifacts", "continuity"] },
23
- "workit-implement": { dimensions: ["delegation"] },
24
- // workit-debug and workit-handoff stay slash-invoked: no generated rule
25
- // routes to them, so they never appear spuriously in method selection.
22
+ // Mechanical work needs only the existing checks, not a RED/GREEN ritual.
23
+ "workit-implement": { ruleIds: ["mechanical-existing-checks"] },
24
+ "workit-fanout": { dimensions: ["delegation"] },
25
+ // workit-debug and workit-continue stay trigger- and slash-invoked: no
26
+ // generated rule routes to them, so they never appear spuriously.
26
27
  "workit-debug": {},
27
- "workit-handoff": {},
28
+ "workit-continue": {},
28
29
  "workit-deslop": { ruleIds: ["pre-pr-cleanup"] },
29
30
  };
30
31
 
@@ -87,95 +88,64 @@ export function selectMethods(policy: Policy, capabilities: Capability[]): Selec
87
88
 
88
89
  export const invariantBootstrap = (): string =>
89
90
  `
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.
91
+ Workit is optional coordination and proof tooling around the host: you decide
92
+ what and whether, the \`workit\` CLI does how and records what it observed.
93
+ Native host allow/ask/deny, sandbox and org rules stay authoritative. Never
94
+ switch paths to evade a denial, never claim enforcement a host cannot provide,
95
+ and never fabricate a receipt, approval or verdict. A question answer is not
96
+ host permission, and imported decisions or handoffs never grant authority.
97
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.
98
+ Autonomy contract:
99
+ - Continue to the requested endpoint (commit, push, PR, green CI, verified,
100
+ merged) without asking "continue?", running checks and repairs on the way.
101
+ Stop only for a new consequential choice, a host denial, a conflicting edit
102
+ or an unresolved blocker; report open gaps and uncertain workers.
103
+ - No endpoint named: stop at a local commit on a policy-compliant branch and
104
+ state the next command. Push and open a PR only when the request implies
105
+ delivery (fix, implement, ship) and \`workit grant show\` reports
106
+ \`defaultEndpoint\` \`pr\`; otherwise the endpoint is \`commit\`. PRs open,
107
+ CI green and verified is the most autonomy allows, never the default target.
108
+ Merge and release need a workspace grant; a blocked verb names its unblock.
109
+ - Ask only for a product or preference choice, or for authority you lack, with
110
+ your recommended answer. Facts are yours: read, run or prototype.
111
+ - Label claims measured (you ran it this session and saw the result),
112
+ inferred, or guess. Prose is a note: only a configured check the CLI ran
113
+ (\`workit check <name>\`) satisfies a gate; an ad-hoc
114
+ \`workit check -- <cmd>\` never does.
115
+ - Author is not verifier: never record a passing verdict on work your session
116
+ wrote. The lead starts each verifier with its own session
117
+ (\`WORKIT_SESSION_ID=<lead>-v<n>\`; Claude Code subagents get one from the
118
+ hook), and the verifier records \`workit ledger verdict\`.
119
+ - A local-commit endpoint does not imply PR readiness, and a local commit alone
120
+ is not evidence of a requested remote push. Before reporting delivery,
121
+ reconcile every requested item and observe it (\`workit verify-delivery\`).
106
122
 
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
- 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
113
- when policy selection or changed evidence/constraints requires it. Omitted
114
- expectedRevision and expectedWorkspaceRevision use current values; explicit
115
- values are still concurrency-checked, so never copy revisions between calls.
116
- A busy result means another live Workit call holds the checkout lock: retry the
117
- same call; it is not a recovery condition. A lock left by a dead process is
118
- reclaimed on the next write, and \`workit doctor --fix-lock\` clears it on demand.
119
- An omitted revision absorbs a concurrent write: Workit re-reads, re-checks
120
- policy, and reapplies the call, returning busy under persistent contention. A
121
- revision_conflict means a revision you passed is stale: re-read the record
122
- before deciding whether to retry.
123
- A solo edit does not need writer acquisition; use it when concurrent checkout
124
- writers need coordination. Record only observed facts and checks. Close-time
125
- testing and verification gates accept only a configured check the CLI ran:
126
- \`workit check <name>\` (\`npx -y @brainervirus/workit-cli check <name>\` when
127
- \`workit\` is not on PATH); a recorded check result is a note and an ad-hoc
128
- \`workit check -- <cmd>\` never satisfies a gate. Evidence can become stale when its bound candidate or tree changes; reconcile findings against the
129
- current candidate before recording completion.
123
+ CLI first: if a step has one right answer, use the \`workit\` verb instead of
124
+ hand-running git, gh or glab: check, git, pr, ci, stack, verify-delivery,
125
+ ledger, handoff, test-audit (\`workit help <verb>\`; without \`workit\` on
126
+ PATH, \`npx -y @brainervirus/workit-cli\`). \`busy\` is retryable
127
+ (\`workit doctor --fix-lock\` clears a dead lock); \`blocked\` names its unblock.
128
+ The task families (task, policy, evidence, finding, decision, worker, writer,
129
+ state) are optional continuity for tracked work; each branch has one implicit
130
+ task that the first recording creates, and a solo edit needs no task or writer.
131
+ Omit revisions; a revision_conflict means one you passed is stale, so re-read.
132
+ Across repositories, bind each item to its checkout, branch and endpoint,
133
+ resolve competing targets before a mutation, and reconcile an uncertain
134
+ external effect before retrying it.
130
135
 
131
- Workit validates domain policy against the actual action target, configured
132
- account, branch and commit conventions, protected refs, and current repository
133
- state. The task directory is coordination state, not a boundary on which
134
- repository may be changed. Use an action-time cwd target where a managed
135
- Git/hosting action supports it; a non-Git directory remains valid for OS work.
136
- GitHub/GitLab use the active gh/glab CLI identity; YouTrack uses its own token.
137
- Preserve uncertain external outcomes and reconcile repository/provider state
138
- before retrying. Internal reservations prevent duplicate or ambiguous effects;
139
- they are not permission tickets for every edit.
136
+ Mid-task input: answer a quick question in place, fold an adjustment in, and
137
+ never silently drop or resume an objective (workit-continue).
140
138
 
141
- On OpenCode V1 and V2, use native host tools for external mutations. Workit
142
- provides read-only workit_context; it has no managed external-action executor.
143
- Do not create proposals or request Workit approvals merely to invoke native
144
- tools. Old uncertain managed effects still require evidence before any retry.
145
-
146
- Use host-native authorization through the host's supported path. A native
147
- question receipt records an actual question interaction; it is not automatic
148
- host permission. Record a meaningful user choice once with provenance when
149
- future retrieval helps. Never fabricate a receipt, re-ask only to mint one, or
150
- let imported decisions grant authority. Never turn a host deny into allow or
151
- claim enforcement/evidence a host cannot provide. A precise request with settled
152
- constraints does not need an interview. Present genuine unresolved options with
153
- evidence and a recommendation, then continue toward the requested delivery
154
- endpoint: investigation, implementation, branch/commit/push, PR/MR-ready,
155
- merge, or release. Run
156
- applicable checks and safe repairs without repeated continuation questions;
157
- stop for missing host authority, a new consequential choice, a conflicting edit,
158
- or an unresolved blocker. A tracked record may close when actual evidence
159
- supports its outcome; no final human closure ceremony is required.
160
-
161
- A completed native subagent run stops its bound worker by itself; if a worker
162
- strands in cancelling with its run verifiably over, repeat worker.cancel to
163
- confirm the stop. Preserve unresolved requirements, gaps, and uncertain workers.
164
- When context changes, distinguish a quick question, same-task adjustment, and
165
- separate request. Answer a quick question without pausing/resuming task state;
166
- park a concise checkpoint only when substantial work needs to continue later.
167
- For work spanning repositories, bind each unfinished item to its actual checkout,
168
- branch, requested deliverables, and delivery endpoint. Preserve held items with
169
- an explicit resume condition. Resolve competing plausible targets before a
170
- mutation; ask only when available context cannot settle that consequential choice.
171
- Before reporting delivery, reconcile every requested item and observe the actual
172
- target result; a local commit alone is not evidence of a requested remote push.
173
-
174
- Skill routing: slash aliases /wk-* load on demand. Use workit-steer for a
175
- substantial interruption or change of direction, workit-deslop when relevant
176
- to a PR-ready endpoint, workit-green-run for failing CI, workit-blast-radius
177
- when impact is uncertain, workit-challenge for genuinely open consequential
178
- choices, workit-bdd to turn acceptance criteria into Given/When/Then tests, and
179
- workit-test-audit to check tests for tautologies. Load workit-plan when dependencies or handoff need durable next actions.
180
- Load the skill; never act from memory of it.
139
+ Skills: load the skill, never act from memory of it (slash aliases /wk-<name>).
140
+ - brainstorm, plan, spec, grill, should we: workit-shape
141
+ - implement, build, add a feature: workit-implement
142
+ - review, blast radius: workit-review
143
+ - bug, broken, flaky, regression: workit-debug
144
+ - ship, babysit, CI, merge: workit-ship
145
+ - resume, pick up, handoff, interruption: workit-continue
146
+ - BDD, TDD, acceptance criteria, Given/When/Then: workit-bdd
147
+ - test audit, tautology, weak tests: workit-test-audit
148
+ - deslop, slop, dead code: workit-deslop
149
+ - fan out, parallelize, parallel agents, swarm: workit-fanout
150
+ - verify the app, smoke test, prove it works: workit-verify-app
181
151
  `.trim();
@@ -2,44 +2,68 @@ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
2
  import path from "node:path";
3
3
 
4
4
  export const WORKIT_METHOD_SKILLS = [
5
- "workit-challenge",
6
- "workit-behavioral-tdd",
7
- "workit-review",
8
- "workit-plan",
5
+ "workit-shape",
9
6
  "workit-implement",
7
+ "workit-review",
10
8
  "workit-debug",
11
- "workit-handoff",
12
- "workit-babysit",
13
- "workit-blast-radius",
14
- "workit-deslop",
15
- "workit-diagram",
16
- "workit-mockup",
17
- "workit-green-run",
18
- "workit-steer",
9
+ "workit-ship",
10
+ "workit-continue",
19
11
  "workit-bdd",
20
12
  "workit-test-audit",
13
+ "workit-deslop",
14
+ "workit-fanout",
15
+ "workit-verify-app",
21
16
  ] as const;
22
17
 
18
+ export type WorkitSkill = (typeof WORKIT_METHOD_SKILLS)[number];
19
+
23
20
  /** wk- slash aliases (one per skill): alias → method skill. An alias routes
24
21
  * through policy to model skills; an alias never calls another alias. */
25
22
  export const WORKIT_SKILL_ALIASES = {
26
- "wk-challenge": "workit-challenge",
27
- "wk-babysit": "workit-babysit",
23
+ "wk-shape": "workit-shape",
28
24
  "wk-implement": "workit-implement",
29
- "wk-plan": "workit-plan",
30
- "wk-debug": "workit-debug",
31
25
  "wk-review": "workit-review",
32
- "wk-handoff": "workit-handoff",
33
- "wk-tdd": "workit-behavioral-tdd",
34
- "wk-blast-radius": "workit-blast-radius",
35
- "wk-deslop": "workit-deslop",
36
- "wk-diagram": "workit-diagram",
37
- "wk-mockup": "workit-mockup",
38
- "wk-green-run": "workit-green-run",
39
- "wk-steer": "workit-steer",
26
+ "wk-debug": "workit-debug",
27
+ "wk-ship": "workit-ship",
28
+ "wk-continue": "workit-continue",
40
29
  "wk-bdd": "workit-bdd",
41
30
  "wk-test-audit": "workit-test-audit",
42
- } as const;
31
+ "wk-deslop": "workit-deslop",
32
+ "wk-fanout": "workit-fanout",
33
+ "wk-verify-app": "workit-verify-app",
34
+ } as const satisfies Record<string, WorkitSkill>;
35
+
36
+ /**
37
+ * The words a user types that should load each skill. Each skill's
38
+ * description and its bootstrap routing line carry every one of them, and no
39
+ * two skills share one (checked by test), so a trigger routes to one skill.
40
+ */
41
+ export const WORKIT_SKILL_TRIGGERS: Readonly<Record<WorkitSkill, readonly string[]>> = {
42
+ "workit-shape": ["brainstorm", "plan", "spec", "grill", "should we"],
43
+ "workit-implement": ["implement", "build", "add a feature"],
44
+ "workit-review": ["review", "blast radius"],
45
+ "workit-debug": ["bug", "broken", "flaky", "regression"],
46
+ "workit-ship": ["ship", "babysit", "CI", "merge"],
47
+ "workit-continue": ["resume", "pick up", "handoff", "interruption"],
48
+ "workit-bdd": ["BDD", "TDD", "acceptance criteria", "Given/When/Then"],
49
+ "workit-test-audit": ["test audit", "tautology", "weak tests"],
50
+ "workit-deslop": ["deslop", "slop", "dead code"],
51
+ "workit-fanout": ["fan out", "parallelize", "parallel agents", "swarm"],
52
+ "workit-verify-app": ["verify the app", "smoke test", "prove it works"],
53
+ };
54
+
55
+ /** The `description:` frontmatter value of a SKILL.md (one line, unquoted). */
56
+ export const skillDescription = (skillMd: string): string => {
57
+ const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---/.exec(skillMd)?.[1] ?? "";
58
+ const line = /^description:\s*(.+)$/m.exec(frontmatter)?.[1] ?? "";
59
+ return line.trim().replace(/^(["'])(.*)\1$/, "$2");
60
+ };
61
+
62
+ /** A generated Cursor slash command for one alias; `commands/` is committed
63
+ * because Cursor discovers the plugin from git, so the build writes these and
64
+ * a drift test compares them to the committed copies. */
65
+ export const cursorCommandText = (alias: string, skill: string, description: string): string =>
66
+ `# /${alias}\n\nLoad and apply the bundled \`${skill}\` skill. ${description}\n\nExtra context: $ARGUMENTS\n`;
43
67
 
44
68
  export const skillManifestNames = (root: string): string[] =>
45
69
  existsSync(root)
@@ -17,14 +17,35 @@ const offered = new Set<string>();
17
17
  * `workit:implementer` is ours: a bare or other-plugin `implementer` is not. */
18
18
  const CLAUDE_WORKTREE_IMPLEMENTER = "workit:implementer";
19
19
 
20
+ /** The plugin's read-only judges: each records verdicts as its own session. */
21
+ const CLAUDE_JUDGES: ReadonlySet<string> = new Set(["workit:verifier", "workit:reviewer"]);
22
+
23
+ /** `<lead session>:<agent id>`, safe for the ledger's session grammar. */
24
+ const subagentSession = (session: string | null, agentId: string): string =>
25
+ [session, agentId]
26
+ .filter((part): part is string => Boolean(part))
27
+ .join(":")
28
+ .replace(/[^A-Za-z0-9_.:@/+-]/g, "-")
29
+ .slice(0, 128);
30
+
20
31
  const subagentStartText = (
21
32
  host: HookInput["host"],
33
+ session: string | null,
22
34
  descriptor: HostDescriptor,
23
35
  event: Extract<HookInput["event"], { kind: "subagent.start" }>,
24
- ): string =>
25
- host === "claude_code" && event.agentType === CLAUDE_WORKTREE_IMPLEMENTER
26
- ? `Workit observed ${descriptor.label} subagent ${event.agentId} (${event.agentType}) working in its own git worktree: it may edit and commit there, within its brief's scope. Before the first commit, switch to a policy-compliant branch (\`git switch -c <type>/<slug>\`, e.g. feature/<slug>); branch policy hooks still deny protected or non-compliant branches. Never push, open a PR, or merge unless the brief asks for it.`
27
- : `Workit observed ${descriptor.label} subagent ${event.agentId} (${event.agentType}) as read-only/agent-guided; writer delegation is unavailable.`;
36
+ ): string => {
37
+ if (host === "claude_code" && event.agentType === CLAUDE_WORKTREE_IMPLEMENTER)
38
+ return `Workit observed ${descriptor.label} subagent ${event.agentId} (${event.agentType}) working in its own git worktree: it may edit and commit there, within its brief's scope. Before the first commit, switch to a policy-compliant branch with \`workit git branch <branch> --base <base>\` (e.g. feature/<slug>); branch policy hooks still deny protected or non-compliant branches. Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
39
+ const readOnly = `Workit observed ${descriptor.label} subagent ${event.agentId} (${event.agentType}) as read-only/agent-guided; writer delegation is unavailable.`;
40
+ if (
41
+ host === "claude_code" &&
42
+ event.agentType &&
43
+ CLAUDE_JUDGES.has(event.agentType) &&
44
+ event.agentId
45
+ )
46
+ return `${readOnly} Its own Workit session is ${subagentSession(session, event.agentId)}: record verdicts with \`workit ledger verdict <result> --session ${subagentSession(session, event.agentId)} ...\` so the ledger tells it apart from the author.`;
47
+ return readOnly;
48
+ };
28
49
 
29
50
  export function handleHook(input: HookInput, deps: HookDeps): HookDecision {
30
51
  const { descriptor } = deps;
@@ -50,7 +71,10 @@ export function handleHook(input: HookInput, deps: HookDeps): HookDecision {
50
71
  case "shell.pre":
51
72
  return usable(descriptor.shellPolicy.deny) ? shellPolicy(input.cwd, event.command) : NONE;
52
73
  case "subagent.start":
53
- return { kind: "context", text: subagentStartText(input.host, descriptor, event) };
74
+ return {
75
+ kind: "context",
76
+ text: subagentStartText(input.host, input.session.id ?? null, descriptor, event),
77
+ };
54
78
  case "compact.pre":
55
79
  return {
56
80
  kind: "notice",
@@ -294,5 +294,5 @@ export const codexAdapter: HostAdapter = {
294
294
  render,
295
295
  // Without a session id there is no actor to bind a writer to.
296
296
  addendum: (input) =>
297
- `<workit-codex-mutations>Codex MCP is read-only: unattested callers cannot mutate. Run the workit CLI for task mutations: node_modules/.bin/workit <family> <action> --json --confirm${input.session.id ? `; bind the writer to this session with node_modules/.bin/workit writer acquire --task <id> --revision <rev> --actor ${input.session.id} --confirm` : ""}. Binding decisions and external actions need a human.</workit-codex-mutations>`,
297
+ `<workit-codex-mutations>Codex MCP is read-only: unattested callers cannot mutate. Run workit verbs with the workit CLI on the shell (node_modules/.bin/workit, or npx -y @brainervirus/workit-cli): check, git branch|commit|push, pr, ci, stack, ledger, handoff; task-family mutations take --json --confirm${input.session.id ? `; bind the writer to this session with node_modules/.bin/workit writer acquire --task <id> --revision <rev> --actor ${input.session.id} --confirm` : ""}. A verifier or reviewer records workit ledger verdict under a session the lead assigns (WORKIT_SESSION_ID=<lead>-v<n>), never the author's. Merge and release need a workspace grant; a question answer is not host permission.</workit-codex-mutations>`,
298
298
  };
package/src/ledger.ts CHANGED
@@ -1071,6 +1071,12 @@ export function recordVerdict(
1071
1071
  surface?: string | null;
1072
1072
  self?: boolean;
1073
1073
  evidenceRefs?: string[];
1074
+ /**
1075
+ * A role id derived from another session (`--as`): the session it was
1076
+ * derived from. `null` means it was derived from no session, which makes
1077
+ * the verdict self. An author's derived id is refused like the author.
1078
+ */
1079
+ derivedFrom?: string | null;
1074
1080
  },
1075
1081
  ): LedgerResult<VerdictRow> {
1076
1082
  if (!(VERDICT_RESULTS as readonly string[]).includes(input.result))
@@ -1105,19 +1111,22 @@ export function recordVerdict(
1105
1111
  );
1106
1112
  const session = context.actor.session;
1107
1113
  const selfReason: SelfReason | null =
1108
- input.self === true ? "flag" : session ? null : "no_session";
1114
+ input.self === true ? "flag" : session && input.derivedFrom !== null ? null : "no_session";
1109
1115
  const self = selfReason !== null;
1110
- if (
1111
- !self &&
1112
- session &&
1113
- authorSessions(context.cwd, ledger.value.rows, branch, { base: key.base, head: key.head }).has(
1114
- session,
1115
- )
1116
- )
1116
+ const authors = authorSessions(context.cwd, ledger.value.rows, branch, {
1117
+ base: key.base,
1118
+ head: key.head,
1119
+ });
1120
+ const authoring = [session, input.derivedFrom].find(
1121
+ (candidate): candidate is string => typeof candidate === "string" && authors.has(candidate),
1122
+ );
1123
+ if (!self && authoring)
1117
1124
  return err(
1118
1125
  "blocked",
1119
- `author_verdict: session ${session} authored ${branch}; a verdict must come from a different session`,
1120
- 'have a non-author session record the verdict, or pass --self (a self verdict is never accepted by merge:"verified")',
1126
+ `author_verdict: session ${authoring} authored ${branch}; a verdict must come from a different session`,
1127
+ input.derivedFrom === undefined
1128
+ ? 'have a non-author session record the verdict, or pass --self (a self verdict is never accepted by merge:"verified")'
1129
+ : "run the verifier as a separate session: the lead spawns it with its own WORKIT_SESSION_ID (Claude Code: subagents get one from the SubagentStart hook)",
1121
1130
  );
1122
1131
  const link = checkSupersede(context, "verdict", self);
1123
1132
  if (!link.ok) return link;
@@ -1,27 +1,26 @@
1
- # <Feature> Implementation Plan
1
+ # <Feature> plan
2
2
 
3
- > **For agentic workers:** Load `workit-implement` when policy selects implementation. Use bounded `workit_worker` delegation when the host supports it; otherwise execute inline within writer scope. Steps use checkbox (`- [ ]`) syntax for tracking.
3
+ Spec: `docs/<slug>/spec.md` · Goal: <one sentence>
4
4
 
5
- **Spec:** `docs/<slug>/spec.md`
6
- **Branch:** `feature/<slug>`
5
+ A plan records decisions, not code: per slice the branch, what it touches, its
6
+ acceptance, how it is verified, and what it depends on (workit-shape
7
+ `references/slicing.md`). Independent slices fan out (workit-fanout);
8
+ dependent ones stack (`workit stack plan <bottom> … <top>`).
7
9
 
8
- **Goal:** <one sentence>
10
+ ## Global constraints
9
11
 
10
- ## Global Constraints
11
-
12
- - Each task lands exactly one contiguous non-empty commit range (`base..head`): fix rounds append commits to that range and never rewrite/amend an active review range; record the real base..head shas in task progress.
13
- - The final task closes the lead Workit task with `workit_task` `action: "close"` (CLI: `workit task close --payload … [--confirm]`) once requirements are satisfied and repository verification passes — never finish while the task is still `active` or `paused`.
14
- - <project-wide requirements, one line each>
12
+ - <project-wide requirement, one line each>
15
13
 
16
14
  ---
17
15
 
18
- ### Task N: <Component>
19
-
20
- - [ ] **Step 1: <action>**
16
+ ### S1 <type>/<slug> (base: main)
21
17
 
22
- <!-- per-task criteria: how this task is verified -->
23
- **Criteria:** <verifiable check>
18
+ Touches: <areas, not line numbers>
19
+ Acceptance:
20
+ - Given <state>, When <action>, Then <observable result>
21
+ Verify: `workit check test`; verify-<app> "<feature>"
22
+ Decisions: <ledger refs or one-line rulings>
24
23
 
25
- | Status | Task |
26
- | --- | --- |
27
- | pending | N: <Component> |
24
+ | Slice | Branch | Depends on | Status |
25
+ | --- | --- | --- | --- |
26
+ | S1 | <type>/<slug> | — | pending |
@@ -19,14 +19,15 @@ no durable agreement: `Spec: none (<reason>)`, no file.)
19
19
 
20
20
  ## Architecture
21
21
 
22
- <!-- May be `N/A (<reason>)` when the change has no flows or architecture. -->
23
- <!-- Otherwise REQUIRED: render a mermaid diagram (workit-diagram skill). -->
22
+ <!-- Optional: only when a diagram argues a decision (workit-shape
23
+ references/diagrams.md); otherwise delete this section. -->
24
24
  ```mermaid
25
25
  flowchart TD
26
26
  A[Start] --> B[Step]
27
27
  ```
28
28
 
29
- <!-- REQUIRED if this spec touches UI: render an ASCII wireframe (workit-mockup skill). -->
29
+ <!-- Optional: a UI choice still open gets an ASCII sketch (workit-shape
30
+ references/mockups.md). -->
30
31
  ```text
31
32
  ┌──────────────┐
32
33
  │ Header │
@@ -1,37 +0,0 @@
1
- ---
2
- name: workit-babysit
3
- description: Use when the user asks to babysit a PR or explicitly opts in to PR follow-up
4
- ---
5
-
6
- # Babysit a PR to PR-ready
7
-
8
- PR creation does not start babysitting. `babysit:true` opts into this skill;
9
- omission means no follow-up. A PR URL by itself is not an instruction to drive
10
- it. When the user asks to babysit a URL from a route Workit did not enforce,
11
- help without claiming the Workit route was enforced. Never mutate PR topology
12
- (no rebase strategy changes, no force-push).
13
-
14
-
15
- ## Method
16
-
17
- 1. Default to drive mode for an explicit babysit request: resolve conflicts,
18
- address review threads, and get checks green. Watch reports status only;
19
- threads-only addresses review threads. Ask about mode only when the choice
20
- materially changes the work.
21
- 2. Work toward PR-ready in order: conflicts → review threads → CI. Keep a brief
22
- checkpoint when continuity needs it; task progress is optional.
23
- 3. Classify CI before retry: flake (rerun once) vs stale base (verify with
24
- `git merge-base --is-ancestor` before updating) vs real failure (fix).
25
- 4. Triage bot findings skeptically: reproduce or quote code before acting;
26
- invalid bots get a reasoned dismissal, never silent ignore.
27
- 5. Batch fixes into one push wave; re-verify green after every push.
28
- 6. Stop at PR-ready. A PR creation or babysit request does not authorize merge
29
- or release. Continue to merge or release only when the user explicitly sets
30
- that delivery endpoint and host authority allows the action.
31
-
32
- ## Completion
33
-
34
- Report PR-ready status with evidence for fixes, or a brief blocker report. If
35
- the user explicitly authorized merge, honor the configured strategy only after
36
- checks and required approval. After a squash merge, re-record evidence against
37
- the new base commit before closing tracked work.
@@ -1,56 +0,0 @@
1
- ---
2
- name: workit-behavioral-tdd
3
- description: Use when policy identifies behavior, side effects, permissions, or data handling that may change and a regression boundary is needed
4
- ---
5
-
6
- # Behavioral TDD
7
-
8
- Test the observable behavior at a stable boundary, not the implementation shape.
9
- Use this method when assessment selects the `testing` dimension.
10
-
11
-
12
- ## Method
13
-
14
- 1. Inspect the task requirement, current candidate, intended behavior, and real
15
- verification entry point with shared `task`, `policy`, and `evidence` operations.
16
- 2. State one behavior and its observable result. Choose the narrowest stable
17
- boundary a caller or user depends on; avoid private helpers and incidental
18
- representations.
19
- 3. Write one vertical RED slice that fails for the missing behavior and run it
20
- through the CLI so the failure is observed: `workit check test` (the repo's
21
- configured `test` check; `npx -y @brainervirus/workit-cli check test` when
22
- `workit` is not on PATH). Implement the smallest change, then run the same
23
- check GREEN. Close accepts only a fresh passing run of the configured check
24
- that `workit check` observed; a recorded "tests pass" is a note, and an
25
- ad-hoc `workit check -- <cmd>` never satisfies the gate. If no `test` check
26
- is configured or detected, add `workit.checks.json` (committed) with it.
27
- 4. Add only another slice for a distinct behavior or risk. Any edit makes the
28
- observed check stale: re-run `workit check test` before closing.
29
-
30
- ## Reject noisy tests
31
-
32
- - A dependency/version-pin assertion is not behavioral evidence.
33
- - A test that mirrors branches, private calls, or exact implementation structure
34
- is coupled to internals; replace it with the public effect.
35
- - Duplicate assertions and tests that add no distinct failure signal are noise;
36
- delete them.
37
- - Do not claim a passing test satisfies a different requirement.
38
- - Banned: tautologies (asserts what the code says, not what it must do),
39
- ghost loops (assert inside a possibly-empty loop), smoke-only renders,
40
- type-only or CSS-class coupling. If the test still passes when every
41
- imported function returns undefined, rewrite the assertion or delete it.
42
- `workit test-audit --diff` flags these; triage them with workit-test-audit.
43
- Turning acceptance criteria into scenarios and seams is workit-bdd.
44
-
45
- Run RED/GREEN through `workit check`, which records the observed result on the
46
- current task; shared `evidence` operations are for notes and non-test evidence.
47
- Do not add a second lifecycle, approval chain, or test workflow outside the
48
- current task state.
49
-
50
- ## Common mistakes
51
-
52
- | Mistake | Correction |
53
- | --- | --- |
54
- | "The pin changed, so assert the new string" | Exercise the affected consumer behavior. |
55
- | "The code is obvious" | A small vertical slice still proves the contract. |
56
- | Keeping a passing test after the boundary moved | Re-run `workit check test` on the current tree. |
@@ -1,26 +0,0 @@
1
- ---
2
- name: workit-blast-radius
3
- description: Use when a small-looking change could break something else, before close or merge
4
- ---
5
-
6
- # Blast radius beyond the diff
7
-
8
- A small diff is not a small risk. Prove the one fact it is safe because of,
9
- with runnable proof — not assertion.
10
-
11
-
12
- ## Method
13
-
14
- 1. List what the change touches: callers, shared state, contracts, config,
15
- migrations. Grep every caller of each touched function.
16
- 2. For each: state the one fact it is safe because of (type boundary,
17
- existing test, unreachable path) plus how to run the proof.
18
- 3. Run the proofs. Unproven claims stay labeled UNPROVEN in findings —
19
- never silently treated as safe.
20
- 4. Fix at the shared root (one guard where all callers route through),
21
- not per caller.
22
-
23
- ## Completion
24
-
25
- Blast-radius note in evidence or review: each risk with fact + proof
26
- command, or an UNPROVEN finding for what could not be proven.