devrites 3.0.4 → 3.0.5
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/CHANGELOG.md +25 -1
- package/README.md +55 -43
- package/SECURITY.md +72 -58
- package/docs/agents/issue-driven-rites.md +5 -5
- package/docs/architecture.md +61 -45
- package/docs/capability-surface-selection.md +3 -1
- package/docs/cli.md +9 -2
- package/docs/command-map.md +6 -2
- package/docs/engine/agent-contract.md +5 -3
- package/docs/engine/commands.md +27 -14
- package/docs/engine/state-schema.md +8 -4
- package/docs/engine/workspace-schema.md +5 -4
- package/docs/flow.md +18 -7
- package/docs/porting-to-a-new-harness.md +6 -2
- package/docs/release.md +10 -9
- package/docs/skills.md +1 -1
- package/docs/templates/CRITIC_REVIEW_PACKET_TEMPLATE.md +35 -0
- package/docs/usage.md +26 -16
- package/engine/internal/lib/packageexistence.go +729 -52
- package/engine/internal/lib/packageexistence_test.go +343 -0
- package/engine/internal/lib/util.go +1 -1
- package/pack/.claude/skills/devrites-lib/SKILL.md +7 -9
- package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +11 -3
- package/pack/.claude/skills/devrites-lib/reference/standards/development-workflow.md +2 -3
- package/pack/.claude/skills/rite/SKILL.md +6 -7
- package/pack/.claude/skills/rite/reference/menu.md +5 -5
- package/pack/.claude/skills/rite-adopt/SKILL.md +2 -3
- package/pack/generated/claude/skills/devrites-lib/SKILL.md +7 -9
- package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +11 -3
- package/pack/generated/claude/skills/devrites-lib/reference/standards/development-workflow.md +2 -3
- package/pack/generated/claude/skills/rite/SKILL.md +6 -7
- package/pack/generated/claude/skills/rite/reference/menu.md +5 -5
- package/pack/generated/claude/skills/rite-adopt/SKILL.md +2 -3
- package/pack/generated/codex/skills/devrites-lib/SKILL.md +7 -9
- package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +11 -3
- package/pack/generated/codex/skills/devrites-lib/reference/standards/development-workflow.md +2 -3
- package/pack/generated/codex/skills/rite/SKILL.md +6 -7
- package/pack/generated/codex/skills/rite/reference/menu.md +5 -5
- package/pack/generated/codex/skills/rite-adopt/SKILL.md +2 -3
- package/package.json +1 -1
- package/scripts/skills-inventory.mjs +18 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
package lib
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"os"
|
|
6
|
+
"os/exec"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"strings"
|
|
9
|
+
"testing"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
func TestPackageExistenceJavaScriptWorkspaces(t *testing.T) {
|
|
13
|
+
tests := []struct {
|
|
14
|
+
name string
|
|
15
|
+
sourcePath string
|
|
16
|
+
workingDir string
|
|
17
|
+
importLine string
|
|
18
|
+
files map[string]string
|
|
19
|
+
wantCode int
|
|
20
|
+
wantFinding string
|
|
21
|
+
}{
|
|
22
|
+
{
|
|
23
|
+
name: "nested workspace",
|
|
24
|
+
sourcePath: "frontend/src/example.ts",
|
|
25
|
+
workingDir: "frontend",
|
|
26
|
+
importLine: `import { Dialog } from "@ark-ui/svelte/dialog";`,
|
|
27
|
+
files: map[string]string{
|
|
28
|
+
"frontend/package.json": `{"dependencies":{"@ark-ui/svelte":"^5.22.1"}}`,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "scoped package subpath",
|
|
33
|
+
sourcePath: "app/src/example.ts",
|
|
34
|
+
importLine: `import value from "@scope/package/subpath";`,
|
|
35
|
+
files: map[string]string{
|
|
36
|
+
"app/package.json": `{"devDependencies":{"@scope/package":"1.0.0"}}`,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "wildcard alias",
|
|
41
|
+
sourcePath: "frontend/src/example.ts",
|
|
42
|
+
importLine: `import Button from "$lib/components/button";`,
|
|
43
|
+
files: map[string]string{
|
|
44
|
+
"frontend/package.json": `{}`,
|
|
45
|
+
"frontend/tsconfig.json": "{\n // SvelteKit generates the inherited aliases.\n \"extends\": \"./.svelte-kit/tsconfig.json\"\n}",
|
|
46
|
+
"frontend/.svelte-kit/tsconfig.json": `{"compilerOptions":{"paths":{"$lib/*":["../src/lib/*"]}}}`,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "exact alias",
|
|
51
|
+
sourcePath: "frontend/src/example.ts",
|
|
52
|
+
importLine: `import library from "$lib";`,
|
|
53
|
+
files: map[string]string{
|
|
54
|
+
"frontend/package.json": `{}`,
|
|
55
|
+
"frontend/tsconfig.json": `{"compilerOptions":{"paths":{"$lib":["./src/lib"]}}}`,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "package shaped alias",
|
|
60
|
+
sourcePath: "frontend/src/example.svelte",
|
|
61
|
+
importLine: `import { css } from "styled-system/css";`,
|
|
62
|
+
files: map[string]string{
|
|
63
|
+
"frontend/package.json": `{}`,
|
|
64
|
+
"frontend/jsconfig.json": `{"compilerOptions":{"paths":{"styled-system/*":["./styled-system/*"]}}}`,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "undeclared dependency",
|
|
69
|
+
sourcePath: "frontend/src/example.ts",
|
|
70
|
+
importLine: `import missing from "definitely-missing-package";`,
|
|
71
|
+
files: map[string]string{"frontend/package.json": `{}`},
|
|
72
|
+
wantCode: 3,
|
|
73
|
+
wantFinding: "definitely-missing-package",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "sibling isolation",
|
|
77
|
+
sourcePath: "workspace-a/src/example.ts",
|
|
78
|
+
importLine: `import sibling from "sibling-only-package";`,
|
|
79
|
+
files: map[string]string{
|
|
80
|
+
"workspace-a/package.json": `{}`,
|
|
81
|
+
"workspace-b/package.json": `{"dependencies":{"sibling-only-package":"1.0.0"}}`,
|
|
82
|
+
},
|
|
83
|
+
wantCode: 3,
|
|
84
|
+
wantFinding: "sibling-only-package",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "malformed config does not allow alias",
|
|
88
|
+
sourcePath: "frontend/src/example.ts",
|
|
89
|
+
importLine: `import { css } from "styled-system/css";`,
|
|
90
|
+
files: map[string]string{
|
|
91
|
+
"frontend/package.json": `{}`,
|
|
92
|
+
"frontend/tsconfig.json": `{"compilerOptions":{"paths":{"styled-system/*":["./styled-system/*"]}}`,
|
|
93
|
+
},
|
|
94
|
+
wantCode: 3,
|
|
95
|
+
wantFinding: "styled-system",
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for _, test := range tests {
|
|
100
|
+
t.Run(test.name, func(t *testing.T) {
|
|
101
|
+
repo := t.TempDir()
|
|
102
|
+
writePackageExistenceFile(t, repo, "Cargo.toml", "[package]\nname = \"fixture\"\nversion = \"0.1.0\"\n")
|
|
103
|
+
writePackageExistenceFile(t, repo, test.sourcePath, "// baseline\n")
|
|
104
|
+
for path, content := range test.files {
|
|
105
|
+
writePackageExistenceFile(t, repo, path, content+"\n")
|
|
106
|
+
}
|
|
107
|
+
gitPackageExistence(t, repo, "init", "-q")
|
|
108
|
+
gitPackageExistence(t, repo, "config", "user.email", "test@example.com")
|
|
109
|
+
gitPackageExistence(t, repo, "config", "user.name", "Test")
|
|
110
|
+
gitPackageExistence(t, repo, "add", ".")
|
|
111
|
+
gitPackageExistence(t, repo, "commit", "-qm", "baseline")
|
|
112
|
+
if err := os.MkdirAll(featureDir(repo, "feat"), 0o755); err != nil {
|
|
113
|
+
t.Fatal(err)
|
|
114
|
+
}
|
|
115
|
+
writePackageExistenceFile(t, repo, test.sourcePath, "// baseline\n"+test.importLine+"\n")
|
|
116
|
+
t.Chdir(filepath.Join(repo, test.workingDir))
|
|
117
|
+
|
|
118
|
+
var stdout, stderr bytes.Buffer
|
|
119
|
+
gotCode := PackageExistence(repo, []string{"feat"}, &stdout, &stderr)
|
|
120
|
+
if gotCode != test.wantCode {
|
|
121
|
+
t.Fatalf("code = %d, want %d\nstdout: %s\nstderr: %s", gotCode, test.wantCode, stdout.String(), stderr.String())
|
|
122
|
+
}
|
|
123
|
+
if test.wantFinding != "" && !strings.Contains(stderr.String(), test.wantFinding) {
|
|
124
|
+
t.Fatalf("stderr does not name %q: %s", test.wantFinding, stderr.String())
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
func TestPackageExistenceNonJavaScriptWorkspaces(t *testing.T) {
|
|
131
|
+
tests := []struct {
|
|
132
|
+
name string
|
|
133
|
+
sourcePath string
|
|
134
|
+
addedSource string
|
|
135
|
+
files map[string]string
|
|
136
|
+
wantCode int
|
|
137
|
+
wantFinding string
|
|
138
|
+
}{
|
|
139
|
+
{
|
|
140
|
+
name: "go import block rejects undeclared module",
|
|
141
|
+
sourcePath: "main.go",
|
|
142
|
+
addedSource: "package main\n\nimport (\n\t\"definitely.invalid/missing\"\n)\n",
|
|
143
|
+
files: map[string]string{"go.mod": "module example.com/app\n\ngo 1.26\n"},
|
|
144
|
+
wantCode: 3,
|
|
145
|
+
wantFinding: "definitely.invalid/missing",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "go modules do not collide by hostname",
|
|
149
|
+
sourcePath: "main.go",
|
|
150
|
+
addedSource: "package main\n\nimport \"github.com/evil/missing\"\n",
|
|
151
|
+
files: map[string]string{"go.mod": "module example.com/app\n\ngo 1.26\n\nrequire github.com/good/pkg v1.0.0\n"},
|
|
152
|
+
wantCode: 3,
|
|
153
|
+
wantFinding: "github.com/evil/missing",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "nested go module declaration",
|
|
157
|
+
sourcePath: "backend/main.go",
|
|
158
|
+
addedSource: "package main\n\nimport \"github.com/good/pkg/sub\"\n",
|
|
159
|
+
files: map[string]string{
|
|
160
|
+
"Cargo.toml": "[package]\nname = \"root\"\nversion = \"0.1.0\"\n",
|
|
161
|
+
"backend/go.mod": "module example.com/backend\n\ngo 1.26\n\nrequire github.com/good/pkg v1.0.0\n",
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "go standard library subpackage",
|
|
166
|
+
sourcePath: "main.go",
|
|
167
|
+
addedSource: "package main\n\nimport \"encoding/json\"\n",
|
|
168
|
+
files: map[string]string{"go.mod": "module example.com/app\n\ngo 1.26\n"},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: "go standard library typo remains undeclared",
|
|
172
|
+
sourcePath: "main.go",
|
|
173
|
+
addedSource: "package main\n\nimport \"encoding/josn\"\n",
|
|
174
|
+
files: map[string]string{"go.mod": "module example.com/app\n\ngo 1.26\n"},
|
|
175
|
+
wantCode: 3,
|
|
176
|
+
wantFinding: "encoding/josn",
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "malformed go manifest does not declare import",
|
|
180
|
+
sourcePath: "main.go",
|
|
181
|
+
addedSource: "package main\n\nimport \"github.com/missing/pkg\"\n",
|
|
182
|
+
files: map[string]string{"go.mod": "module example.com/app\n\ngo 1.26\n\nrequire (\n github.com/missing/pkg v1.0.0\n"},
|
|
183
|
+
wantCode: 3,
|
|
184
|
+
wantFinding: "github.com/missing/pkg",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "rust rejects undeclared crate",
|
|
188
|
+
sourcePath: "src/main.rs",
|
|
189
|
+
addedSource: "use definitely_missing_crate::Thing;\n",
|
|
190
|
+
files: map[string]string{"Cargo.toml": "[package]\nname = \"fixture\"\nversion = \"0.1.0\"\n\n[dependencies]\n"},
|
|
191
|
+
wantCode: 3,
|
|
192
|
+
wantFinding: "definitely_missing_crate",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: "nested rust dependency and hyphen normalization",
|
|
196
|
+
sourcePath: "crates/app/src/main.rs",
|
|
197
|
+
addedSource: "use serde_json::Value;\n",
|
|
198
|
+
files: map[string]string{
|
|
199
|
+
"Cargo.toml": "[workspace]\nmembers = [\"crates/app\"]\n",
|
|
200
|
+
"crates/app/Cargo.toml": "[package]\nname = \"app\"\nversion = \"0.1.0\"\n\n[dependencies.serde-json]\nversion = \"1\"\n",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: "python dotted import uses top-level module",
|
|
205
|
+
sourcePath: "app.py",
|
|
206
|
+
addedSource: "from requests.sessions import Session\n",
|
|
207
|
+
files: map[string]string{"requirements.txt": "requests==2.32.0\n"},
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: "python standard library module",
|
|
211
|
+
sourcePath: "app.py",
|
|
212
|
+
addedSource: "import asyncio\n",
|
|
213
|
+
files: map[string]string{"requirements.txt": "requests==2.32.0\n"},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: "nested python manifest declaration",
|
|
217
|
+
sourcePath: "backend/app.py",
|
|
218
|
+
addedSource: "import requests\n",
|
|
219
|
+
files: map[string]string{
|
|
220
|
+
"Cargo.toml": "[package]\nname = \"root\"\nversion = \"0.1.0\"\n",
|
|
221
|
+
"backend/requirements.txt": "requests==2.32.0\n",
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: "pyproject dependency declaration",
|
|
226
|
+
sourcePath: "service/app.py",
|
|
227
|
+
addedSource: "import requests\n",
|
|
228
|
+
files: map[string]string{
|
|
229
|
+
"service/pyproject.toml": "[project]\ndependencies = [\n \"requests[socks]>=2.32\",\n]\n",
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: "pipfile dependency declaration",
|
|
234
|
+
sourcePath: "service/app.py",
|
|
235
|
+
addedSource: "import requests\n",
|
|
236
|
+
files: map[string]string{
|
|
237
|
+
"service/Pipfile": "[packages]\nrequests = \"*\"\n",
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: "malformed pyproject does not declare import",
|
|
242
|
+
sourcePath: "service/app.py",
|
|
243
|
+
addedSource: "import definitely_missing\n",
|
|
244
|
+
files: map[string]string{
|
|
245
|
+
"service/pyproject.toml": "[project]\ndependencies = [\n \"definitely_missing\"\n",
|
|
246
|
+
},
|
|
247
|
+
wantCode: 3,
|
|
248
|
+
wantFinding: "definitely_missing",
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: "python multiple imports reject undeclared module",
|
|
252
|
+
sourcePath: "app.py",
|
|
253
|
+
addedSource: "import requests, definitely_missing\n",
|
|
254
|
+
files: map[string]string{"requirements.txt": "requests==2.32.0\n"},
|
|
255
|
+
wantCode: 3,
|
|
256
|
+
wantFinding: "definitely_missing",
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: "comments and other ecosystems do not declare python imports",
|
|
260
|
+
sourcePath: "app.py",
|
|
261
|
+
addedSource: "import definitely_missing\n",
|
|
262
|
+
files: map[string]string{
|
|
263
|
+
"Cargo.toml": "[package]\nname = \"definitely_missing\"\nversion = \"0.1.0\"\n",
|
|
264
|
+
"requirements.txt": "# definitely_missing is intentionally absent\n",
|
|
265
|
+
},
|
|
266
|
+
wantCode: 3,
|
|
267
|
+
wantFinding: "definitely_missing",
|
|
268
|
+
},
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
for _, test := range tests {
|
|
272
|
+
t.Run(test.name, func(t *testing.T) {
|
|
273
|
+
repo := t.TempDir()
|
|
274
|
+
for path, content := range test.files {
|
|
275
|
+
writePackageExistenceFile(t, repo, path, content)
|
|
276
|
+
}
|
|
277
|
+
writePackageExistenceFile(t, repo, test.sourcePath, "// baseline\n")
|
|
278
|
+
gitPackageExistence(t, repo, "init", "-q")
|
|
279
|
+
gitPackageExistence(t, repo, "config", "user.email", "test@example.com")
|
|
280
|
+
gitPackageExistence(t, repo, "config", "user.name", "Test")
|
|
281
|
+
gitPackageExistence(t, repo, "add", ".")
|
|
282
|
+
gitPackageExistence(t, repo, "commit", "-qm", "baseline")
|
|
283
|
+
if err := os.MkdirAll(featureDir(repo, "feat"), 0o755); err != nil {
|
|
284
|
+
t.Fatal(err)
|
|
285
|
+
}
|
|
286
|
+
writePackageExistenceFile(t, repo, test.sourcePath, test.addedSource)
|
|
287
|
+
t.Chdir(repo)
|
|
288
|
+
|
|
289
|
+
var stdout, stderr bytes.Buffer
|
|
290
|
+
gotCode := PackageExistence(repo, []string{"feat"}, &stdout, &stderr)
|
|
291
|
+
if gotCode != test.wantCode {
|
|
292
|
+
t.Fatalf("code = %d, want %d\nstdout: %s\nstderr: %s", gotCode, test.wantCode, stdout.String(), stderr.String())
|
|
293
|
+
}
|
|
294
|
+
if test.wantFinding != "" && !strings.Contains(stderr.String(), test.wantFinding) {
|
|
295
|
+
t.Fatalf("stderr does not name %q: %s", test.wantFinding, stderr.String())
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
func TestPackageExistenceGenericResolverFailsClosed(t *testing.T) {
|
|
302
|
+
repo := t.TempDir()
|
|
303
|
+
writePackageExistenceFile(t, repo, "package.json", `{"dependencies":{"definitely-missing-package":"1.0.0"}}`)
|
|
304
|
+
writePackageExistenceFile(t, repo, "src/example.custom", "// baseline\n")
|
|
305
|
+
gitPackageExistence(t, repo, "init", "-q")
|
|
306
|
+
gitPackageExistence(t, repo, "config", "user.email", "test@example.com")
|
|
307
|
+
gitPackageExistence(t, repo, "config", "user.name", "Test")
|
|
308
|
+
gitPackageExistence(t, repo, "add", ".")
|
|
309
|
+
gitPackageExistence(t, repo, "commit", "-qm", "baseline")
|
|
310
|
+
if err := os.MkdirAll(featureDir(repo, "feat"), 0o755); err != nil {
|
|
311
|
+
t.Fatal(err)
|
|
312
|
+
}
|
|
313
|
+
writePackageExistenceFile(t, repo, "src/example.custom", "import \"definitely-missing-package\"\n")
|
|
314
|
+
t.Chdir(repo)
|
|
315
|
+
|
|
316
|
+
var stdout, stderr bytes.Buffer
|
|
317
|
+
gotCode := PackageExistence(repo, []string{"feat"}, &stdout, &stderr)
|
|
318
|
+
if gotCode != 3 {
|
|
319
|
+
t.Fatalf("code = %d, want 3\nstdout: %s\nstderr: %s", gotCode, stdout.String(), stderr.String())
|
|
320
|
+
}
|
|
321
|
+
if !strings.Contains(stderr.String(), "definitely-missing-package") {
|
|
322
|
+
t.Fatalf("stderr does not name unresolved import: %s", stderr.String())
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
func writePackageExistenceFile(t *testing.T, root, path, content string) {
|
|
327
|
+
t.Helper()
|
|
328
|
+
fullPath := filepath.Join(root, filepath.FromSlash(path))
|
|
329
|
+
if err := os.MkdirAll(filepath.Dir(fullPath), 0o755); err != nil {
|
|
330
|
+
t.Fatal(err)
|
|
331
|
+
}
|
|
332
|
+
if err := os.WriteFile(fullPath, []byte(content), 0o644); err != nil {
|
|
333
|
+
t.Fatal(err)
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
func gitPackageExistence(t *testing.T, root string, args ...string) {
|
|
338
|
+
t.Helper()
|
|
339
|
+
cmd := exec.Command("git", append([]string{"-C", root}, args...)...)
|
|
340
|
+
if out, err := cmd.CombinedOutput(); err != nil {
|
|
341
|
+
t.Fatalf("git %s: %v\n%s", strings.Join(args, " "), err, out)
|
|
342
|
+
}
|
|
343
|
+
}
|
|
@@ -14,7 +14,7 @@ func gitToplevel(dir string) string {
|
|
|
14
14
|
if err != nil {
|
|
15
15
|
return ""
|
|
16
16
|
}
|
|
17
|
-
return strings.TrimSpace(string(out))
|
|
17
|
+
return filepath.Clean(strings.TrimSpace(string(out)))
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// gitDiffNames lists the paths that differ, relative to gitRoot. With one ref it
|
|
@@ -7,14 +7,13 @@ disable-model-invocation: true
|
|
|
7
7
|
|
|
8
8
|
# devrites-lib — internal shared helpers (not a command)
|
|
9
9
|
|
|
10
|
-
This is **not** a skill you run. It is
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
any workspace, with no install layout or script path to resolve.
|
|
10
|
+
This is **not** a skill you run. It is DevRites' manifest for shared references
|
|
11
|
+
and control-plane operations. Skills call `devrites-engine <command>` from any
|
|
12
|
+
workspace; no pack script path is required.
|
|
14
13
|
|
|
15
14
|
## Operations
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
These are selected `devrites-engine` contracts; `devrites-engine help` is exhaustive.
|
|
18
17
|
|
|
19
18
|
**Read-only — orient / gate (never mutate the workspace):**
|
|
20
19
|
|
|
@@ -76,7 +75,6 @@ devrites-engine progress
|
|
|
76
75
|
|
|
77
76
|
**Unified entrypoint (tool-agnostic):**
|
|
78
77
|
|
|
79
|
-
-
|
|
80
|
-
`
|
|
81
|
-
|
|
82
|
-
prose. See [`docs/cli.md`](../../../../docs/cli.md).
|
|
78
|
+
- `devrites-engine` is the shared CLI for agents, CI, and humans. The npm
|
|
79
|
+
`devrites` shim acquires it, owns install/update/uninstall bootstrap, and
|
|
80
|
+
proxies other commands.
|
|
@@ -59,9 +59,17 @@ Rules:
|
|
|
59
59
|
|
|
60
60
|
```text
|
|
61
61
|
Awaiting human: <qid> · <gate> · <slice/phase>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
|
|
63
|
+
Question
|
|
64
|
+
<question>
|
|
65
|
+
|
|
66
|
+
Recommended
|
|
67
|
+
1. <option 1 + short reason>
|
|
68
|
+
|
|
69
|
+
Other options
|
|
70
|
+
2. <option 2>
|
|
71
|
+
3. <option 3 if any>
|
|
72
|
+
|
|
65
73
|
Resume: /rite-resolve <qid> "<answer>"
|
|
66
74
|
Record: .devrites/work/<slug>/questions.md
|
|
67
75
|
↻ Hygiene: no /clear until the answer is persisted
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Development workflow
|
|
2
2
|
|
|
3
|
-
Ship small, integrate often, keep the main branch releasable.
|
|
4
|
-
|
|
5
|
-
review → seal → ship) runs on top of.
|
|
3
|
+
Ship small, integrate often, keep the main branch releasable. DevRites runs this
|
|
4
|
+
lifecycle atop that loop: spec → define → vet → build → prove → polish → review → seal → ship.
|
|
6
5
|
|
|
7
6
|
## Work in small batches
|
|
8
7
|
- Break work into thin, independently shippable slices and integrate them frequently —
|
|
@@ -70,12 +70,10 @@ Both forms hit the same skill — the menu form for discovery, the `/rite-<verb>
|
|
|
70
70
|
the now-active feature. It is cheap context-switching only — no re-spec, no phase run. If
|
|
71
71
|
the workspace is missing, list the slugs under `.devrites/work/` and stop.
|
|
72
72
|
|
|
73
|
-
`guide` is
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
every phase — the small change is what makes the full ceremony affordable to watch. Pause
|
|
78
|
-
at each boundary for the user's go-ahead. Teach without lecturing.
|
|
73
|
+
`guide` is an inline first-feature walkthrough. Agree on one **real, genuinely small**
|
|
74
|
+
change, then run spec → temper → define → vet → build → prove → polish → review → seal →
|
|
75
|
+
ship. Before each phase, say what it decides; after, name what it wrote and why. Pause at
|
|
76
|
+
every boundary. Teach without lecturing.
|
|
79
77
|
|
|
80
78
|
Specialist triggers (model-invoked inside the above):
|
|
81
79
|
`devrites-frontend-craft` (UI) · `devrites-browser-proof` (UI verify) ·
|
|
@@ -106,9 +104,10 @@ SPEC /rite spec ≡ /rite-spec investigate deep
|
|
|
106
104
|
ADOPT /rite adopt ≡ /rite-adopt onboard existing code → reverse-derive spec.md + seed conventions
|
|
107
105
|
TEMPER /rite temper ≡ /rite-temper optional — strategic review: scope mode + pre-mortem, harden the spec
|
|
108
106
|
PLAN /rite define ≡ /rite-define turn the spec into plan + task slices + state
|
|
109
|
-
VET /rite vet ≡ /rite-vet
|
|
107
|
+
VET /rite vet ≡ /rite-vet mandatory every plan — light/full engineering review by stakes
|
|
110
108
|
REPLAN /rite plan ≡ /rite-plan decompose / reslice / repair an active plan
|
|
111
109
|
BUILD /rite build ≡ /rite-build implement exactly one verified vertical slice, then stop
|
|
110
|
+
CONVERGE /rite converge ≡ /rite-converge recovery — append work needed to meet intent
|
|
112
111
|
PROVE /rite prove ≡ /rite-prove tests + build + runtime + browser evidence
|
|
113
112
|
POLISH /rite polish ≡ /rite-polish code polish always; UI normalize + polish if UI
|
|
114
113
|
REVIEW /rite review ≡ /rite-review feature-scoped multi-axis review
|
|
@@ -7,11 +7,11 @@ what each command does or how phases connect.
|
|
|
7
7
|
|
|
8
8
|
| Phase | Command | Use when |
|
|
9
9
|
|---|---|---|
|
|
10
|
-
| Spec | `/rite-spec <feature>` | **
|
|
10
|
+
| Spec | `/rite-spec <feature>` | **New feature.** Investigate deeply → write spec.md. Asks with options; gathers attached design references (optional). |
|
|
11
11
|
| Adopt | `/rite-adopt` | Onboard an existing codebase instead of starting fresh — reverse-derive spec.md + seed the conventions ledger. |
|
|
12
12
|
| Temper | `/rite-temper` | _Optional, before define._ Strategic review of the spec — scope mode (expand/selective/hold-rigor/reduce) + pre-mortem; hardens the spec. Best on big/risky features; mandatory in `/rite-autocomplete`. |
|
|
13
13
|
| Plan | `/rite-define` | Turn the approved spec into plan + vertical task slices + state. |
|
|
14
|
-
| Vet | `/rite-vet` |
|
|
14
|
+
| Vet | `/rite-vet` | _Required before build._ Review every plan — scope · architecture · tests · perf; light for simple/reversible, full for high stakes. |
|
|
15
15
|
| Re-plan | `/rite-plan` | The active plan is too big, wrong, stale, ambiguous, or blocked. |
|
|
16
16
|
| Build | `/rite-build` | Implement the next single vertical slice. Stops after one slice. |
|
|
17
17
|
| Converge | `/rite-converge` | _Recovery._ Code drifted from / falls short of intent (resumed cold, adopted, stalled build) — assess live code vs spec/plan/tasks and append the remaining work as new slices for `/rite-build`. |
|
|
@@ -27,10 +27,10 @@ what each command does or how phases connect.
|
|
|
27
27
|
## Typical orderings
|
|
28
28
|
|
|
29
29
|
- **Every feature**: `/rite-spec` (spec) → *(big feature? `/rite-temper` — strategic review)* →
|
|
30
|
-
`/rite-define` (plan) →
|
|
30
|
+
`/rite-define` (plan) → `/rite-vet` (engineering review; light or full) →
|
|
31
31
|
`/rite-build` ×N (all slices) → `/rite-prove` (once all built) →
|
|
32
32
|
`/rite-polish` (always: code + UI if UI) → `/rite-review` → `/rite-seal` → `/rite-ship`.
|
|
33
|
-
- **Existing codebase**: `/rite-adopt`
|
|
33
|
+
- **Existing codebase**: `/rite-adopt` → `/rite-define` → `/rite-vet` → build.
|
|
34
34
|
- **Drift mid-build**: stop → drift question → `/rite-plan` (repair) → resume build.
|
|
35
35
|
- **Resumed / adopted / stalled**: `/rite-converge` (assess live code vs intent → append the
|
|
36
36
|
remaining slices) → `/rite-build` ×N → continue at `/rite-prove`.
|
|
@@ -38,5 +38,5 @@ what each command does or how phases connect.
|
|
|
38
38
|
## Rules this menu obeys
|
|
39
39
|
|
|
40
40
|
- `/rite` never edits code or runs a phase workflow.
|
|
41
|
-
-
|
|
41
|
+
- Menu mode runs `devrites-engine first-task`; `/rite-status` owns workspace status.
|
|
42
42
|
- It suggests; the user (or Claude, when appropriate) invokes the real skill.
|
|
@@ -79,9 +79,8 @@ upholds invariants worth proposing as project principles (step 4a).
|
|
|
79
79
|
([`principles.md`](../devrites-lib/reference/standards/principles.md)). Propose, don't impose — an unratified candidate
|
|
80
80
|
stays a convention, not a gate. Skip cleanly when nothing rises to an invariant (common — a
|
|
81
81
|
fresh adopt may declare zero principles, and that's valid).
|
|
82
|
-
5. **Hand off.**
|
|
83
|
-
|
|
84
|
-
plan or build here.
|
|
82
|
+
5. **Hand off.** Spec and ledger are ready. Next: `/rite-temper` if big/risky,
|
|
83
|
+
else `/rite-define`; every plan then runs `/rite-vet` before build. Do not plan/build here.
|
|
85
84
|
**Completion:** one next rite is reported and no plan or application code was written.
|
|
86
85
|
|
|
87
86
|
> **Mid-flight discipline.** Don't invent conventions the code doesn't actually follow, don't
|
|
@@ -7,14 +7,13 @@ disable-model-invocation: true
|
|
|
7
7
|
|
|
8
8
|
# devrites-lib — internal shared helpers (not a command)
|
|
9
9
|
|
|
10
|
-
This is **not** a skill you run. It is
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
any workspace, with no install layout or script path to resolve.
|
|
10
|
+
This is **not** a skill you run. It is DevRites' manifest for shared references
|
|
11
|
+
and control-plane operations. Skills call `devrites-engine <command>` from any
|
|
12
|
+
workspace; no pack script path is required.
|
|
14
13
|
|
|
15
14
|
## Operations
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
These are selected `devrites-engine` contracts; `devrites-engine help` is exhaustive.
|
|
18
17
|
|
|
19
18
|
**Read-only — orient / gate (never mutate the workspace):**
|
|
20
19
|
|
|
@@ -76,7 +75,6 @@ devrites-engine progress
|
|
|
76
75
|
|
|
77
76
|
**Unified entrypoint (tool-agnostic):**
|
|
78
77
|
|
|
79
|
-
-
|
|
80
|
-
`
|
|
81
|
-
|
|
82
|
-
prose. See [`docs/cli.md`](../../../../docs/cli.md).
|
|
78
|
+
- `devrites-engine` is the shared CLI for agents, CI, and humans. The npm
|
|
79
|
+
`devrites` shim acquires it, owns install/update/uninstall bootstrap, and
|
|
80
|
+
proxies other commands.
|
|
@@ -59,9 +59,17 @@ Rules:
|
|
|
59
59
|
|
|
60
60
|
```text
|
|
61
61
|
Awaiting human: <qid> · <gate> · <slice/phase>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
|
|
63
|
+
Question
|
|
64
|
+
<question>
|
|
65
|
+
|
|
66
|
+
Recommended
|
|
67
|
+
1. <option 1 + short reason>
|
|
68
|
+
|
|
69
|
+
Other options
|
|
70
|
+
2. <option 2>
|
|
71
|
+
3. <option 3 if any>
|
|
72
|
+
|
|
65
73
|
Resume: /rite-resolve <qid> "<answer>"
|
|
66
74
|
Record: .devrites/work/<slug>/questions.md
|
|
67
75
|
↻ Hygiene: no /clear until the answer is persisted
|
package/pack/generated/claude/skills/devrites-lib/reference/standards/development-workflow.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Development workflow
|
|
2
2
|
|
|
3
|
-
Ship small, integrate often, keep the main branch releasable.
|
|
4
|
-
|
|
5
|
-
review → seal → ship) runs on top of.
|
|
3
|
+
Ship small, integrate often, keep the main branch releasable. DevRites runs this
|
|
4
|
+
lifecycle atop that loop: spec → define → vet → build → prove → polish → review → seal → ship.
|
|
6
5
|
|
|
7
6
|
## Work in small batches
|
|
8
7
|
- Break work into thin, independently shippable slices and integrate them frequently —
|
|
@@ -70,12 +70,10 @@ Both forms hit the same skill — the menu form for discovery, the `/rite-<verb>
|
|
|
70
70
|
the now-active feature. It is cheap context-switching only — no re-spec, no phase run. If
|
|
71
71
|
the workspace is missing, list the slugs under `.devrites/work/` and stop.
|
|
72
72
|
|
|
73
|
-
`guide` is
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
every phase — the small change is what makes the full ceremony affordable to watch. Pause
|
|
78
|
-
at each boundary for the user's go-ahead. Teach without lecturing.
|
|
73
|
+
`guide` is an inline first-feature walkthrough. Agree on one **real, genuinely small**
|
|
74
|
+
change, then run spec → temper → define → vet → build → prove → polish → review → seal →
|
|
75
|
+
ship. Before each phase, say what it decides; after, name what it wrote and why. Pause at
|
|
76
|
+
every boundary. Teach without lecturing.
|
|
79
77
|
|
|
80
78
|
Specialist triggers (model-invoked inside the above):
|
|
81
79
|
`devrites-frontend-craft` (UI) · `devrites-browser-proof` (UI verify) ·
|
|
@@ -106,9 +104,10 @@ SPEC /rite spec ≡ /rite-spec investigate deep
|
|
|
106
104
|
ADOPT /rite adopt ≡ /rite-adopt onboard existing code → reverse-derive spec.md + seed conventions
|
|
107
105
|
TEMPER /rite temper ≡ /rite-temper optional — strategic review: scope mode + pre-mortem, harden the spec
|
|
108
106
|
PLAN /rite define ≡ /rite-define turn the spec into plan + task slices + state
|
|
109
|
-
VET /rite vet ≡ /rite-vet
|
|
107
|
+
VET /rite vet ≡ /rite-vet mandatory every plan — light/full engineering review by stakes
|
|
110
108
|
REPLAN /rite plan ≡ /rite-plan decompose / reslice / repair an active plan
|
|
111
109
|
BUILD /rite build ≡ /rite-build implement exactly one verified vertical slice, then stop
|
|
110
|
+
CONVERGE /rite converge ≡ /rite-converge recovery — append work needed to meet intent
|
|
112
111
|
PROVE /rite prove ≡ /rite-prove tests + build + runtime + browser evidence
|
|
113
112
|
POLISH /rite polish ≡ /rite-polish code polish always; UI normalize + polish if UI
|
|
114
113
|
REVIEW /rite review ≡ /rite-review feature-scoped multi-axis review
|
|
@@ -7,11 +7,11 @@ what each command does or how phases connect.
|
|
|
7
7
|
|
|
8
8
|
| Phase | Command | Use when |
|
|
9
9
|
|---|---|---|
|
|
10
|
-
| Spec | `/rite-spec <feature>` | **
|
|
10
|
+
| Spec | `/rite-spec <feature>` | **New feature.** Investigate deeply → write spec.md. Asks with options; gathers attached design references (optional). |
|
|
11
11
|
| Adopt | `/rite-adopt` | Onboard an existing codebase instead of starting fresh — reverse-derive spec.md + seed the conventions ledger. |
|
|
12
12
|
| Temper | `/rite-temper` | _Optional, before define._ Strategic review of the spec — scope mode (expand/selective/hold-rigor/reduce) + pre-mortem; hardens the spec. Best on big/risky features; mandatory in `/rite-autocomplete`. |
|
|
13
13
|
| Plan | `/rite-define` | Turn the approved spec into plan + vertical task slices + state. |
|
|
14
|
-
| Vet | `/rite-vet` |
|
|
14
|
+
| Vet | `/rite-vet` | _Required before build._ Review every plan — scope · architecture · tests · perf; light for simple/reversible, full for high stakes. |
|
|
15
15
|
| Re-plan | `/rite-plan` | The active plan is too big, wrong, stale, ambiguous, or blocked. |
|
|
16
16
|
| Build | `/rite-build` | Implement the next single vertical slice. Stops after one slice. |
|
|
17
17
|
| Converge | `/rite-converge` | _Recovery._ Code drifted from / falls short of intent (resumed cold, adopted, stalled build) — assess live code vs spec/plan/tasks and append the remaining work as new slices for `/rite-build`. |
|
|
@@ -27,10 +27,10 @@ what each command does or how phases connect.
|
|
|
27
27
|
## Typical orderings
|
|
28
28
|
|
|
29
29
|
- **Every feature**: `/rite-spec` (spec) → *(big feature? `/rite-temper` — strategic review)* →
|
|
30
|
-
`/rite-define` (plan) →
|
|
30
|
+
`/rite-define` (plan) → `/rite-vet` (engineering review; light or full) →
|
|
31
31
|
`/rite-build` ×N (all slices) → `/rite-prove` (once all built) →
|
|
32
32
|
`/rite-polish` (always: code + UI if UI) → `/rite-review` → `/rite-seal` → `/rite-ship`.
|
|
33
|
-
- **Existing codebase**: `/rite-adopt`
|
|
33
|
+
- **Existing codebase**: `/rite-adopt` → `/rite-define` → `/rite-vet` → build.
|
|
34
34
|
- **Drift mid-build**: stop → drift question → `/rite-plan` (repair) → resume build.
|
|
35
35
|
- **Resumed / adopted / stalled**: `/rite-converge` (assess live code vs intent → append the
|
|
36
36
|
remaining slices) → `/rite-build` ×N → continue at `/rite-prove`.
|
|
@@ -38,5 +38,5 @@ what each command does or how phases connect.
|
|
|
38
38
|
## Rules this menu obeys
|
|
39
39
|
|
|
40
40
|
- `/rite` never edits code or runs a phase workflow.
|
|
41
|
-
-
|
|
41
|
+
- Menu mode runs `devrites-engine first-task`; `/rite-status` owns workspace status.
|
|
42
42
|
- It suggests; the user (or Claude, when appropriate) invokes the real skill.
|
|
@@ -79,9 +79,8 @@ upholds invariants worth proposing as project principles (step 4a).
|
|
|
79
79
|
([`principles.md`](../devrites-lib/reference/standards/principles.md)). Propose, don't impose — an unratified candidate
|
|
80
80
|
stays a convention, not a gate. Skip cleanly when nothing rises to an invariant (common — a
|
|
81
81
|
fresh adopt may declare zero principles, and that's valid).
|
|
82
|
-
5. **Hand off.**
|
|
83
|
-
|
|
84
|
-
plan or build here.
|
|
82
|
+
5. **Hand off.** Spec and ledger are ready. Next: `/rite-temper` if big/risky,
|
|
83
|
+
else `/rite-define`; every plan then runs `/rite-vet` before build. Do not plan/build here.
|
|
85
84
|
**Completion:** one next rite is reported and no plan or application code was written.
|
|
86
85
|
|
|
87
86
|
> **Mid-flight discipline.** Don't invent conventions the code doesn't actually follow, don't
|