@brainervirus/workit-opencode 8.17.1 → 8.18.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/assets/skills/workit-bdd/SKILL.md +1 -1
- package/assets/skills/workit-fanout/SKILL.md +2 -2
- package/assets/skills/workit-fanout/references/orchestration.md +21 -0
- package/assets/skills/workit-retro/SKILL.md +2 -2
- package/assets/skills/workit-review/SKILL.md +4 -1
- package/assets/skills/workit-ship/SKILL.md +1 -1
- package/assets/skills/workit-verify-app/SKILL.md +1 -1
- package/dist/plugin.js +26 -15
- package/package.json +2 -2
|
@@ -22,7 +22,7 @@ description: Turn requirements into Given/When/Then scenarios, agree the test se
|
|
|
22
22
|
then take the next scenario. Any edit makes the observation stale; re-run
|
|
23
23
|
before you claim it. A recorded "tests pass" is a note, and an ad-hoc
|
|
24
24
|
`workit check -- <cmd>` never satisfies the gate: only the configured
|
|
25
|
-
`test` check does. No `test` detected? Create
|
|
25
|
+
`test` check does. No `test` detected? Create `.workit/checks.json`, copying
|
|
26
26
|
in every check the repo already runs: once it exists it replaces the
|
|
27
27
|
detected defaults.
|
|
28
28
|
5. **Mock only at system boundaries:** network, clock, randomness, other
|
|
@@ -37,8 +37,8 @@ units of ~100+ lines each. Coupled work stays with one owner. Workit never spawn
|
|
|
37
37
|
6. **Retry once, then escalate.** A stuck or failed slice gets one retry with
|
|
38
38
|
a fresh brief (`workit fanout brief <slice> --mode resume`, same branch);
|
|
39
39
|
if that fails too, re-slice it, take it over, or report the gap. Never
|
|
40
|
-
chain resumes. A
|
|
41
|
-
|
|
40
|
+
chain resumes. A fix round and recheck that both fail: a decider, not a
|
|
41
|
+
third round; product choices go to the user (`references/orchestration.md`).
|
|
42
42
|
7. **Verify by the workspace `verification` setting** (`workit grant show`).
|
|
43
43
|
A report is a pointer, never evidence. `self`: a verifier that wrote none
|
|
44
44
|
of the slices, or you if you wrote none. `independent` or high risk: a
|
|
@@ -43,6 +43,27 @@ rulings). If the retry fails too, escalate instead of a third worker:
|
|
|
43
43
|
re-slice it smaller (`workit fanout plan` again), take it over yourself, or
|
|
44
44
|
report the gap to the user.
|
|
45
45
|
|
|
46
|
+
## Stuck calls: the decider
|
|
47
|
+
|
|
48
|
+
A failed review gets one fix round and one recheck (the changed hunks only).
|
|
49
|
+
If the recheck fails, or a worker and a reviewer disagree, spawn a decider
|
|
50
|
+
instead of a third round. A decider is a fresh read-only agent, not the author
|
|
51
|
+
or the reviewer. It returns three things: the options, a pick by
|
|
52
|
+
reversibility (the option cheapest to undo wins a tie), and the exact change
|
|
53
|
+
list. The fixer applies that list once, then one focused recheck follows.
|
|
54
|
+
|
|
55
|
+
- Hard call (shared contract, security, migration): two deciders on different
|
|
56
|
+
models (Claude Code: a different `model` on the Agent call; OpenCode: a
|
|
57
|
+
child session on another configured agent; Codex, Cursor and Pi: one
|
|
58
|
+
decider, since the host picks no model). If they disagree, you take the
|
|
59
|
+
more reversible option.
|
|
60
|
+
- Record it: `workit ledger decision "<pick>" --by decider --why "<why it is
|
|
61
|
+
the more reversible>" --view "<decider A: view>" --view "<decider B: view>"`.
|
|
62
|
+
The row is listed as a decider call; it is never the user's answer to a
|
|
63
|
+
product choice and never a verdict.
|
|
64
|
+
- A product choice, or authority nobody here has, is not a decider's call:
|
|
65
|
+
ask the user with your recommendation.
|
|
66
|
+
|
|
46
67
|
## Race a hard slice
|
|
47
68
|
|
|
48
69
|
When a hard slice has an uncertain approach and a cheap VERIFY, run 2-3
|
|
@@ -32,7 +32,7 @@ proposes and stops. Nothing changes until the user approves.
|
|
|
32
32
|
more cited occurrences**; a one-off is not a learning.
|
|
33
33
|
4. **Name the strongest enforcer that works**, strongest first: architecture
|
|
34
34
|
or types (the mistake cannot be written) > lint rule > configured check
|
|
35
|
-
(
|
|
35
|
+
(`.workit/checks.json`, `workit check <name>`) or CI job > test >
|
|
36
36
|
`CODING_STANDARDS.md` (judgment the reviewer reads) > AGENTS.md pointer
|
|
37
37
|
(navigation only). A mechanical rule gets a check, not prose: a check can
|
|
38
38
|
fail, a sentence cannot. From types to test, the proof is that the new
|
|
@@ -60,7 +60,7 @@ Bad: "Agents seem lost in the build. Added 'read the build docs carefully' to
|
|
|
60
60
|
AGENTS.md." One vague occurrence, no citation, a no-op line, auto-applied.
|
|
61
61
|
|
|
62
62
|
Good: "Navigation, 3 occurrences (rulings 12, 19; PR #88 thread): agents sought
|
|
63
|
-
check names in package.json, missing
|
|
63
|
+
check names in package.json, missing `.workit/checks.json`. Enforcer: AGENTS.md
|
|
64
64
|
pointer, +74 bytes, minus the stale Commands paragraph (-210). Unstable check,
|
|
65
65
|
2 occurrences (ledger rows 31, 44: `e2e` red then green on one SHA): a debug
|
|
66
66
|
slice to fix the check. Approve?"
|
|
@@ -36,7 +36,10 @@ independent; `type-check-only` never proves a behavior change.
|
|
|
36
36
|
7. Record the verdict:
|
|
37
37
|
`workit ledger verdict verified|failed|blocked --kind review --branch <b> --how "<what you ran and read>"`
|
|
38
38
|
under your own session (the one the lead or the hook gave you).
|
|
39
|
-
8. **
|
|
39
|
+
8. **Bound the loop.** A failed verdict gets one fix round and one recheck of
|
|
40
|
+
the changed hunks. Still failing, or stuck: no third round; the lead spawns
|
|
41
|
+
a decider (call the skill tool with `workit-fanout`). A product choice goes to the user.
|
|
42
|
+
9. **Harness note** (optional): friction caused by the repo or harness, not
|
|
40
43
|
the change (a flaky fixture, a missing check, a misleading doc or skill
|
|
41
44
|
line), goes on its own `Harness note:` line in the report and any PR
|
|
42
45
|
comment, never as a finding or in the verdict, so a later retro finds it.
|
|
@@ -30,7 +30,7 @@ once the verdict is accepted. `merged`: step 7. `null` (closed, not merged):
|
|
|
30
30
|
stop and report. Stop early only for a new consequential choice, a host
|
|
31
31
|
denial, a review comment that needs a product decision, a required update
|
|
32
32
|
that repeats because the base keeps moving, or after 3 failed fix attempts on
|
|
33
|
-
the same check.
|
|
33
|
+
the same check. Fix round and recheck both failed: a decider (call the skill tool with `workit-fanout`).
|
|
34
34
|
|
|
35
35
|
1. **Open.** `workit git push`, then `workit pr create --title "<title>"
|
|
36
36
|
--body-file <f|->` plus `--issue <#n|ID>` per issue (`--refs`: no close; idempotent;
|
|
@@ -35,7 +35,7 @@ way a user would.
|
|
|
35
35
|
never run is a guess; do not hand it off.
|
|
36
36
|
4. **Make the driver a named check** when it is cheap and deterministic. A
|
|
37
37
|
named check is one command without shell syntax (put pipes and setup in a
|
|
38
|
-
script). Add `"verify": "<driver>"` to `checks` in
|
|
38
|
+
script). Add `"verify": "<driver>"` to `checks` in `.workit/checks.json`; if
|
|
39
39
|
the file does not exist yet, first copy in every check the repo already runs
|
|
40
40
|
(test, lint, typecheck), because the file replaces the detected defaults.
|
|
41
41
|
`workit check verify` then records it, and it joins the verification gate
|
package/dist/plugin.js
CHANGED
|
@@ -12397,8 +12397,13 @@ import { spawnSync as spawnSync3 } from "node:child_process";
|
|
|
12397
12397
|
import fs9 from "node:fs";
|
|
12398
12398
|
import path9 from "node:path";
|
|
12399
12399
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
12400
|
-
var CHECKS_FILE = "workit
|
|
12401
|
-
var
|
|
12400
|
+
var CHECKS_FILE = ".workit/checks.json";
|
|
12401
|
+
var LEGACY_CHECKS_FILE = "workit.checks.json";
|
|
12402
|
+
function checksConfigFile(root) {
|
|
12403
|
+
const current = path9.join(root, CHECKS_FILE);
|
|
12404
|
+
const legacy = path9.join(root, LEGACY_CHECKS_FILE);
|
|
12405
|
+
return !fs9.existsSync(current) && fs9.existsSync(legacy) ? legacy : current;
|
|
12406
|
+
}
|
|
12402
12407
|
function repoHeavyCommands(root) {
|
|
12403
12408
|
try {
|
|
12404
12409
|
const value = JSON.parse(fs9.readFileSync(checksConfigFile(root), "utf8"));
|
|
@@ -12522,19 +12527,25 @@ var GATES = ["testing", "verification"];
|
|
|
12522
12527
|
function loadCheckConfig(cwd) {
|
|
12523
12528
|
const root = checkRoot(cwd);
|
|
12524
12529
|
const file = checksConfigFile(root);
|
|
12530
|
+
const legacy = path9.basename(file) === LEGACY_CHECKS_FILE;
|
|
12531
|
+
const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
|
|
12532
|
+
const note = legacy ? {
|
|
12533
|
+
note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
|
|
12534
|
+
} : {};
|
|
12525
12535
|
const none = (error = null) => ({
|
|
12526
|
-
source: error ?
|
|
12536
|
+
source: error ? label : "none",
|
|
12527
12537
|
root,
|
|
12528
12538
|
checks: [],
|
|
12529
12539
|
gates: {},
|
|
12530
|
-
error
|
|
12540
|
+
error,
|
|
12541
|
+
...note
|
|
12531
12542
|
});
|
|
12532
12543
|
let text = null;
|
|
12533
12544
|
try {
|
|
12534
12545
|
text = fs9.readFileSync(file, "utf8");
|
|
12535
12546
|
} catch (error) {
|
|
12536
12547
|
if (error.code !== "ENOENT")
|
|
12537
|
-
return none(`cannot read ${
|
|
12548
|
+
return none(`cannot read ${label}: ${error.message}`);
|
|
12538
12549
|
}
|
|
12539
12550
|
if (text === null) {
|
|
12540
12551
|
const checks = detectedChecks(root);
|
|
@@ -12544,33 +12555,33 @@ function loadCheckConfig(cwd) {
|
|
|
12544
12555
|
try {
|
|
12545
12556
|
parsed = JSON.parse(text);
|
|
12546
12557
|
} catch {
|
|
12547
|
-
return none(`${
|
|
12558
|
+
return none(`${label} is not valid JSON`);
|
|
12548
12559
|
}
|
|
12549
12560
|
if (!isRecord(parsed) || !isRecord(parsed.checks))
|
|
12550
|
-
return none(`${
|
|
12561
|
+
return none(`${label} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
|
|
12551
12562
|
const checks = [];
|
|
12552
12563
|
for (const [name, value] of Object.entries(parsed.checks)) {
|
|
12553
12564
|
if (!CHECK_NAME.test(name))
|
|
12554
|
-
return none(`${
|
|
12565
|
+
return none(`${label}: check name "${name}" must match ${CHECK_NAME.source}`);
|
|
12555
12566
|
const argv = typeof value === "string" ? splitCommand(value) : Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && item.length > 0) ? value : new Error("expected a command string or a non-empty argv array");
|
|
12556
12567
|
if (argv instanceof Error)
|
|
12557
|
-
return none(`${
|
|
12568
|
+
return none(`${label}: check "${name}": ${argv.message}`);
|
|
12558
12569
|
checks.push({ name, argv, accepts: [argv] });
|
|
12559
12570
|
}
|
|
12560
12571
|
const gates = {};
|
|
12561
12572
|
if (parsed.gates !== undefined) {
|
|
12562
12573
|
if (!isRecord(parsed.gates))
|
|
12563
|
-
return none(`${
|
|
12574
|
+
return none(`${label}: gates must be {"testing":"<name>"|["<name>",…]}`);
|
|
12564
12575
|
for (const [gate, value] of Object.entries(parsed.gates)) {
|
|
12565
12576
|
if (!GATES.includes(gate))
|
|
12566
|
-
return none(`${
|
|
12577
|
+
return none(`${label}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
|
|
12567
12578
|
const names = typeof value === "string" ? [value] : value;
|
|
12568
12579
|
if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
|
|
12569
|
-
return none(`${
|
|
12580
|
+
return none(`${label}: gates.${gate} must name configured checks`);
|
|
12570
12581
|
gates[gate] = names;
|
|
12571
12582
|
}
|
|
12572
12583
|
}
|
|
12573
|
-
return { source:
|
|
12584
|
+
return { source: label, root, checks, gates, error: null, ...note, resources: parsed.resources };
|
|
12574
12585
|
}
|
|
12575
12586
|
var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
|
|
12576
12587
|
function matchesNamedCheck(config, name, argv, cwd) {
|
|
@@ -15367,7 +15378,7 @@ var productDecision = (task, ledger) => {
|
|
|
15367
15378
|
};
|
|
15368
15379
|
const since = Date.parse(task.createdAt);
|
|
15369
15380
|
const branch = ledger.branch();
|
|
15370
|
-
const row = ledger.rows().findLast((item) => item.type === "decision" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
15381
|
+
const row = ledger.rows().findLast((item) => item.type === "decision" && item.by !== "decider" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
15371
15382
|
return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
|
|
15372
15383
|
met: false,
|
|
15373
15384
|
reason: 'open product choice: ask the user, then `workit ledger decision "<choice>" --why "<reason>"` (or, if it is not open, `workit policy assess --judge product-choice=no --why "<reason>"`)',
|
|
@@ -15862,7 +15873,7 @@ function evaluateObservedGate(task, requirement, statuses, config) {
|
|
|
15862
15873
|
return {
|
|
15863
15874
|
satisfied: [],
|
|
15864
15875
|
considered,
|
|
15865
|
-
reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE}
|
|
15876
|
+
reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE}, e.g. {"checks":{"test":"<your test command>"}}, then run ${checkCommand("test", runtimeVersion())}`
|
|
15866
15877
|
};
|
|
15867
15878
|
const satisfied = fresh.filter(named);
|
|
15868
15879
|
if (satisfied.length)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-opencode",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.18.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit OpenCode plugin — native tools, adaptive policy, and sixteen method skills",
|
|
6
6
|
"keywords": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"typecheck": "tsc --noEmit"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@brainervirus/workit-core": "^8.
|
|
42
|
+
"@brainervirus/workit-core": "^8.18.0",
|
|
43
43
|
"@opencode/plugin": "2.0.18"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|