@brainervirus/workit-core 0.11.0 → 1.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 +5 -3
- package/package.json +8 -3
- package/scripts/analyze-release-scope.ts +4 -1
- package/scripts/doctor-check.ts +3 -1
- package/scripts/install-codex-plugin.sh +28 -0
- package/scripts/install-cursor-plugin.sh +1 -0
- package/scripts/install-opencode-plugin.sh +1 -0
- package/scripts/install-pi-package.sh +56 -0
- package/scripts/rewrite-workspace-deps.ts +22 -5
- package/scripts/sync-release-manifests.ts +8 -5
- package/scripts/sync-runtime.sh +7 -3
- package/scripts/validate-cursor-marketplace.ts +16 -62
- package/skills/workit-babysit/SKILL.md +33 -0
- package/skills/workit-behavioral-tdd/SKILL.md +53 -0
- package/skills/workit-blast-radius/SKILL.md +31 -0
- package/skills/workit-challenge/SKILL.md +62 -0
- package/skills/workit-debug/SKILL.md +61 -0
- package/skills/workit-deslop/SKILL.md +36 -0
- package/skills/workit-diagram/SKILL.md +32 -0
- package/skills/workit-green-run/SKILL.md +29 -0
- package/skills/workit-handoff/SKILL.md +43 -0
- package/skills/workit-implement/SKILL.md +46 -0
- package/skills/workit-mockup/SKILL.md +28 -0
- package/skills/workit-plan/SKILL.md +66 -0
- package/skills/workit-review/SKILL.md +60 -0
- package/skills/workit-steer/SKILL.md +32 -0
- package/src/core/authority.ts +918 -0
- package/src/core/boundary.ts +29 -0
- package/src/core/branch.ts +26 -255
- package/src/core/changelog.ts +95 -18
- package/src/core/commit-flavors.ts +65 -0
- package/src/core/config-conversion.ts +183 -0
- package/src/core/config.ts +42 -0
- package/src/core/cutover.ts +721 -0
- package/src/core/detect-hosts.ts +107 -0
- package/src/core/doctor.ts +428 -18
- package/src/core/external-action-effects.ts +1171 -0
- package/src/core/external-action.ts +541 -0
- package/src/core/init.ts +83 -32
- package/src/core/methods.ts +125 -0
- package/src/core/policy-resolver.ts +523 -0
- package/src/core/pr-create.ts +7 -1
- package/src/core/registration.ts +69 -4
- package/src/core/repo-context.ts +59 -1
- package/src/core/setup.ts +28 -0
- package/src/core/skill-manifests.ts +40 -42
- package/src/core/support-matrix.ts +8 -3
- package/src/core/sync-runtime.ts +15 -3
- package/src/core/task-context.ts +179 -0
- package/src/core/task-contract.ts +1136 -0
- package/src/core/task-engine.ts +2562 -0
- package/src/core/task-evaluation.ts +679 -0
- package/src/core/task-store.ts +1071 -0
- package/src/core/tracker-issues.ts +237 -0
- package/src/core/uninstall.ts +67 -3
- package/src/core/vcs-config.ts +53 -22
- package/src/core/workers.ts +166 -0
- package/src/core/workspaces.ts +3 -1
- package/src/core/youtrack-tools.ts +2 -0
- package/src/core/youtrack.ts +145 -10
- package/src/core.ts +126 -0
- package/templates/execution-contract.md +17 -50
- package/templates/plan-template.md +3 -3
- package/templates/spec-template.md +15 -3
- package/templates/workit-contract.md +12 -0
- package/commands/wk-changelog.md +0 -2
- package/commands/wk-commit.md +0 -2
- package/commands/wk-docs-refresh.md +0 -2
- package/commands/wk-handoff.md +0 -2
- package/commands/wk-implement.md +0 -2
- package/commands/wk-init.md +0 -2
- package/commands/wk-issue-update.md +0 -2
- package/commands/wk-meetings.md +0 -2
- package/commands/wk-pr.md +0 -2
- package/commands/wk-release-notes.md +0 -2
- package/commands/wk-status.md +0 -2
- package/commands/wk-verify.md +0 -2
- package/scripts/update-superpowers.sh +0 -82
- package/scripts/vendor-assets.ts +0 -37
- package/skills/wk-changelog/SKILL.md +0 -15
- package/skills/wk-commit/SKILL.md +0 -16
- package/skills/wk-docs-refresh/SKILL.md +0 -15
- package/skills/wk-handoff/SKILL.md +0 -20
- package/skills/wk-implement/SKILL.md +0 -47
- package/skills/wk-init/SKILL.md +0 -31
- package/skills/wk-issue-update/SKILL.md +0 -27
- package/skills/wk-issue-update/references/youtrack-update-style.md +0 -81
- package/skills/wk-meetings/SKILL.md +0 -17
- package/skills/wk-pr/SKILL.md +0 -27
- package/skills/wk-release-notes/SKILL.md +0 -15
- package/skills/wk-status/SKILL.md +0 -16
- package/skills/wk-verify/SKILL.md +0 -16
- package/src/core/detector.ts +0 -239
- package/src/core/flow-state.ts +0 -3308
- package/src/core/handoff-context.ts +0 -136
- package/src/core/handoff-tools.ts +0 -133
- package/src/core/menu.ts +0 -70
- package/src/core/plan-tasks.ts +0 -43
- package/src/core/reminder.ts +0 -124
- package/src/core/sdd.ts +0 -353
- package/src/state.ts +0 -22
- package/templates/superpowers-doc-contract.md +0 -75
- package/vendor/superpowers/skills/brainstorming/SKILL.md +0 -159
- package/vendor/superpowers/skills/brainstorming/scripts/frame-template.html +0 -213
- package/vendor/superpowers/skills/brainstorming/scripts/helper.js +0 -167
- package/vendor/superpowers/skills/brainstorming/scripts/server.cjs +0 -723
- package/vendor/superpowers/skills/brainstorming/scripts/start-server.sh +0 -209
- package/vendor/superpowers/skills/brainstorming/scripts/stop-server.sh +0 -120
- package/vendor/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
- package/vendor/superpowers/skills/brainstorming/visual-companion.md +0 -291
- package/vendor/superpowers/skills/dispatching-parallel-agents/SKILL.md +0 -185
- package/vendor/superpowers/skills/executing-plans/SKILL.md +0 -70
- package/vendor/superpowers/skills/finishing-a-development-branch/SKILL.md +0 -241
- package/vendor/superpowers/skills/receiving-code-review/SKILL.md +0 -213
- package/vendor/superpowers/skills/requesting-code-review/SKILL.md +0 -103
- package/vendor/superpowers/skills/requesting-code-review/code-reviewer.md +0 -172
- package/vendor/superpowers/skills/subagent-driven-development/SKILL.md +0 -428
- package/vendor/superpowers/skills/subagent-driven-development/implementer-prompt.md +0 -139
- package/vendor/superpowers/skills/subagent-driven-development/scripts/review-package +0 -44
- package/vendor/superpowers/skills/subagent-driven-development/scripts/sdd-workspace +0 -22
- package/vendor/superpowers/skills/subagent-driven-development/scripts/task-brief +0 -40
- package/vendor/superpowers/skills/subagent-driven-development/task-reviewer-prompt.md +0 -188
- package/vendor/superpowers/skills/systematic-debugging/CREATION-LOG.md +0 -119
- package/vendor/superpowers/skills/systematic-debugging/SKILL.md +0 -296
- package/vendor/superpowers/skills/systematic-debugging/condition-based-waiting-example.ts +0 -158
- package/vendor/superpowers/skills/systematic-debugging/condition-based-waiting.md +0 -115
- package/vendor/superpowers/skills/systematic-debugging/defense-in-depth.md +0 -122
- package/vendor/superpowers/skills/systematic-debugging/find-polluter.sh +0 -63
- package/vendor/superpowers/skills/systematic-debugging/root-cause-tracing.md +0 -169
- package/vendor/superpowers/skills/systematic-debugging/test-academic.md +0 -14
- package/vendor/superpowers/skills/systematic-debugging/test-pressure-1.md +0 -58
- package/vendor/superpowers/skills/systematic-debugging/test-pressure-2.md +0 -68
- package/vendor/superpowers/skills/systematic-debugging/test-pressure-3.md +0 -69
- package/vendor/superpowers/skills/test-driven-development/SKILL.md +0 -371
- package/vendor/superpowers/skills/test-driven-development/testing-anti-patterns.md +0 -299
- package/vendor/superpowers/skills/using-git-worktrees/SKILL.md +0 -202
- package/vendor/superpowers/skills/using-superpowers/SKILL.md +0 -62
- package/vendor/superpowers/skills/using-superpowers/references/antigravity-tools.md +0 -23
- package/vendor/superpowers/skills/using-superpowers/references/codex-tools.md +0 -39
- package/vendor/superpowers/skills/using-superpowers/references/pi-tools.md +0 -16
- package/vendor/superpowers/skills/verification-before-completion/SKILL.md +0 -139
- package/vendor/superpowers/skills/writing-plans/SKILL.md +0 -174
- package/vendor/superpowers/skills/writing-plans/plan-document-reviewer-prompt.md +0 -49
- package/vendor/superpowers/skills/writing-skills/SKILL.md +0 -689
- package/vendor/superpowers/skills/writing-skills/anthropic-best-practices.md +0 -1150
- package/vendor/superpowers/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -189
- package/vendor/superpowers/skills/writing-skills/graphviz-conventions.dot +0 -172
- package/vendor/superpowers/skills/writing-skills/persuasion-principles.md +0 -187
- package/vendor/superpowers/skills/writing-skills/render-graphs.js +0 -168
- package/vendor/superpowers/skills/writing-skills/testing-skills-with-subagents.md +0 -384
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @brainervirus/workit-core
|
|
2
2
|
|
|
3
|
+
[](https://github.com/BrainerVirus/workit/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@brainervirus/workit-core)
|
|
5
|
+
[](../../LICENSE)
|
|
6
|
+
|
|
3
7
|
Shared core for the workit plugins — the single source of workflow logic that the OpenCode, Cursor, and CLI packages adapt to their native surfaces.
|
|
4
8
|
|
|
5
9
|
## When you need it directly
|
|
@@ -17,11 +21,9 @@ You consume `@brainervirus/workit-core` directly only when:
|
|
|
17
21
|
| --- | --- |
|
|
18
22
|
| `src/core/` | Shared TS logic (setup, registration, doctor, logger, branch, PR, changelog, YouTrack, presentation, docs, SDD, support matrix). |
|
|
19
23
|
| `src/core.ts` | Package entry; adapters import `@brainervirus/workit-core/src/*`. |
|
|
20
|
-
| `skills/` |
|
|
21
|
-
| `commands/` | 12 OpenCode `wk-*` command prompts. |
|
|
24
|
+
| `skills/` | 14 `workit-*` method skills (single source; adapters ship byte-identical copies). |
|
|
22
25
|
| `scripts/` | Shared shell installers/launchers and the release-time workspace-dep rewrite. |
|
|
23
26
|
| `templates/` | Execution and Superpowers contract templates. |
|
|
24
|
-
| `vendor/superpowers/skills/` | 14 vendored Superpowers skills. |
|
|
25
27
|
|
|
26
28
|
Adapters map host-native surfaces to `src/core/`; they never re-implement core logic. Keep shared behavior here and host-specific presentation in each adapter.
|
|
27
29
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit — workflow rails for agentic coding: specs, plans, YouTrack, CI-gated commits (shared core)",
|
|
6
6
|
"keywords": [
|
|
@@ -23,8 +23,6 @@
|
|
|
23
23
|
"src/",
|
|
24
24
|
"scripts/",
|
|
25
25
|
"skills/",
|
|
26
|
-
"commands/",
|
|
27
|
-
"vendor/superpowers/skills/",
|
|
28
26
|
"templates/",
|
|
29
27
|
"README.md",
|
|
30
28
|
"LICENSE",
|
|
@@ -42,5 +40,12 @@
|
|
|
42
40
|
},
|
|
43
41
|
"scripts": {
|
|
44
42
|
"typecheck": "tsc --noEmit"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@openclaw/fs-safe": "0.8.1",
|
|
46
|
+
"zod": "4.5.4"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=24"
|
|
45
50
|
}
|
|
46
51
|
}
|
|
@@ -7,9 +7,12 @@ import { resolve } from "node:path";
|
|
|
7
7
|
|
|
8
8
|
export const RELEASE_PACKAGES = [
|
|
9
9
|
"workit-core",
|
|
10
|
+
"workit-mcp",
|
|
11
|
+
"workit-cli",
|
|
10
12
|
"workit-opencode",
|
|
11
13
|
"workit-cursor",
|
|
12
|
-
"workit-
|
|
14
|
+
"workit-codex",
|
|
15
|
+
"workit-pi",
|
|
13
16
|
] as const;
|
|
14
17
|
|
|
15
18
|
const g = (root: string, args: string[]): string =>
|
package/scripts/doctor-check.ts
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
// failures are left to the post-install gate.
|
|
11
11
|
import { runDoctor } from "../src/core/doctor";
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const hostArg = process.argv[2];
|
|
14
|
+
const host =
|
|
15
|
+
hostArg === "cursor" ? "cursor" : hostArg === "cli" ? "cli" : "opencode";
|
|
14
16
|
const staleOnly = process.argv.includes("--stale");
|
|
15
17
|
const report = runDoctor({ host, installer: true });
|
|
16
18
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install / refresh the Codex workit plugin from the local checkout or share clone.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
|
6
|
+
ROOT="$(CDPATH= cd -- "$SCRIPT_DIR/../../.." && pwd)"
|
|
7
|
+
SHARE="${HOME}/.local/share/workit"
|
|
8
|
+
DEV="${WORKFLOW_TOOLKIT_DEV:-$ROOT}"
|
|
9
|
+
PLUGIN_DIR="${HOME}/.codex/plugins/workit"
|
|
10
|
+
|
|
11
|
+
chmod +x "$ROOT/packages/workit-core/scripts/sync-runtime.sh"
|
|
12
|
+
WORKFLOW_TOOLKIT_DEV="$ROOT" "$ROOT/packages/workit-core/scripts/sync-runtime.sh"
|
|
13
|
+
|
|
14
|
+
SOURCE="$DEV"
|
|
15
|
+
if [ ! -d "$SOURCE/packages/workit-codex/.codex-plugin" ] && [ -d "$SHARE/packages/workit-codex/.codex-plugin" ]; then
|
|
16
|
+
SOURCE="$SHARE"
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
mkdir -p "$PLUGIN_DIR"
|
|
20
|
+
rsync -a --delete "$SOURCE/packages/workit-codex/" "$PLUGIN_DIR/"
|
|
21
|
+
|
|
22
|
+
if ! bun "$ROOT/packages/workit-core/scripts/doctor-check.ts" cli; then
|
|
23
|
+
echo "FATAL: post-install doctor found problems after Codex plugin sync" >&2
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
echo "Codex plugin installed at $PLUGIN_DIR"
|
|
28
|
+
echo "Complete an explicit v1 cutover before treating this as a generation switch."
|
|
@@ -140,5 +140,6 @@ if [ -d "$LEGACY_PLUGIN_DIR" ] && [ "$LEGACY_PLUGIN_DIR" != "$PLUGIN_DIR" ]; the
|
|
|
140
140
|
fi
|
|
141
141
|
|
|
142
142
|
echo "Cursor plugin installed + auto-sync enabled (sessionStart)."
|
|
143
|
+
echo "Generation switches require an explicit cutover preview/apply — install alone is not a cutover."
|
|
143
144
|
echo "Share: $SHARE"
|
|
144
145
|
ls "$HOME/.cursor/plugins/local/workit/skills" | grep '^wk-' || true
|
|
@@ -77,3 +77,4 @@ if ! bun "$ROOT/packages/workit-core/scripts/doctor-check.ts" opencode; then
|
|
|
77
77
|
fi
|
|
78
78
|
|
|
79
79
|
echo "OpenCode install done. Fully quit all opencode processes, then restart."
|
|
80
|
+
echo "Generation switches require an explicit cutover preview/apply — install alone is not a cutover."
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Register the Pi workit extension and skills from the local checkout or share clone.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
|
6
|
+
ROOT="$(CDPATH= cd -- "$SCRIPT_DIR/../../.." && pwd)"
|
|
7
|
+
SHARE="${HOME}/.local/share/workit"
|
|
8
|
+
DEV="${WORKFLOW_TOOLKIT_DEV:-$ROOT}"
|
|
9
|
+
PI_CONFIG="${HOME}/.pi/config.json"
|
|
10
|
+
|
|
11
|
+
chmod +x "$ROOT/packages/workit-core/scripts/sync-runtime.sh"
|
|
12
|
+
WORKFLOW_TOOLKIT_DEV="$ROOT" "$ROOT/packages/workit-core/scripts/sync-runtime.sh"
|
|
13
|
+
|
|
14
|
+
SOURCE="$DEV"
|
|
15
|
+
if [ ! -f "$SOURCE/packages/workit-pi/dist/workit.js" ] && [ -f "$SHARE/packages/workit-pi/dist/workit.js" ]; then
|
|
16
|
+
SOURCE="$SHARE"
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# pi 0.85.1 loads extensions from the settings `packages` list, not from
|
|
20
|
+
# ~/.pi/config.json below. Register the absolute package dir there (honoring
|
|
21
|
+
# PI_CODING_AGENT_DIR) so `pi list` picks it up; the config.json write stays
|
|
22
|
+
# because uninstall/cutover/detection still key pi state off it — migrating
|
|
23
|
+
# those three off config.json is a separate contract change.
|
|
24
|
+
AGENT_DIR="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
|
|
25
|
+
SETTINGS="$AGENT_DIR/settings.json"
|
|
26
|
+
mkdir -p "$AGENT_DIR"
|
|
27
|
+
PI_SETTINGS="$SETTINGS" EXT="$SOURCE/packages/workit-pi" bun -e '
|
|
28
|
+
import fs from "node:fs";
|
|
29
|
+
const path = process.env.PI_SETTINGS!;
|
|
30
|
+
const ext = process.env.EXT!;
|
|
31
|
+
const current = fs.existsSync(path) ? JSON.parse(fs.readFileSync(path, "utf8")) : {};
|
|
32
|
+
const packages = Array.isArray(current.packages) ? [...current.packages] : [];
|
|
33
|
+
const next = {
|
|
34
|
+
...current,
|
|
35
|
+
packages: [...packages.filter((p) => !String(p).includes("workit")), ext],
|
|
36
|
+
};
|
|
37
|
+
fs.writeFileSync(path, JSON.stringify(next, null, 2) + "\n");
|
|
38
|
+
console.log("Registered Pi package:", ext);
|
|
39
|
+
'
|
|
40
|
+
|
|
41
|
+
mkdir -p "$(dirname "$PI_CONFIG")"
|
|
42
|
+
PI_CONFIG="$PI_CONFIG" EXT="$SOURCE/packages/workit-pi/dist/workit.js" SKILLS="$SOURCE/packages/workit-pi/skills" bun -e '
|
|
43
|
+
import fs from "node:fs";
|
|
44
|
+
const path = process.env.PI_CONFIG!;
|
|
45
|
+
const current = fs.existsSync(path) ? JSON.parse(fs.readFileSync(path, "utf8")) : {};
|
|
46
|
+
const next = {
|
|
47
|
+
...current,
|
|
48
|
+
extensions: [process.env.EXT!],
|
|
49
|
+
skills: [process.env.SKILLS!],
|
|
50
|
+
};
|
|
51
|
+
fs.writeFileSync(path, JSON.stringify(next, null, 2) + "\n");
|
|
52
|
+
console.log("Registered Pi extension:", process.env.EXT);
|
|
53
|
+
'
|
|
54
|
+
|
|
55
|
+
echo "Pi package registered in $SETTINGS (and legacy $PI_CONFIG)"
|
|
56
|
+
echo "Complete an explicit v1 cutover before treating this as a generation switch."
|
|
@@ -9,12 +9,23 @@
|
|
|
9
9
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { resolve } from "node:path";
|
|
11
11
|
|
|
12
|
-
const root = process.argv[2]
|
|
12
|
+
const root = process.argv[2]
|
|
13
|
+
? resolve(process.argv[2])
|
|
14
|
+
: resolve(import.meta.dir, "..", "..", "..");
|
|
13
15
|
const core = JSON.parse(readFileSync(resolve(root, "packages/workit-core/package.json"), "utf8"));
|
|
14
16
|
if (!core.version || typeof core.version !== "string") {
|
|
15
|
-
throw new Error(
|
|
17
|
+
throw new Error(
|
|
18
|
+
`workit-core version missing in ${resolve(root, "packages/workit-core/package.json")}`,
|
|
19
|
+
);
|
|
16
20
|
}
|
|
17
|
-
for (const pkg of [
|
|
21
|
+
for (const pkg of [
|
|
22
|
+
"workit-mcp",
|
|
23
|
+
"workit-cli",
|
|
24
|
+
"workit-opencode",
|
|
25
|
+
"workit-cursor",
|
|
26
|
+
"workit-codex",
|
|
27
|
+
"workit-pi",
|
|
28
|
+
]) {
|
|
18
29
|
const file = resolve(root, `packages/${pkg}/package.json`);
|
|
19
30
|
const data = JSON.parse(readFileSync(file, "utf8"));
|
|
20
31
|
const deps = data.dependencies;
|
|
@@ -28,10 +39,16 @@ for (const pkg of ["workit-opencode", "workit-cursor", "workit-cli"]) {
|
|
|
28
39
|
// plugin manifest is versioned at release time.
|
|
29
40
|
for (const file of [
|
|
30
41
|
resolve(root, "packages/workit-cursor/.cursor-plugin/plugin.json"),
|
|
42
|
+
resolve(root, "packages/workit-codex/.codex-plugin/plugin.json"),
|
|
31
43
|
]) {
|
|
32
44
|
const data = JSON.parse(readFileSync(file, "utf8"));
|
|
33
45
|
data.version = core.version;
|
|
34
|
-
if (data.homepage)
|
|
35
|
-
|
|
46
|
+
if (data.homepage)
|
|
47
|
+
data.homepage = data.homepage.replace("BrainerVirus/workflow-toolkit", "BrainerVirus/workit");
|
|
48
|
+
if (data.repository)
|
|
49
|
+
data.repository = data.repository.replace(
|
|
50
|
+
"BrainerVirus/workflow-toolkit",
|
|
51
|
+
"BrainerVirus/workit",
|
|
52
|
+
);
|
|
36
53
|
writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`);
|
|
37
54
|
}
|
|
@@ -9,14 +9,18 @@ import { execFileSync } from "node:child_process";
|
|
|
9
9
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { resolve } from "node:path";
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const SYNC_MANIFEST_PATHS = [
|
|
13
13
|
"package.json",
|
|
14
14
|
"packages/workit-core/package.json",
|
|
15
|
+
"packages/workit-mcp/package.json",
|
|
16
|
+
"packages/workit-cli/package.json",
|
|
15
17
|
"packages/workit-opencode/package.json",
|
|
16
18
|
"packages/workit-cursor/package.json",
|
|
17
|
-
"packages/workit-
|
|
19
|
+
"packages/workit-codex/package.json",
|
|
20
|
+
"packages/workit-pi/package.json",
|
|
18
21
|
// Kept in lockstep with packages/workit-core/package.json by contract test.
|
|
19
22
|
"packages/workit-cursor/.cursor-plugin/plugin.json",
|
|
23
|
+
"packages/workit-codex/.codex-plugin/plugin.json",
|
|
20
24
|
];
|
|
21
25
|
|
|
22
26
|
export type ManifestSyncResult = { version: string; changed: string[] };
|
|
@@ -27,8 +31,7 @@ export type ManifestSyncResult = { version: string; changed: string[] };
|
|
|
27
31
|
* are left byte-untouched and omitted from `changed`. Throws on a value that
|
|
28
32
|
* is not a plain release version (`latest`, branches, ranges).
|
|
29
33
|
*/
|
|
30
|
-
export function syncManifests(root: string, tagOrVersion: string): ManifestSyncResult {
|
|
31
|
-
const match = /^v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/.exec(tagOrVersion.trim());
|
|
34
|
+
export function syncManifests(root: string, tagOrVersion: string): ManifestSyncResult { const match = /^v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/.exec(tagOrVersion.trim());
|
|
32
35
|
if (!match) throw new Error(`invalid version tag: ${JSON.stringify(tagOrVersion)}`);
|
|
33
36
|
const version = match[1];
|
|
34
37
|
const changed: string[] = [];
|
|
@@ -44,7 +47,7 @@ export function syncManifests(root: string, tagOrVersion: string): ManifestSyncR
|
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
/** The newest `v*` tag by descending semver refname order (empty repo throws). */
|
|
47
|
-
|
|
50
|
+
function latestReleaseTag(cwd?: string): string {
|
|
48
51
|
const out = execFileSync("git", ["tag", "--list", "v*", "--sort=-v:refname"], {
|
|
49
52
|
cwd,
|
|
50
53
|
encoding: "utf8",
|
package/scripts/sync-runtime.sh
CHANGED
|
@@ -69,7 +69,7 @@ else
|
|
|
69
69
|
exit 1
|
|
70
70
|
fi
|
|
71
71
|
|
|
72
|
-
for DEP in @brainervirus/workit-core @modelcontextprotocol/sdk zod; do
|
|
72
|
+
for DEP in @brainervirus/workit-core @brainervirus/workit-mcp @modelcontextprotocol/sdk zod; do
|
|
73
73
|
if [ ! -e "$SRC/node_modules/$DEP" ]; then
|
|
74
74
|
if [ ! -f "$SRC/bun.lock" ]; then
|
|
75
75
|
echo "FATAL: dependency install requires $SRC/bun.lock" >&2
|
|
@@ -83,11 +83,15 @@ for DEP in @brainervirus/workit-core @modelcontextprotocol/sdk zod; do
|
|
|
83
83
|
fi
|
|
84
84
|
done
|
|
85
85
|
|
|
86
|
+
if ! (cd "$SRC" && PATH="$(dirname "$BUN_BIN"):$PATH" "$BUN_BIN" "$SRC/packages/workit-mcp/scripts/build.ts"); then
|
|
87
|
+
echo "FATAL: shared MCP build failed in $SRC" >&2
|
|
88
|
+
exit 1
|
|
89
|
+
fi
|
|
86
90
|
if ! (cd "$SRC" && PATH="$(dirname "$BUN_BIN"):$PATH" "$BUN_BIN" "$CURSOR_SRC/scripts/build.ts"); then
|
|
87
91
|
echo "FATAL: Cursor adapter build failed in $CURSOR_SRC" >&2
|
|
88
92
|
exit 1
|
|
89
93
|
fi
|
|
90
|
-
for ENTRY in mcp-server.js cursor-session-start.js; do
|
|
94
|
+
for ENTRY in mcp-server.js cursor-session-start.js workit-hook.js; do
|
|
91
95
|
DIST_ENTRY="$CURSOR_SRC/dist/$ENTRY"
|
|
92
96
|
if [ ! -f "$DIST_ENTRY" ] || [ ! -s "$DIST_ENTRY" ] || [ "$(IFS= read -r LINE <"$DIST_ENTRY"; printf '%s' "$LINE")" != '#!/usr/bin/env node' ]; then
|
|
93
97
|
echo "FATAL: Cursor adapter invalid dist entry: $DIST_ENTRY" >&2
|
|
@@ -153,7 +157,7 @@ import fs from "node:fs";
|
|
|
153
157
|
const path = process.env.PKG_PATH!;
|
|
154
158
|
const data = JSON.parse(fs.readFileSync(path, "utf8"));
|
|
155
159
|
data.dependencies = data.dependencies ?? {};
|
|
156
|
-
data.dependencies["@opencode-ai/plugin"] = data.dependencies["@opencode-ai/plugin"] ?? "1.
|
|
160
|
+
data.dependencies["@opencode-ai/plugin"] = data.dependencies["@opencode-ai/plugin"] ?? "1.18.30";
|
|
157
161
|
fs.writeFileSync(path, JSON.stringify(data, null, 2) + "\n");
|
|
158
162
|
' || true
|
|
159
163
|
fi
|
|
@@ -20,20 +20,12 @@
|
|
|
20
20
|
// `continual-learning` and `third_party/gmail` with no `../` prefix, even though
|
|
21
21
|
// `.cursor-plugin/` is a sibling of those directories. Therefore
|
|
22
22
|
// `source: "packages/workit-cursor"` is correct and kept verbatim.
|
|
23
|
-
import {
|
|
24
|
-
existsSync,
|
|
25
|
-
mkdtempSync,
|
|
26
|
-
readFileSync,
|
|
27
|
-
readdirSync,
|
|
28
|
-
rmSync,
|
|
29
|
-
} from "node:fs";
|
|
30
|
-
import os from "node:os";
|
|
23
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
31
24
|
import path from "node:path";
|
|
32
25
|
import { fileURLToPath } from "node:url";
|
|
33
26
|
import Ajv from "ajv";
|
|
34
27
|
import addFormats from "ajv-formats";
|
|
35
|
-
import {
|
|
36
|
-
import { copySanitizedVendor } from "./vendor-assets";
|
|
28
|
+
import { WORKIT_METHOD_SKILLS, validateSkillManifests } from "../src/core/skill-manifests";
|
|
37
29
|
|
|
38
30
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
39
31
|
const repoRoot = path.resolve(scriptDir, "..", "..", "..");
|
|
@@ -54,30 +46,6 @@ const frontmatterKeys = (file: string): Set<string> => {
|
|
|
54
46
|
return keys;
|
|
55
47
|
};
|
|
56
48
|
|
|
57
|
-
const treesEqual = (a: string, b: string): string[] => {
|
|
58
|
-
const diffs: string[] = [];
|
|
59
|
-
// Thread the tree root so each file is keyed by its ROOT-relative path —
|
|
60
|
-
// otherwise every `SKILL.md` collapses to one map entry and only the
|
|
61
|
-
// last-read (readdir-order-dependent) skill is actually compared.
|
|
62
|
-
const walk = (rootDir: string, dir: string, into: Map<string, string>): void => {
|
|
63
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
64
|
-
const p = path.join(dir, entry.name);
|
|
65
|
-
if (entry.isDirectory()) walk(rootDir, p, into);
|
|
66
|
-
else into.set(path.relative(rootDir, p).split(path.sep).join("/"), readFileSync(p, "utf8"));
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
const left = new Map<string, string>();
|
|
70
|
-
const right = new Map<string, string>();
|
|
71
|
-
walk(a, a, left);
|
|
72
|
-
walk(b, b, right);
|
|
73
|
-
for (const [rel, content] of left) {
|
|
74
|
-
if (!right.has(rel)) diffs.push(`missing in rebuilt: ${rel}`);
|
|
75
|
-
else if (right.get(rel) !== content) diffs.push(`content drift: ${rel}`);
|
|
76
|
-
}
|
|
77
|
-
for (const rel of right.keys()) if (!left.has(rel)) diffs.push(`extra in rebuilt: ${rel}`);
|
|
78
|
-
return diffs;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
49
|
export const validateMarketplace = (rootArg: string): string[] => {
|
|
82
50
|
const errors: string[] = [];
|
|
83
51
|
|
|
@@ -90,9 +58,7 @@ export const validateMarketplace = (rootArg: string): string[] => {
|
|
|
90
58
|
const validate = new Ajv({ strict: true, allErrors: true });
|
|
91
59
|
addFormats(validate);
|
|
92
60
|
const schemaDir = path.join(rootArg, "test/fixtures/cursor-schemas");
|
|
93
|
-
const pluginSchema = JSON.parse(
|
|
94
|
-
readFileSync(path.join(schemaDir, "plugin.schema.json"), "utf8"),
|
|
95
|
-
);
|
|
61
|
+
const pluginSchema = JSON.parse(readFileSync(path.join(schemaDir, "plugin.schema.json"), "utf8"));
|
|
96
62
|
const marketSchema = JSON.parse(
|
|
97
63
|
readFileSync(path.join(schemaDir, "marketplace.schema.json"), "utf8"),
|
|
98
64
|
);
|
|
@@ -106,12 +72,13 @@ export const validateMarketplace = (rootArg: string): string[] => {
|
|
|
106
72
|
// 2. Resolve each plugin source relative to the repo root; name must match.
|
|
107
73
|
for (const entry of (market.plugins ?? []) as { name: string; source: string }[]) {
|
|
108
74
|
const pluginDir = path.join(rootArg, entry.source);
|
|
109
|
-
const manifestRel = path.join(entry.source, ".cursor-plugin/plugin.json");
|
|
110
75
|
if (!existsSync(path.join(pluginDir, ".cursor-plugin/plugin.json"))) {
|
|
111
76
|
errors.push(`plugin ${entry.name}: source ${entry.source} has no .cursor-plugin/plugin.json`);
|
|
112
77
|
continue;
|
|
113
78
|
}
|
|
114
|
-
const plugin = JSON.parse(
|
|
79
|
+
const plugin = JSON.parse(
|
|
80
|
+
readFileSync(path.join(pluginDir, ".cursor-plugin/plugin.json"), "utf8"),
|
|
81
|
+
);
|
|
115
82
|
if (!validate.validate(pluginSchema, plugin)) {
|
|
116
83
|
errors.push(
|
|
117
84
|
`plugin ${entry.name} plugin.json invalid: ${(validate.errors ?? []).map((e) => e.message).join("; ")}`,
|
|
@@ -155,23 +122,25 @@ export const validateMarketplace = (rootArg: string): string[] => {
|
|
|
155
122
|
|
|
156
123
|
// 5. Skills and rules carry valid frontmatter.
|
|
157
124
|
const skillRoots: [string, readonly string[]][] = [
|
|
158
|
-
[path.join(pluginDir, "skills"),
|
|
159
|
-
[path.join(pluginDir, "vendor/superpowers/skills"), CANONICAL_SKILLS.superpowers],
|
|
125
|
+
[path.join(pluginDir, "skills"), WORKIT_METHOD_SKILLS],
|
|
160
126
|
];
|
|
161
127
|
for (const [dir, expected] of skillRoots) {
|
|
162
128
|
const mismatch = validateSkillManifests(dir, expected, "skills");
|
|
163
129
|
if (mismatch) errors.push(`plugin ${entry.name}: ${mismatch}`);
|
|
164
130
|
for (const skill of expected) {
|
|
165
131
|
const keys = frontmatterKeys(path.join(dir, skill, "SKILL.md"));
|
|
166
|
-
if (!keys.has("name"))
|
|
167
|
-
|
|
132
|
+
if (!keys.has("name"))
|
|
133
|
+
errors.push(`plugin ${entry.name}: ${skill}/SKILL.md missing frontmatter name`);
|
|
134
|
+
if (!keys.has("description"))
|
|
135
|
+
errors.push(`plugin ${entry.name}: ${skill}/SKILL.md missing frontmatter description`);
|
|
168
136
|
}
|
|
169
137
|
}
|
|
170
138
|
const rulesDir = path.join(pluginDir, "rules");
|
|
171
139
|
if (existsSync(rulesDir)) {
|
|
172
140
|
for (const rule of readdirSync(rulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
173
141
|
const keys = frontmatterKeys(path.join(rulesDir, rule));
|
|
174
|
-
if (!keys.has("description"))
|
|
142
|
+
if (!keys.has("description"))
|
|
143
|
+
errors.push(`plugin ${entry.name}: ${rule} missing frontmatter description`);
|
|
175
144
|
}
|
|
176
145
|
}
|
|
177
146
|
|
|
@@ -184,23 +153,6 @@ export const validateMarketplace = (rootArg: string): string[] => {
|
|
|
184
153
|
errors.push(`plugin ${entry.name}: ${rel} references ignored runtime output`);
|
|
185
154
|
}
|
|
186
155
|
}
|
|
187
|
-
|
|
188
|
-
// 7. Rebuilding the sanitized vendor tree yields no diff.
|
|
189
|
-
const sourceVendor = path.join(rootArg, "packages/workit-core/vendor/superpowers/skills");
|
|
190
|
-
const trackedVendor = path.join(pluginDir, "vendor/superpowers/skills");
|
|
191
|
-
if (existsSync(sourceVendor) && existsSync(trackedVendor)) {
|
|
192
|
-
const rebuilt = mkdtempSync(path.join(os.tmpdir(), "wk-vendor-rebuild-"));
|
|
193
|
-
try {
|
|
194
|
-
copySanitizedVendor(sourceVendor, rebuilt);
|
|
195
|
-
for (const diff of treesEqual(rebuilt, trackedVendor)) {
|
|
196
|
-
errors.push(`plugin ${entry.name}: vendor drift: ${diff}`);
|
|
197
|
-
}
|
|
198
|
-
} finally {
|
|
199
|
-
rmSync(rebuilt, { recursive: true, force: true });
|
|
200
|
-
}
|
|
201
|
-
} else {
|
|
202
|
-
errors.push(`plugin ${entry.name}: missing vendor source or tracked tree`);
|
|
203
|
-
}
|
|
204
156
|
}
|
|
205
157
|
|
|
206
158
|
return errors;
|
|
@@ -210,7 +162,9 @@ if (import.meta.main) {
|
|
|
210
162
|
const errors = validateMarketplace(root);
|
|
211
163
|
if (errors.length > 0) {
|
|
212
164
|
for (const e of errors) process.stderr.write(`${e}\n`);
|
|
213
|
-
process.stderr.write(
|
|
165
|
+
process.stderr.write(
|
|
166
|
+
`marketplace validation FAILED (${errors.length} error${errors.length === 1 ? "" : "s"})\n`,
|
|
167
|
+
);
|
|
214
168
|
process.exit(1);
|
|
215
169
|
}
|
|
216
170
|
console.log(`marketplace validation passed (root: ${root})`);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workit-babysit
|
|
3
|
+
description: Use when a PR needs driving to merge-ready, CI is red, or the user says babysit
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Babysit a PR to merge-ready
|
|
7
|
+
|
|
8
|
+
Babysit starts automatically on PR creation unless declined (`--no-babysit`).
|
|
9
|
+
One babysitter per PR; never mutate PR topology (no rebase strategy changes,
|
|
10
|
+
no force-push).
|
|
11
|
+
|
|
12
|
+
## Before method work
|
|
13
|
+
|
|
14
|
+
If there is no active or paused task, run shared `task.start` then `policy.assess`
|
|
15
|
+
before relying on selected policy rules or other product mutations. Assessment
|
|
16
|
+
selects requirements; do not wait for a rule that can only exist after assess.
|
|
17
|
+
|
|
18
|
+
## Method
|
|
19
|
+
|
|
20
|
+
1. Declare mode: drive (fix + merge), watch (report only), or threads-only.
|
|
21
|
+
2. Work the merge frontier in order: conflicts → review threads → CI.
|
|
22
|
+
3. Classify CI before retry: flake (rerun once) vs stale base (verify with
|
|
23
|
+
`git merge-base --is-ancestor` before updating) vs real failure (fix).
|
|
24
|
+
4. Triage bot findings skeptically: reproduce or quote code before acting;
|
|
25
|
+
invalid bots get a reasoned dismissal, never silent ignore.
|
|
26
|
+
5. Batch fixes into one push wave; re-verify green after every push.
|
|
27
|
+
6. Merge only when green and approved, honoring `pr` settings (squash +
|
|
28
|
+
delete branch). Stop at the human's line: never merge on explicit hold.
|
|
29
|
+
|
|
30
|
+
## Completion
|
|
31
|
+
|
|
32
|
+
PR merged per settings, or a status brief (frontier state, next action) when
|
|
33
|
+
blocked on the human. Record evidence for fixes, findings for blockers.
|
|
@@ -0,0 +1,53 @@
|
|
|
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
|
+
## Before method work
|
|
12
|
+
|
|
13
|
+
If there is no active or paused task, run shared `task.start` then `policy.assess`
|
|
14
|
+
before relying on selected policy rules or other product mutations. Assessment
|
|
15
|
+
selects requirements; do not wait for a rule that can only exist after assess.
|
|
16
|
+
|
|
17
|
+
## Method
|
|
18
|
+
|
|
19
|
+
1. Inspect the task requirement, current candidate, intended behavior, and real
|
|
20
|
+
verification entry point with shared `task`, `policy`, and `evidence` operations.
|
|
21
|
+
2. State one behavior and its observable result. Choose the narrowest stable
|
|
22
|
+
boundary a caller or user depends on; avoid private helpers and incidental
|
|
23
|
+
representations.
|
|
24
|
+
3. Write one vertical RED slice that fails for the missing behavior, run it, and
|
|
25
|
+
preserve the actual failure as evidence. Implement the smallest change, then
|
|
26
|
+
run the same slice GREEN and record its result. Close enforces the order: a
|
|
27
|
+
testing requirement with GREEN but no preceding RED evidence stays unsatisfied.
|
|
28
|
+
4. Add only another slice for a distinct behavior or risk. Reconcile stale
|
|
29
|
+
evidence if the candidate changes.
|
|
30
|
+
|
|
31
|
+
## Reject noisy tests
|
|
32
|
+
|
|
33
|
+
- A dependency/version-pin assertion is not behavioral evidence.
|
|
34
|
+
- A test that mirrors branches, private calls, or exact implementation structure
|
|
35
|
+
is coupled to internals; replace it with the public effect.
|
|
36
|
+
- Duplicate assertions and tests that add no distinct failure signal are noise;
|
|
37
|
+
delete them.
|
|
38
|
+
- Do not claim a passing test satisfies a different requirement.
|
|
39
|
+
- Banned: tautologies (asserts what the code says, not what it must do),
|
|
40
|
+
ghost loops (assert inside a possibly-empty loop), smoke-only renders,
|
|
41
|
+
type-only or CSS-class coupling. If the test still passes when every
|
|
42
|
+
imported function returns undefined, rewrite the assertion or delete it.
|
|
43
|
+
|
|
44
|
+
Use shared `evidence` operations for RED/GREEN results. Do not add a second
|
|
45
|
+
lifecycle, approval chain, or test workflow outside the current task state.
|
|
46
|
+
|
|
47
|
+
## Common mistakes
|
|
48
|
+
|
|
49
|
+
| Mistake | Correction |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| "The pin changed, so assert the new string" | Exercise the affected consumer behavior. |
|
|
52
|
+
| "The code is obvious" | A small vertical slice still proves the contract. |
|
|
53
|
+
| Keeping a passing test after the boundary moved | Mark it stale and retest the current candidate. |
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
## Before method work
|
|
12
|
+
|
|
13
|
+
If there is no active or paused task, run shared `task.start` then `policy.assess`
|
|
14
|
+
before relying on selected policy rules or other product mutations. Assessment
|
|
15
|
+
selects requirements; do not wait for a rule that can only exist after assess.
|
|
16
|
+
|
|
17
|
+
## Method
|
|
18
|
+
|
|
19
|
+
1. List what the change touches: callers, shared state, contracts, config,
|
|
20
|
+
migrations. Grep every caller of each touched function.
|
|
21
|
+
2. For each: state the one fact it is safe because of (type boundary,
|
|
22
|
+
existing test, unreachable path) plus how to run the proof.
|
|
23
|
+
3. Run the proofs. Unproven claims stay labeled UNPROVEN in findings —
|
|
24
|
+
never silently treated as safe.
|
|
25
|
+
4. Fix at the shared root (one guard where all callers route through),
|
|
26
|
+
not per caller.
|
|
27
|
+
|
|
28
|
+
## Completion
|
|
29
|
+
|
|
30
|
+
Blast-radius note in evidence or review: each risk with fact + proof
|
|
31
|
+
command, or an UNPROVEN finding for what could not be proven.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workit-challenge
|
|
3
|
+
description: Use when a proposal is ambiguous, consequential, disputed, or may hide assumptions, coupling, failure modes, or a simpler solution
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Challenge a proposal with a grounded grill
|
|
7
|
+
|
|
8
|
+
Treat the proposal as a hypothesis. Facts are the agent's job; decisions are
|
|
9
|
+
the user's. Use this method when assessment selects the `challenge` or
|
|
10
|
+
`decisions` dimension.
|
|
11
|
+
|
|
12
|
+
## Before method work
|
|
13
|
+
|
|
14
|
+
If there is no active or paused task, run shared `task.start` then `policy.assess`
|
|
15
|
+
before relying on selected policy rules or other product mutations. Assessment
|
|
16
|
+
selects requirements; do not wait for a rule that can only exist after assess.
|
|
17
|
+
|
|
18
|
+
## Method
|
|
19
|
+
|
|
20
|
+
1. Ground first: inspect the task, policy, candidate, evidence, decisions, and
|
|
21
|
+
the repo docs that exist. Never ask what code or docs can answer.
|
|
22
|
+
2. Diverge once, bounded: when the approach is unknown, offer 3-5 candidate
|
|
23
|
+
directions with evidence and tradeoffs in one advisory burst, without
|
|
24
|
+
critique. Record the burst as artifact evidence when it matters; the user
|
|
25
|
+
steers or mixes.
|
|
26
|
+
3. Grill one question at a time: each question carries your recommended answer,
|
|
27
|
+
the facts behind it, and at least one rejected alternative. Resolve
|
|
28
|
+
dependency order and recompute after every answer — a wall of questions is
|
|
29
|
+
not an interview.
|
|
30
|
+
4. Funnel every resolution: the moment a consequential choice settles, bind it
|
|
31
|
+
with a receipt-shaped question (header `Workit decision: <purpose>`, exactly
|
|
32
|
+
`approved`/`rejected`, the approved description carrying the exact content).
|
|
33
|
+
If the user already stated the choice in conversation and no receipt can be
|
|
34
|
+
minted, record it in task progress and reassess so the settled requirement
|
|
35
|
+
retires — never re-ask to mint a receipt, and never leave it for close to
|
|
36
|
+
demand.
|
|
37
|
+
5. Durability: write or refresh the spec under `docs/<slug>/` only when the
|
|
38
|
+
`durable-spec` requirement fires. No glossary, no second lifecycle.
|
|
39
|
+
6. Counter-case: each material recommendation carries one strongest
|
|
40
|
+
counter-case (hidden assumption, failure mode, coupling, simpler
|
|
41
|
+
alternative). Stop when a counter-case adds no new constraint.
|
|
42
|
+
|
|
43
|
+
Stop at an empty frontier or three rounds; the cap is the bound. Say directly
|
|
44
|
+
when the proposal is weak, overcomplicated, or solves the wrong problem.
|
|
45
|
+
|
|
46
|
+
## Guardrails
|
|
47
|
+
|
|
48
|
+
- Do not create a universal spec, plan, approval chain, or second lifecycle.
|
|
49
|
+
- Unknowns that affect the dependent action remain unresolved until evidence or a
|
|
50
|
+
user decision closes them.
|
|
51
|
+
- Use the shared operations for state and provenance; never write task metadata
|
|
52
|
+
directly.
|
|
53
|
+
- An in-session counter-case is never fresh-context review; claim only what it is.
|
|
54
|
+
|
|
55
|
+
## Common mistakes
|
|
56
|
+
|
|
57
|
+
| Mistake | Correction |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| Asking what the code or docs already answer | Ground first; the user owns choices, not lookups. |
|
|
60
|
+
| Listing every hypothetical objection | One strongest counter-case, then stop. |
|
|
61
|
+
| Asking "what do you think?" without a recommendation | Recommend an option and explain the tradeoff. |
|
|
62
|
+
| Leaving a settled choice for close to confirm | Funnel it when it resolves; progress plus reassessment if no receipt. |
|