@brainervirus/workit-claude-code 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/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/agents/implementer.md +25 -15
- package/agents/reviewer.md +21 -15
- package/agents/verifier.md +27 -18
- package/assets/templates/plan-template.md +17 -18
- package/assets/templates/spec-template.md +4 -3
- package/dist/workit-hook.js +75 -106
- package/dist/workit.js +62 -30
- package/package.json +3 -3
- package/skills/bdd/SKILL.md +35 -38
- package/skills/continue/SKILL.md +53 -0
- package/skills/debug/SKILL.md +41 -45
- package/skills/deslop/SKILL.md +35 -34
- package/skills/fanout/SKILL.md +62 -0
- package/skills/fanout/references/brief.md +56 -0
- package/skills/implement/SKILL.md +48 -53
- package/skills/review/SKILL.md +42 -60
- package/skills/review/references/impact.md +24 -0
- package/skills/shape/SKILL.md +71 -0
- package/skills/shape/references/diagrams.md +17 -0
- package/skills/shape/references/knowledge.md +58 -0
- package/skills/shape/references/mockups.md +15 -0
- package/skills/shape/references/slicing.md +42 -0
- package/skills/ship/SKILL.md +52 -0
- package/skills/test-audit/SKILL.md +10 -11
- package/skills/verify-app/SKILL.md +63 -0
- package/skills/verify-app/references/template.md +49 -0
- package/assets/templates/execution-contract.md +0 -40
- package/skills/babysit/SKILL.md +0 -46
- package/skills/behavioral-tdd/SKILL.md +0 -65
- package/skills/blast-radius/SKILL.md +0 -35
- package/skills/challenge/SKILL.md +0 -56
- package/skills/diagram/SKILL.md +0 -36
- package/skills/green-run/SKILL.md +0 -33
- package/skills/handoff/SKILL.md +0 -46
- package/skills/mockup/SKILL.md +0 -32
- package/skills/plan/SKILL.md +0 -54
- package/skills/steer/SKILL.md +0 -48
package/dist/workit.js
CHANGED
|
@@ -65,7 +65,7 @@ var package_default;
|
|
|
65
65
|
var init_package = __esm(() => {
|
|
66
66
|
package_default = {
|
|
67
67
|
name: "@brainervirus/workit-cli",
|
|
68
|
-
version: "
|
|
68
|
+
version: "4.0.0",
|
|
69
69
|
private: false,
|
|
70
70
|
description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
71
71
|
keywords: [
|
|
@@ -15598,13 +15598,13 @@ var METHODS, methodMatches = (definition, requirement) => definition.dimensions?
|
|
|
15598
15598
|
};
|
|
15599
15599
|
var init_methods = __esm(() => {
|
|
15600
15600
|
METHODS = {
|
|
15601
|
-
"workit-
|
|
15602
|
-
"workit-
|
|
15601
|
+
"workit-shape": { dimensions: ["challenge", "decisions", "artifacts", "continuity"] },
|
|
15602
|
+
"workit-bdd": { dimensions: ["testing"] },
|
|
15603
15603
|
"workit-review": { dimensions: ["review"], ruleIds: ["fresh-context-review", "self-review"] },
|
|
15604
|
-
"workit-
|
|
15605
|
-
"workit-
|
|
15604
|
+
"workit-implement": { ruleIds: ["mechanical-existing-checks"] },
|
|
15605
|
+
"workit-fanout": { dimensions: ["delegation"] },
|
|
15606
15606
|
"workit-debug": {},
|
|
15607
|
-
"workit-
|
|
15607
|
+
"workit-continue": {},
|
|
15608
15608
|
"workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
|
|
15609
15609
|
};
|
|
15610
15610
|
});
|
|
@@ -21627,10 +21627,15 @@ function recordVerdict(context, input) {
|
|
|
21627
21627
|
if (key.dirty === true)
|
|
21628
21628
|
return err("blocked", `dirty_worktree: ${branch} has uncommitted changes, so its head is not what was judged`, "commit or stash the changes, re-check, then record the verdict");
|
|
21629
21629
|
const session = context.actor.session;
|
|
21630
|
-
const selfReason = input.self === true ? "flag" : session ? null : "no_session";
|
|
21630
|
+
const selfReason = input.self === true ? "flag" : session && input.derivedFrom !== null ? null : "no_session";
|
|
21631
21631
|
const self2 = selfReason !== null;
|
|
21632
|
-
|
|
21633
|
-
|
|
21632
|
+
const authors = authorSessions(context.cwd, ledger.value.rows, branch, {
|
|
21633
|
+
base: key.base,
|
|
21634
|
+
head: key.head
|
|
21635
|
+
});
|
|
21636
|
+
const authoring = [session, input.derivedFrom].find((candidate) => typeof candidate === "string" && authors.has(candidate));
|
|
21637
|
+
if (!self2 && authoring)
|
|
21638
|
+
return err("blocked", `author_verdict: session ${authoring} authored ${branch}; a verdict must come from a different session`, input.derivedFrom === undefined ? 'have a non-author session record the verdict, or pass --self (a self verdict is never accepted by merge:"verified")' : "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)");
|
|
21634
21639
|
const link = checkSupersede(context, "verdict", self2);
|
|
21635
21640
|
if (!link.ok)
|
|
21636
21641
|
return link;
|
|
@@ -23602,7 +23607,7 @@ var run3 = (root, args) => {
|
|
|
23602
23607
|
cwd: target.data
|
|
23603
23608
|
}
|
|
23604
23609
|
});
|
|
23605
|
-
}, prBabysitNext = (output) => `PR ready: ${output}. Babysitting was explicitly requested. Follow workit-
|
|
23610
|
+
}, prBabysitNext = (output) => `PR ready: ${output}. Babysitting was explicitly requested. Follow workit-ship to resolve conflicts, review threads, and get checks green. Stop at PR-ready; a PR or babysit request does not authorize merge or release.`, executeConcreteExternalAction = async (request, root, marker, dateMs, step, workText, caller, approvedBeforeDigest, approvedBeforeExists, coordinationRoot = root, approvedTip, approvedRemote, approvedAccount, approvedApiHost, approvedSourceCommit, approvedCommit, approvedRemoteBase, approvedLocalBase, writerActionLease, approvedSourceBranch, approvedMergeBaseCommit, expectedWorkspaceRevision) => {
|
|
23606
23611
|
if (localAction(request.operation)) {
|
|
23607
23612
|
if (!caller)
|
|
23608
23613
|
return failure2("capability_unavailable", "local external action writer authority is unavailable", { outcome: "not_started" });
|
|
@@ -23717,7 +23722,7 @@ var run3 = (root, args) => {
|
|
|
23717
23722
|
...result,
|
|
23718
23723
|
...request.payload.babysit !== undefined ? { babysit: request.payload.babysit } : {},
|
|
23719
23724
|
...babysit ? {
|
|
23720
|
-
babysitSkill: "workit-
|
|
23725
|
+
babysitSkill: "workit-ship",
|
|
23721
23726
|
next: prBabysitNext(String(result.output ?? ""))
|
|
23722
23727
|
} : {}
|
|
23723
23728
|
});
|
|
@@ -68239,22 +68244,17 @@ var WORKIT_METHOD_SKILLS, skillManifestNames = (root) => existsSync12(root) ? re
|
|
|
68239
68244
|
};
|
|
68240
68245
|
var init_skill_manifests = __esm(() => {
|
|
68241
68246
|
WORKIT_METHOD_SKILLS = [
|
|
68242
|
-
"workit-
|
|
68243
|
-
"workit-behavioral-tdd",
|
|
68244
|
-
"workit-review",
|
|
68245
|
-
"workit-plan",
|
|
68247
|
+
"workit-shape",
|
|
68246
68248
|
"workit-implement",
|
|
68249
|
+
"workit-review",
|
|
68247
68250
|
"workit-debug",
|
|
68248
|
-
"workit-
|
|
68249
|
-
"workit-
|
|
68250
|
-
"workit-blast-radius",
|
|
68251
|
-
"workit-deslop",
|
|
68252
|
-
"workit-diagram",
|
|
68253
|
-
"workit-mockup",
|
|
68254
|
-
"workit-green-run",
|
|
68255
|
-
"workit-steer",
|
|
68251
|
+
"workit-ship",
|
|
68252
|
+
"workit-continue",
|
|
68256
68253
|
"workit-bdd",
|
|
68257
|
-
"workit-test-audit"
|
|
68254
|
+
"workit-test-audit",
|
|
68255
|
+
"workit-deslop",
|
|
68256
|
+
"workit-fanout",
|
|
68257
|
+
"workit-verify-app"
|
|
68258
68258
|
];
|
|
68259
68259
|
if (false) {}
|
|
68260
68260
|
});
|
|
@@ -68531,8 +68531,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
68531
68531
|
}, assetPathsFor = (host, dev) => {
|
|
68532
68532
|
const pkg = path32.join(dev, "packages", `workit-${host}`);
|
|
68533
68533
|
switch (host) {
|
|
68534
|
-
case "opencode":
|
|
68535
|
-
|
|
68534
|
+
case "opencode": {
|
|
68535
|
+
const packaged = path32.join(pkg, "assets", "skills");
|
|
68536
|
+
const root = existsSync13(packaged) ? packaged : path32.join(dev, "packages", "workit-core", "skills");
|
|
68537
|
+
return WORKIT_METHOD_SKILLS.map((skill) => path32.join(root, skill, "SKILL.md"));
|
|
68538
|
+
}
|
|
68536
68539
|
case "cursor":
|
|
68537
68540
|
return [
|
|
68538
68541
|
path32.join(pkg, "assets", "templates", "workit-contract.md"),
|
|
@@ -79819,6 +79822,7 @@ var exports_ledger = {};
|
|
|
79819
79822
|
__export(exports_ledger, {
|
|
79820
79823
|
run: () => run18
|
|
79821
79824
|
});
|
|
79825
|
+
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
79822
79826
|
import { parseArgs } from "node:util";
|
|
79823
79827
|
function targetBranch(io, rows, branch, pr) {
|
|
79824
79828
|
if (pr !== undefined) {
|
|
@@ -79901,9 +79905,13 @@ async function run18(argv, io) {
|
|
|
79901
79905
|
const target = targetBranch(io, rows, values.branch, pr);
|
|
79902
79906
|
if (!target.ok)
|
|
79903
79907
|
return failed(io, target);
|
|
79908
|
+
const acting = actorFor(io, values);
|
|
79909
|
+
if (acting instanceof Error)
|
|
79910
|
+
return usage3(io, acting.message);
|
|
79911
|
+
const { actor } = acting;
|
|
79904
79912
|
const context = {
|
|
79905
79913
|
cwd: io.cwd,
|
|
79906
|
-
actor
|
|
79914
|
+
actor,
|
|
79907
79915
|
branch: target.value,
|
|
79908
79916
|
base: values.base ?? null,
|
|
79909
79917
|
...pr === undefined ? {} : { pr },
|
|
@@ -79931,10 +79939,30 @@ async function run18(argv, io) {
|
|
|
79931
79939
|
how: values.how,
|
|
79932
79940
|
surface: values.surface ?? null,
|
|
79933
79941
|
self: values.self === true,
|
|
79934
|
-
evidenceRefs: values.evidence
|
|
79935
|
-
|
|
79942
|
+
evidenceRefs: values.evidence,
|
|
79943
|
+
..."derivedFrom" in acting ? { derivedFrom: acting.derivedFrom } : {}
|
|
79944
|
+
})), (row) => `recorded verdict ${row.id}: ${row.result} [${row.kind}] for ${row.branch} @ ${(row.head ?? "").slice(0, 12)} as ${actor.session ?? "no session"}${row.self ? ` (self${row.selfReason === "no_session" ? ": WORKIT_SESSION_ID unset" : ""}; never accepted)` : ""}`);
|
|
79936
79945
|
}
|
|
79937
|
-
var USAGE7 = "workit ledger decision|ruling|verdict|list|check ... (workit help ledger for the grammar)", OPTIONS,
|
|
79946
|
+
var USAGE7 = "workit ledger decision|ruling|verdict|list|check ... (workit help ledger for the grammar)", OPTIONS, SESSION_SAFE2, ROLE_SAFE, actorFor = (io, values) => {
|
|
79947
|
+
const actor = actorFromEnv(io.env);
|
|
79948
|
+
if (values.session !== undefined && values.as !== undefined)
|
|
79949
|
+
return new Error("pass --session or --as, not both");
|
|
79950
|
+
if (values.session !== undefined) {
|
|
79951
|
+
if (!SESSION_SAFE2.test(values.session))
|
|
79952
|
+
return new Error("--session must be 1-128 characters of [A-Za-z0-9_.:@/+-]");
|
|
79953
|
+
return { actor: { ...actor, session: values.session } };
|
|
79954
|
+
}
|
|
79955
|
+
if (values.as !== undefined) {
|
|
79956
|
+
if (!ROLE_SAFE.test(values.as))
|
|
79957
|
+
return new Error("--as takes a lowercase role, e.g. verifier");
|
|
79958
|
+
const prefix = (actor.session ?? actor.host).slice(0, 96);
|
|
79959
|
+
return {
|
|
79960
|
+
actor: { ...actor, session: `${prefix}:${values.as}:${randomBytes4(4).toString("hex")}` },
|
|
79961
|
+
derivedFrom: actor.session
|
|
79962
|
+
};
|
|
79963
|
+
}
|
|
79964
|
+
return { actor };
|
|
79965
|
+
}, positiveInt2 = (value, flag) => {
|
|
79938
79966
|
if (value === undefined)
|
|
79939
79967
|
return;
|
|
79940
79968
|
const parsed = Number(value);
|
|
@@ -79967,8 +79995,12 @@ var init_ledger2 = __esm(() => {
|
|
|
79967
79995
|
type: { type: "string" },
|
|
79968
79996
|
last: { type: "string" },
|
|
79969
79997
|
supersedes: { type: "string" },
|
|
79998
|
+
session: { type: "string" },
|
|
79999
|
+
as: { type: "string" },
|
|
79970
80000
|
json: { type: "boolean" }
|
|
79971
80001
|
};
|
|
80002
|
+
SESSION_SAFE2 = /^[A-Za-z0-9_.:@/+-]{1,128}$/;
|
|
80003
|
+
ROLE_SAFE = /^[a-z][a-z0-9-]{0,31}$/;
|
|
79972
80004
|
});
|
|
79973
80005
|
|
|
79974
80006
|
// node_modules/@babel/parser/lib/index.js
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-claude-code",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit Claude Code plugin — session and per-turn task context, branch policy on git shell commands, workit method skills, and verifier/reviewer/implementer agents",
|
|
6
6
|
"keywords": [
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"build": "bun scripts/build.ts"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@brainervirus/workit-cli": "^
|
|
43
|
-
"@brainervirus/workit-core": "^
|
|
42
|
+
"@brainervirus/workit-cli": "^5.0.0",
|
|
43
|
+
"@brainervirus/workit-core": "^5.0.0"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=24"
|
package/skills/bdd/SKILL.md
CHANGED
|
@@ -1,51 +1,48 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bdd
|
|
3
|
-
description:
|
|
3
|
+
description: Turn requirements into Given/When/Then scenarios, agree the test seam, and work test-first in vertical RED/GREEN slices. Use for BDD, TDD, acceptance criteria, scenarios, Given/When/Then, write a test first.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Behavior first: Given/When/Then
|
|
7
7
|
|
|
8
|
-
Write
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
5.
|
|
28
|
-
|
|
29
|
-
make the smallest change, run `workit check test` GREEN, then the next.
|
|
30
|
-
6. Mock only at system boundaries: network, clock, randomness, other
|
|
31
|
-
processes, sometimes the filesystem. Never the unit or its internal
|
|
8
|
+
1. **Write the scenarios.** One behavior each, in the caller's words:
|
|
9
|
+
`Given <state>, When <action>, Then <observable result>`. Include the
|
|
10
|
+
unhappy paths callers depend on (denied, empty, invalid, timeout).
|
|
11
|
+
2. **Agree the seam.** The highest stable interface the scenario can be
|
|
12
|
+
observed through: a CLI verb, a public function, an HTTP route; ideally one
|
|
13
|
+
per feature. Do not test at a seam nobody agreed to.
|
|
14
|
+
3. **Name tests after scenarios.** The name is the Given/When/Then sentence;
|
|
15
|
+
the body is arrange, act, assert. Expected values come from the scenario (a
|
|
16
|
+
literal from a worked example, the spec, an external contract), never from
|
|
17
|
+
the code under test.
|
|
18
|
+
4. **Build in vertical slices.** Write one vertical RED slice that fails for
|
|
19
|
+
the missing behavior and run it through the CLI so the failure is observed:
|
|
20
|
+
`workit check test`. Make the smallest change, run the same check GREEN,
|
|
21
|
+
then take the next scenario. Any edit makes the observation stale; re-run
|
|
22
|
+
before you claim it. A recorded "tests pass" is a note, and an ad-hoc
|
|
23
|
+
`workit check -- <cmd>` never satisfies the gate: only the configured
|
|
24
|
+
`test` check does. No `test` detected? Create `workit.checks.json`, copying
|
|
25
|
+
in every check the repo already runs: once it exists it replaces the
|
|
26
|
+
detected defaults.
|
|
27
|
+
5. **Mock only at system boundaries:** network, clock, randomness, other
|
|
28
|
+
processes, sometimes the filesystem. Never the unit or its own
|
|
32
29
|
collaborators; use the real thing or an in-memory adapter behind a port.
|
|
30
|
+
6. **Gherkin only where the repo already uses it** (`.feature` files with
|
|
31
|
+
playwright-bdd, cucumber, jest-cucumber). Otherwise test names carry it.
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
Reject noise: version-pin assertions, tests that mirror private structure,
|
|
34
|
+
assertions inside a possibly-empty loop, smoke-only renders, duplicates. If a
|
|
35
|
+
test still passes when every imported function returns `undefined`, rewrite it
|
|
36
|
+
(workit-test-audit finds these).
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
seen RED then GREEN through `workit check test`, and the new tests have no
|
|
38
|
-
tautologies:
|
|
38
|
+
## Example
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
workit test-audit --diff && workit check test
|
|
42
|
-
```
|
|
40
|
+
Bad: `test("calculateTotal works", () => expect(calculateTotal(items)).toBe(items.reduce((s, i) => s + i.price, 0)))`
|
|
43
41
|
|
|
42
|
+
Good: `test("Given two items of 5 and 10, When totalled, Then the total is 15", () => expect(calculateTotal([{ price: 5 }, { price: 10 }])).toBe(15))`
|
|
44
43
|
|
|
45
|
-
##
|
|
44
|
+
## Check
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
and `implementer` agents take independent verification, fresh-context
|
|
51
|
-
review and isolated implementation.
|
|
46
|
+
```sh
|
|
47
|
+
workit test-audit --diff && workit check test
|
|
48
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: continue
|
|
3
|
+
description: Keep work on track across interruptions and sessions - sort new input, checkpoint, hand off with a resume brief, and pick up by verifying inherited claims. Use for resume, pick up, handoff, new session, interruption, change of direction.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Continue without losing the thread
|
|
7
|
+
|
|
8
|
+
## New input mid-task
|
|
9
|
+
|
|
10
|
+
- **Quick question:** answer it; change nothing else.
|
|
11
|
+
- **Same-task adjustment:** update the affected constraint and next step, then
|
|
12
|
+
keep going.
|
|
13
|
+
- **Separate request:** do not silently resume an old objective, and do not
|
|
14
|
+
drop the current one. Checkpoint it (below) if it must continue later, then
|
|
15
|
+
start the new work. Held items stay parked with their resume condition until
|
|
16
|
+
the user resumes them.
|
|
17
|
+
|
|
18
|
+
## Checkpoint and hand off
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
workit git commit -m "wip: <state>" --all # nothing lives only in your context
|
|
22
|
+
workit handoff --note "<state in one line>" --next "<next command>" --record
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The brief carries the branch, HEAD, dirty state, check freshness, verdict,
|
|
26
|
+
rulings and the next command. Add only what it cannot know: choices still
|
|
27
|
+
open, approaches that failed and why. Work spanning repos gets one brief per
|
|
28
|
+
checkout, each with its branch and delivery endpoint.
|
|
29
|
+
|
|
30
|
+
## Pick up
|
|
31
|
+
|
|
32
|
+
1. In the checkout: `workit handoff`, then `workit ledger list` and
|
|
33
|
+
`git log --oneline -10`.
|
|
34
|
+
2. Trust the trail, verify the claims: re-run the checks the brief calls
|
|
35
|
+
stale, and confirm each "done" item against the goal on the real artifact
|
|
36
|
+
(a pushed SHA, a PR state, a running feature). Do not re-derive settled
|
|
37
|
+
decisions.
|
|
38
|
+
3. Continue to the recorded endpoint with the brief's next command.
|
|
39
|
+
|
|
40
|
+
## Example
|
|
41
|
+
|
|
42
|
+
Bad: a new session re-reads the whole codebase, re-asks the user which
|
|
43
|
+
approach to take, and redoes a finished slice.
|
|
44
|
+
|
|
45
|
+
Good: "`workit handoff`: feature/usage at 4be1, `test` stale, verdict none,
|
|
46
|
+
next `workit check test`. Re-ran it: exit 0. The brief says PR #42 is open:
|
|
47
|
+
`workit pr status` confirms, CI pending. Continuing with workit-ship."
|
|
48
|
+
|
|
49
|
+
## Check
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
workit handoff # read: "next command" is set and no check is listed as stale
|
|
53
|
+
```
|
package/skills/debug/SKILL.md
CHANGED
|
@@ -1,49 +1,45 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: debug
|
|
3
|
-
description:
|
|
3
|
+
description: Find a root cause before patching - start from a red-capable deterministic repro, rank hypotheses, bisect regressions, fix at the root with a regression test. Use for bug, broken, failing, flaky, regression, error, why does.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Debug
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
the `workit` CLI on the Bash tool: `workit <family> <action> --json`
|
|
47
|
-
(`workit --help` lists the verbs). The plugin's `verifier`, `reviewer`
|
|
48
|
-
and `implementer` agents take independent verification, fresh-context
|
|
49
|
-
review and isolated implementation.
|
|
6
|
+
# Debug from a red loop
|
|
7
|
+
|
|
8
|
+
## Steps
|
|
9
|
+
|
|
10
|
+
1. **Build the loop before any hypothesis.** One command that is red for the
|
|
11
|
+
user's exact symptom, deterministic, fast and runnable by you:
|
|
12
|
+
`workit check -- <repro>`. Shrink it until it fails in seconds. If the
|
|
13
|
+
symptom only shows on the running app, drive it with the project's
|
|
14
|
+
verify-<app> skill. No loop yet? Building it is the task; do not guess-patch.
|
|
15
|
+
2. **Read the failure, not the summary:** the full error, the failing value,
|
|
16
|
+
and every caller on its path.
|
|
17
|
+
3. **Rank three to five falsifiable hypotheses**, likeliest first. Test one at
|
|
18
|
+
a time with the loop or a tagged log line (`[DEBUG-<id>]`, removed at the
|
|
19
|
+
end with one grep). Keep a short hypothesis log so a dead idea stays dead.
|
|
20
|
+
4. **Regression? Bisect it:** `git bisect start <bad> <good>` then
|
|
21
|
+
`git bisect run <repro>`. The first bad commit names the cause.
|
|
22
|
+
5. **Fix at the root**, the one place every failing caller passes through.
|
|
23
|
+
Add a regression test at a seam that exercises the real bug pattern; if no
|
|
24
|
+
such seam exists, report that as a finding.
|
|
25
|
+
6. **Stop rule:** after three dead hypotheses, write down what is measured and
|
|
26
|
+
what is inferred, widen the loop, or ask for the one fact only the user has.
|
|
27
|
+
|
|
28
|
+
Every shipped line traces to evidence from the loop. A "might help" retry or
|
|
29
|
+
guard is a hypothesis, not a fix.
|
|
30
|
+
|
|
31
|
+
## Example
|
|
32
|
+
|
|
33
|
+
Bad: "Probably a race; added a retry." (no repro, nothing measured)
|
|
34
|
+
|
|
35
|
+
Good: "Repro: `workit check -- bun test lock.test.ts -t stale` red 10/10.
|
|
36
|
+
H1 dead pid not reclaimed - confirmed: `kill(pid, 0)` throws EPERM for another
|
|
37
|
+
user's pid and we treated it as dead. Fix: EPERM means alive. Loop green 10/10;
|
|
38
|
+
regression test pins the EPERM case."
|
|
39
|
+
|
|
40
|
+
## Check
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
workit check -- <repro> # red before the fix, green after
|
|
44
|
+
workit check test
|
|
45
|
+
```
|
package/skills/deslop/SKILL.md
CHANGED
|
@@ -1,40 +1,41 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: deslop
|
|
3
|
-
description:
|
|
3
|
+
description: Remove AI slop before a PR - dead code, comments that restate the code, filler prose - with a minimal diff and identical behavior. Use for deslop, clean up, slop, tidy before PR, remove dead code, trim the PR body.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Deslop code and prose
|
|
7
7
|
|
|
8
|
-
Throughput without quality is slop.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
3.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
8
|
+
Throughput without quality is slop. Deslop only removes; it never changes
|
|
9
|
+
behavior. A change that wants new behavior is its own change.
|
|
10
|
+
|
|
11
|
+
1. **Find it with tools first.** The repo's dead-code and lint tools on the
|
|
12
|
+
branch diff (for example `knip`, `ts-prune`, `vulture`, `cargo udeps`, or
|
|
13
|
+
the linter's unused rules), then read the diff:
|
|
14
|
+
`git diff <base>...HEAD`.
|
|
15
|
+
2. **Code.** Delete unused helpers and exports, stub references, debug
|
|
16
|
+
leftovers (`[DEBUG-` tags, stray logs), and comments that restate the next
|
|
17
|
+
line. Keep comments that say *why* (a constraint, a workaround with its
|
|
18
|
+
link), license headers and tool directives.
|
|
19
|
+
3. **Prose** (PR body, spec, docs): cut filler and hedging, keep real symbol
|
|
20
|
+
names and before-to-after numbers. One doc, one purpose.
|
|
21
|
+
4. **Minimal diff.** Deslop removes lines; it never moves logic. A removed
|
|
22
|
+
validator that changes behavior is not deslop.
|
|
23
|
+
5. **Re-run the checks** and report lines removed, not lines written. Nothing
|
|
24
|
+
to clean is a valid result: say what you checked ("0 removals; ran knip and
|
|
25
|
+
read the diff"). When a tracked task lists a `pre-pr-cleanup` requirement,
|
|
26
|
+
record this result as its evidence.
|
|
27
|
+
|
|
28
|
+
## Example
|
|
29
|
+
|
|
30
|
+
Bad: deleting `// retry: the gateway drops the first request after idle (#412)`
|
|
31
|
+
because "comments die".
|
|
32
|
+
|
|
33
|
+
Good: deleting `// increment the counter` above `count += 1`, an unused
|
|
34
|
+
`formatLegacyDate` export reported by knip, and two hedging paragraphs from the
|
|
35
|
+
PR body: "-34 lines, behavior unchanged, `workit check test` exit 0".
|
|
36
|
+
|
|
37
|
+
## Check
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
workit check test && git diff --stat <base>...HEAD
|
|
41
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fanout
|
|
3
|
+
description: Run independent slices in parallel - one worker per isolated worktree with a fixed brief and file-scope manifest, a non-author verifier per slice, results in the ledger. Use for fan out, parallelize, parallel agents, split the work, delegate, swarm.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fan out parallel workers
|
|
7
|
+
|
|
8
|
+
Fan out only independent slices: disjoint files, no shared mutable state, each
|
|
9
|
+
verifiable alone. Code-coupled work stays with one owner, who fans out after
|
|
10
|
+
the blocking part lands. A worker whose whole job is re-running one command is
|
|
11
|
+
ceremony; do it yourself.
|
|
12
|
+
|
|
13
|
+
1. **Slice** (workit-shape): each slice gets a branch, a file-scope manifest
|
|
14
|
+
(the globs it may write) and, if it depends on another, its stack parent.
|
|
15
|
+
2. **Check disjointness.** No two manifests overlap. Shared files (lockfile,
|
|
16
|
+
registry, barrel exports) belong to one slice, or to you after fan-in.
|
|
17
|
+
3. **Brief each worker** with the fixed template and refuse to spawn while a
|
|
18
|
+
field is empty: GOAL, SCOPE (the manifest), CONTEXT (pointers, not pasted
|
|
19
|
+
text), ACCEPTANCE (Given/When/Then), VERIFY (exact commands), TIMEBOX,
|
|
20
|
+
FORBIDDEN, REPORT, STANDING. STANDING is every standing order and user
|
|
21
|
+
directive so far, pasted verbatim into each spawn and respawn, because
|
|
22
|
+
directives decay across resumes. Template: `references/brief.md`.
|
|
23
|
+
4. **Spawn all workers in one message**, in the background, each in its own
|
|
24
|
+
worktree (Claude Code: the `implementer` agent; elsewhere
|
|
25
|
+
`git worktree add --detach ../<repo>-wt/<slug> origin/<base>`). The first
|
|
26
|
+
command a worker runs is `workit git branch <branch> --base <base>`.
|
|
27
|
+
5. **Judge liveness by side effects only:** new commits and pushes
|
|
28
|
+
(`git log <branch>`), PR and check changes (`workit pr status --branch <b>`).
|
|
29
|
+
No progress past the timebox means stuck. Stop the old worker and observe
|
|
30
|
+
that it exited (a timeout is not proof). `git worktree remove --force`
|
|
31
|
+
drops its uncommitted changes, so first record `git -C <wt> status --short`
|
|
32
|
+
in the ledger or your report; only then remove the worktree. Respawn with the brief in
|
|
33
|
+
`MODE: resume` (consolidated: original, later directives, its last report):
|
|
34
|
+
the new worker runs `git switch <branch>` in its fresh worktree instead of
|
|
35
|
+
`workit git branch`. Never two live workers on one branch. Replace at most
|
|
36
|
+
twice, then re-slice or report the gap. Never chain resumes.
|
|
37
|
+
6. **Verify each slice independently.** A fresh agent that did not write it
|
|
38
|
+
(Claude Code: the `verifier` agent) runs VERIFY and verify-<app>, then
|
|
39
|
+
`workit ledger verdict <result> --branch <b> --how "<evidence>"` under the
|
|
40
|
+
session you started it with (`WORKIT_SESSION_ID=<lead>-v<n>`, set by you,
|
|
41
|
+
never chosen by the author; Claude Code: the hook names one).
|
|
42
|
+
A worker's report is a pointer, never evidence.
|
|
43
|
+
7. **Fan in.** Compare `git diff --name-only <base>...<b>` with the slice's
|
|
44
|
+
SCOPE: any file outside it stops the fan-in with a report. Then
|
|
45
|
+
`workit ledger check --branch <b>` for each slice; restack
|
|
46
|
+
stacked slices with `workit stack sync`. Only you touch topology: workers
|
|
47
|
+
never rebase, retarget or merge. Then workit-ship.
|
|
48
|
+
|
|
49
|
+
## Example
|
|
50
|
+
|
|
51
|
+
Bad brief: "Do the API part and add tests." (no scope, no acceptance, no
|
|
52
|
+
verify command, so nobody can tell when it is done)
|
|
53
|
+
|
|
54
|
+
Good brief: `references/brief.md` (GOAL: `GET /v1/usage` returns daily run
|
|
55
|
+
counts; SCOPE: `src/routes/usage.ts`, `test/usage.test.ts`; VERIFY:
|
|
56
|
+
`workit check test`; ...).
|
|
57
|
+
|
|
58
|
+
## Check
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
workit ledger check --branch <b> # per slice: accepted (current, passing, independent)
|
|
62
|
+
```
|