@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.
- package/README.md +1 -1
- package/package.json +1 -1
- package/skills/workit-bdd/SKILL.md +35 -29
- package/skills/workit-continue/SKILL.md +53 -0
- package/skills/workit-debug/SKILL.md +41 -36
- package/skills/workit-deslop/SKILL.md +35 -25
- package/skills/workit-fanout/SKILL.md +62 -0
- package/skills/workit-fanout/references/brief.md +56 -0
- package/skills/workit-implement/SKILL.md +48 -44
- package/skills/workit-review/SKILL.md +42 -51
- package/skills/workit-review/references/impact.md +24 -0
- package/skills/workit-shape/SKILL.md +71 -0
- package/skills/workit-shape/references/diagrams.md +17 -0
- package/skills/workit-shape/references/knowledge.md +58 -0
- package/skills/workit-shape/references/mockups.md +15 -0
- package/skills/workit-shape/references/slicing.md +42 -0
- package/skills/workit-ship/SKILL.md +52 -0
- package/skills/workit-test-audit/SKILL.md +10 -2
- package/skills/workit-verify-app/SKILL.md +63 -0
- package/skills/workit-verify-app/references/template.md +49 -0
- package/src/core/external-action-effects.ts +2 -2
- package/src/core/methods.ts +68 -98
- package/src/core/skill-manifests.ts +49 -25
- package/src/hooks/handle.ts +29 -5
- package/src/hooks/hosts/codex.ts +1 -1
- package/src/ledger.ts +19 -10
- package/templates/plan-template.md +17 -18
- package/templates/spec-template.md +4 -3
- package/skills/workit-babysit/SKILL.md +0 -37
- package/skills/workit-behavioral-tdd/SKILL.md +0 -56
- package/skills/workit-blast-radius/SKILL.md +0 -26
- package/skills/workit-challenge/SKILL.md +0 -47
- package/skills/workit-diagram/SKILL.md +0 -27
- package/skills/workit-green-run/SKILL.md +0 -24
- package/skills/workit-handoff/SKILL.md +0 -37
- package/skills/workit-mockup/SKILL.md +0 -23
- package/skills/workit-plan/SKILL.md +0 -45
- package/skills/workit-steer/SKILL.md +0 -39
- package/templates/execution-contract.md +0 -40
package/src/core/methods.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { Assurance, Capability, Dimension, Policy } from "./task-contract";
|
|
2
2
|
|
|
3
3
|
export type MethodId =
|
|
4
|
-
| "workit-
|
|
5
|
-
| "workit-
|
|
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-
|
|
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-
|
|
20
|
-
"workit-
|
|
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
|
-
|
|
23
|
-
"workit-implement": {
|
|
24
|
-
|
|
25
|
-
//
|
|
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-
|
|
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
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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-
|
|
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-
|
|
12
|
-
"workit-
|
|
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-
|
|
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-
|
|
33
|
-
"wk-
|
|
34
|
-
"wk-
|
|
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
|
-
|
|
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)
|
package/src/hooks/handle.ts
CHANGED
|
@@ -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
|
-
|
|
27
|
-
|
|
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 {
|
|
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",
|
package/src/hooks/hosts/codex.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
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 ${
|
|
1120
|
-
|
|
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>
|
|
1
|
+
# <Feature> plan
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Spec: `docs/<slug>/spec.md` · Goal: <one sentence>
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
10
|
+
## Global constraints
|
|
9
11
|
|
|
10
|
-
|
|
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
|
-
###
|
|
19
|
-
|
|
20
|
-
- [ ] **Step 1: <action>**
|
|
16
|
+
### S1 <type>/<slug> (base: main)
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
|
26
|
-
| --- | --- |
|
|
27
|
-
|
|
|
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
|
-
<!--
|
|
23
|
-
|
|
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
|
-
<!--
|
|
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.
|