devrites 3.0.0 → 3.0.1
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 +6 -0
- package/README.md +1 -1
- package/docs/engine/commands.md +1 -1
- package/engine/internal/lib/analyze.go +9 -10
- package/engine/internal/lib/checkacceptance.go +9 -9
- package/engine/internal/lib/coverage.go +13 -15
- package/engine/internal/lib/lanes.go +1 -1
- package/engine/testdata/golden/TestParityAnalyze/slug=noac.golden +1 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-ok.golden +1 -1
- package/engine/tests/canonical_ac_ids_test.go +44 -0
- package/pack/.claude/skills/rite-define/SKILL.md +6 -0
- package/pack/.claude/skills/rite-spec/SKILL.md +2 -0
- package/pack/.claude/skills/rite-vet/SKILL.md +6 -1
- package/pack/generated/claude/skills/rite-define/SKILL.md +6 -0
- package/pack/generated/claude/skills/rite-spec/SKILL.md +2 -0
- package/pack/generated/claude/skills/rite-vet/SKILL.md +6 -1
- package/pack/generated/codex/skills/rite-define/SKILL.md +6 -0
- package/pack/generated/codex/skills/rite-spec/SKILL.md +2 -0
- package/pack/generated/codex/skills/rite-vet/SKILL.md +6 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to DevRites are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and DevRites adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Releases are generated automatically by [semantic-release](https://semantic-release.gitbook.io/) from Conventional Commits on `main`.
|
|
4
4
|
|
|
5
|
+
## [3.0.1](https://github.com/ViktorsBaikers/DevRites/compare/v3.0.0...v3.0.1) (2026-07-20)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
* **rite:** run analysis after task definition ([6373227](https://github.com/ViktorsBaikers/DevRites/commit/6373227521b5e1e6b22bfe48e98799e709f5dc7a))
|
|
10
|
+
|
|
5
11
|
## [3.0.0](https://github.com/ViktorsBaikers/DevRites/compare/v2.6.1...v3.0.0) (2026-07-17)
|
|
6
12
|
|
|
7
13
|
### ⚠ BREAKING CHANGES
|
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ Full diagram set (lifecycle, polish orchestrator, review fan-out, debug loop,
|
|
|
115
115
|
rules carrier, workspace state, namespace map) →
|
|
116
116
|
[`docs/flow.md`](docs/flow.md).
|
|
117
117
|
|
|
118
|
-
**Status:** [`v3.0.
|
|
118
|
+
**Status:** [`v3.0.1`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v3.0.1) — see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
|
|
119
119
|
|
|
120
120
|
## Contents
|
|
121
121
|
|
package/docs/engine/commands.md
CHANGED
|
@@ -159,7 +159,7 @@ Prohibitions tables — a blocking spec-gate check.
|
|
|
159
159
|
any code is written, so a coverage gap surfaces as a one-line plan edit instead of a reslice
|
|
160
160
|
mid-build. It emits a markdown report with four passes:
|
|
161
161
|
|
|
162
|
-
- **Coverage** — a spec `
|
|
162
|
+
- **Coverage** — a spec `AC-###` that no slice `Satisfies:` (**CRITICAL**; legacy `[ACn]` remains supported).
|
|
163
163
|
- **Consistency** — a slice that `Satisfies:` an AC the spec never defines (**CRITICAL**).
|
|
164
164
|
- **Orphan slice** — a slice satisfying no acceptance criterion (warn).
|
|
165
165
|
- **Ambiguity** — an unquantified vague adjective (`fast`, `robust`, `intuitive`, …) or an
|
|
@@ -15,7 +15,7 @@ import (
|
|
|
15
15
|
// written, so a coverage gap surfaces as a one-line plan edit rather than a reslice
|
|
16
16
|
// mid-build. It emits a markdown report and flags:
|
|
17
17
|
//
|
|
18
|
-
// Coverage — a spec
|
|
18
|
+
// Coverage — a spec AC id that no slice Satisfies (CRITICAL)
|
|
19
19
|
// Consistency — a slice that Satisfies an AC the spec never defines (CRITICAL)
|
|
20
20
|
// Orphan slice — a slice that satisfies no acceptance criterion (warn)
|
|
21
21
|
// Ambiguity — an unquantified vague adjective or unresolved
|
|
@@ -27,9 +27,8 @@ import (
|
|
|
27
27
|
// Exit codes: 0 clear · 1 at least one CRITICAL finding · 2 no workspace (no active
|
|
28
28
|
// slug, or spec.md/tasks.md missing).
|
|
29
29
|
var (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
sliceHeadRe = regexp.MustCompile(`^##[[:space:]]*Slice`)
|
|
30
|
+
taskACRe = regexp.MustCompile(`\b(?:AC-[0-9]{3}|AC[0-9]+)\b`)
|
|
31
|
+
sliceHeadRe = regexp.MustCompile(`(?i)^##[[:space:]]*(?:SLICE-[0-9]{3}\b|Slice\b)`)
|
|
33
32
|
sliceNamePrefixRe = regexp.MustCompile(`^##[[:space:]]*`) // stripped to leave the slice name
|
|
34
33
|
analyzeSatisfiesRe = regexp.MustCompile(`^[[:space:]]*Satisfies:`)
|
|
35
34
|
// Ambiguity scan: a vague quality adjective with no number on the line is an
|
|
@@ -57,7 +56,7 @@ func Analyze(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
57
56
|
specData, _ := os.ReadFile(spec)
|
|
58
57
|
tasksData, _ := os.ReadFile(tasks)
|
|
59
58
|
|
|
60
|
-
specACs := sortedACIDs(
|
|
59
|
+
specACs := sortedACIDs(acIDRe, specData, true) // legacy brackets stripped: "[AC1]" -> "AC1"
|
|
61
60
|
taskACs := sortedACIDs(taskACRe, tasksData, false)
|
|
62
61
|
specSet := stringSet(specACs)
|
|
63
62
|
taskSet := stringSet(taskACs)
|
|
@@ -70,7 +69,7 @@ func Analyze(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
70
69
|
covered := 0
|
|
71
70
|
fmt.Fprintln(stdout, "## Coverage")
|
|
72
71
|
if len(specACs) == 0 {
|
|
73
|
-
fmt.Fprintln(stdout, "- [warn] spec.md — no
|
|
72
|
+
fmt.Fprintln(stdout, "- [warn] spec.md — no AC-### acceptance ids found; tag criteria for a machine-checkable gate.")
|
|
74
73
|
} else {
|
|
75
74
|
for _, ac := range specACs {
|
|
76
75
|
if taskSet[ac] {
|
|
@@ -155,9 +154,9 @@ func ambiguityFindings(specData []byte) []string {
|
|
|
155
154
|
out = append(out, fmt.Sprintf("- [warn] spec.md:L%d — unresolved placeholder %q", ln, m))
|
|
156
155
|
continue
|
|
157
156
|
}
|
|
158
|
-
// Strip the AC id before the digit gate — else the id's own digit
|
|
159
|
-
//
|
|
160
|
-
bare :=
|
|
157
|
+
// Strip the AC id before the digit gate — else the id's own digit masks
|
|
158
|
+
// a vague adjective on the very criterion line it tags.
|
|
159
|
+
bare := acIDRe.ReplaceAllString(line, "")
|
|
161
160
|
bare = taskACRe.ReplaceAllString(bare, "")
|
|
162
161
|
if !analyzeDigitRe.MatchString(bare) {
|
|
163
162
|
if m := vagueAdjRe.FindString(bare); m != "" {
|
|
@@ -170,7 +169,7 @@ func ambiguityFindings(specData []byte) []string {
|
|
|
170
169
|
|
|
171
170
|
// sortedACIDs collects every acceptance id matched by re, deduplicated and sorted
|
|
172
171
|
// lexicographically. When strip is set the surrounding brackets are removed, so
|
|
173
|
-
// "[AC1]" becomes "AC1". Returns nil when there are none.
|
|
172
|
+
// Legacy "[AC1]" becomes "AC1". Returns nil when there are none.
|
|
174
173
|
func sortedACIDs(re *regexp.Regexp, data []byte, strip bool) []string {
|
|
175
174
|
seen := map[string]bool{}
|
|
176
175
|
var out []string
|
|
@@ -10,17 +10,17 @@ import (
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
// Executable acceptance criteria — a port of check-acceptance.sh. Grades the
|
|
13
|
-
//
|
|
13
|
+
// AC-### acceptance criteria in spec.md against seal.md: each spec
|
|
14
14
|
// criterion must be checked ([x]) in seal.md. Without ids it falls back to a
|
|
15
15
|
// count/unchecked heuristic. The spec-time mirror is SpecValidate (shape); this
|
|
16
|
-
// grades proof.
|
|
16
|
+
// grades proof. Legacy [ACn] ids remain supported for existing workspaces.
|
|
17
17
|
//
|
|
18
18
|
// Exit codes: 0 every criterion proven · 1 gap (uncovered/unproven) · 2 usage ·
|
|
19
19
|
// 5 missing spec.md or seal.md.
|
|
20
20
|
var (
|
|
21
21
|
acHeadingRe = regexp.MustCompile(`^##[[:space:]]+[Aa]cceptance [Cc]riteria`)
|
|
22
22
|
h2Re = regexp.MustCompile(`^##[[:space:]]`)
|
|
23
|
-
acIDRe = regexp.MustCompile(`\[AC[0-9]+\]`)
|
|
23
|
+
acIDRe = regexp.MustCompile(`\[AC[0-9]+\]|\bAC-[0-9]{3}\b`)
|
|
24
24
|
checkedRe = regexp.MustCompile(`^[[:space:]]*-[[:space:]]*\[[xX]\]`)
|
|
25
25
|
bulletRe = regexp.MustCompile(`^[[:space:]]*-[[:space:]]`)
|
|
26
26
|
checkboxRe = regexp.MustCompile(`^[[:space:]]*-[[:space:]]*\[[ xX]\]`)
|
|
@@ -64,7 +64,7 @@ func CheckAcceptance(ws string, stdout, stderr io.Writer) int {
|
|
|
64
64
|
specIDs := sortedUniqueIDs(specBody)
|
|
65
65
|
|
|
66
66
|
if len(specIDs) > 0 {
|
|
67
|
-
// ID mode — each spec
|
|
67
|
+
// ID mode — each spec AC id must be checked ([x]) in seal.md.
|
|
68
68
|
checkedIDs := map[string]bool{}
|
|
69
69
|
for _, line := range sealBody {
|
|
70
70
|
if checkedRe.MatchString(line) {
|
|
@@ -96,15 +96,15 @@ func CheckAcceptance(ws string, stdout, stderr io.Writer) int {
|
|
|
96
96
|
sealUnchecked := countMatching(sealBody, uncheckedRe)
|
|
97
97
|
|
|
98
98
|
if sealTotal < specN {
|
|
99
|
-
fmt.Fprintf(stderr, "check-acceptance: seal.md lists %d acceptance items but spec.md has %d criteria — %d dropped. (Tag criteria
|
|
99
|
+
fmt.Fprintf(stderr, "check-acceptance: seal.md lists %d acceptance items but spec.md has %d criteria — %d dropped. (Tag criteria AC-### to grade by id.)\n",
|
|
100
100
|
sealTotal, specN, specN-sealTotal)
|
|
101
101
|
return 1
|
|
102
102
|
}
|
|
103
103
|
if sealUnchecked > 0 {
|
|
104
|
-
fmt.Fprintf(stderr, "check-acceptance: %d acceptance criterion(s) unchecked in seal.md (unproven). (Tag criteria
|
|
104
|
+
fmt.Fprintf(stderr, "check-acceptance: %d acceptance criterion(s) unchecked in seal.md (unproven). (Tag criteria AC-### to grade by id.)\n", sealUnchecked)
|
|
105
105
|
return 1
|
|
106
106
|
}
|
|
107
|
-
fmt.Fprintf(stdout, "check-acceptance: OK — %d acceptance items all checked in seal.md (no ids; add
|
|
107
|
+
fmt.Fprintf(stdout, "check-acceptance: OK — %d acceptance items all checked in seal.md (no ids; add AC-### for id-level grading)\n", sealTotal)
|
|
108
108
|
return 0
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -145,8 +145,8 @@ func sectionEmpty(lines []string) bool {
|
|
|
145
145
|
return strings.TrimRight(strings.Join(lines, "\n"), "\n") == ""
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
// sortedUniqueIDs extracts
|
|
149
|
-
//
|
|
148
|
+
// sortedUniqueIDs extracts canonical AC-### and legacy [ACn] ids from the lines,
|
|
149
|
+
// then returns them sorted and de-duplicated.
|
|
150
150
|
func sortedUniqueIDs(lines []string) []string {
|
|
151
151
|
set := map[string]bool{}
|
|
152
152
|
for _, line := range lines {
|
|
@@ -18,9 +18,9 @@ import (
|
|
|
18
18
|
// criterion is covered by a slice and proven in the seal. Read-only.
|
|
19
19
|
//
|
|
20
20
|
// The convention pairs with check-acceptance: a criterion carries a stable id,
|
|
21
|
-
// spec.md declares `- [ ]
|
|
22
|
-
// `Satisfies:
|
|
23
|
-
//
|
|
21
|
+
// spec.md declares `- [ ] AC-###: …`, a tasks.md slice claims it with a
|
|
22
|
+
// `Satisfies: AC-###` line, and seal.md proves it with `- [x] AC-###: …`.
|
|
23
|
+
// Legacy [ACn] ids remain supported for existing workspaces.
|
|
24
24
|
//
|
|
25
25
|
// base is the project directory that contains the .devrites tree; slug defaults
|
|
26
26
|
// to the active feature named in .devrites/ACTIVE. The matrix is the stdout
|
|
@@ -83,16 +83,14 @@ func Coverage(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
83
83
|
return 0
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
// acIDRe
|
|
86
|
+
// acIDRe is shared with check-acceptance, which declares it.
|
|
87
87
|
var (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
satisfiesRe = regexp.MustCompile(`^[[:space:]]*Satisfies:`)
|
|
88
|
+
sliceStripRe = regexp.MustCompile(`^##[[:space:]]*`)
|
|
89
|
+
satisfiesRe = regexp.MustCompile(`^[[:space:]]*Satisfies:`)
|
|
91
90
|
)
|
|
92
91
|
|
|
93
|
-
// acIDs returns
|
|
94
|
-
//
|
|
95
|
-
// under LC_ALL=C.
|
|
92
|
+
// acIDs returns unique canonical AC-### and legacy [ACn] ids declared in
|
|
93
|
+
// spec.md, bracket-stripped and byte-order sorted.
|
|
96
94
|
func acIDs(spec string) []string {
|
|
97
95
|
seen := map[string]bool{}
|
|
98
96
|
var ids []string
|
|
@@ -118,7 +116,7 @@ func slicesForAC(tasks, ac string) string {
|
|
|
118
116
|
sc := newLineScanner(strings.NewReader(tasks))
|
|
119
117
|
for sc.Scan() {
|
|
120
118
|
line := sc.Text()
|
|
121
|
-
if
|
|
119
|
+
if sliceHeadRe.MatchString(line) {
|
|
122
120
|
cur = sliceStripRe.ReplaceAllString(line, "")
|
|
123
121
|
}
|
|
124
122
|
if satisfiesRe.MatchString(line) && strings.Contains(line, ac) {
|
|
@@ -128,11 +126,11 @@ func slicesForAC(tasks, ac string) string {
|
|
|
128
126
|
return strings.Join(matched, ";")
|
|
129
127
|
}
|
|
130
128
|
|
|
131
|
-
// provenInSeal reports whether seal.md
|
|
132
|
-
//
|
|
133
|
-
// script greps for.
|
|
129
|
+
// provenInSeal reports whether seal.md contains a checked canonical or legacy
|
|
130
|
+
// criterion line.
|
|
134
131
|
func provenInSeal(seal, ac string) bool {
|
|
135
|
-
|
|
132
|
+
quoted := regexp.QuoteMeta(ac)
|
|
133
|
+
re := regexp.MustCompile(`(?m)^[[:space:]]*-[[:space:]]*\[x\][[:space:]]*(?:\[` + quoted + `\]|` + quoted + `\b)`)
|
|
136
134
|
return re.MatchString(seal)
|
|
137
135
|
}
|
|
138
136
|
|
|
@@ -83,7 +83,7 @@ func parseLaneSlices(md string) []laneSlice {
|
|
|
83
83
|
}
|
|
84
84
|
for _, line := range strings.Split(md, "\n") {
|
|
85
85
|
trim := strings.TrimSpace(line)
|
|
86
|
-
if
|
|
86
|
+
if sliceHeadRe.MatchString(trim) {
|
|
87
87
|
flush()
|
|
88
88
|
cur = &laneSlice{name: strings.TrimPrefix(trim, "## ")}
|
|
89
89
|
continue
|
|
@@ -2,7 +2,7 @@ exit 0
|
|
|
2
2
|
# Cross-artifact analysis: noac
|
|
3
3
|
|
|
4
4
|
## Coverage
|
|
5
|
-
- [warn] spec.md — no
|
|
5
|
+
- [warn] spec.md — no AC-### acceptance ids found; tag criteria for a machine-checkable gate.
|
|
6
6
|
|
|
7
7
|
## Consistency
|
|
8
8
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
exit 0
|
|
2
|
-
check-acceptance: OK — 2 acceptance items all checked in seal.md (no ids; add
|
|
2
|
+
check-acceptance: OK — 2 acceptance items all checked in seal.md (no ids; add AC-### for id-level grading)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
package main_test
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
func TestCanonicalAcceptanceIDsAcrossGates(t *testing.T) {
|
|
9
|
+
work := t.TempDir()
|
|
10
|
+
writeFeatureFile(t, work, "canonical", "spec.md",
|
|
11
|
+
"## Acceptance criteria\n- [ ] AC-001: alpha\n- [ ] AC-002: beta\n")
|
|
12
|
+
writeFeatureFile(t, work, "canonical", "tasks.md",
|
|
13
|
+
"## SLICE-001 alpha\nSatisfies: AC-001\n## SLICE-002 beta\nSatisfies: AC-002\n## SLICE-003 orphan\n")
|
|
14
|
+
writeFeatureFile(t, work, "canonical", "seal.md",
|
|
15
|
+
"## Acceptance Criteria\n- [x] AC-001: alpha — evidence: t1\n- [x] AC-002: beta — evidence: t2\n")
|
|
16
|
+
|
|
17
|
+
t.Run("analyze", func(t *testing.T) {
|
|
18
|
+
stdout, stderr, code := runGo(t, work, "analyze", "canonical")
|
|
19
|
+
if code != 0 || !strings.Contains(stdout, "AC-001 — covered") || !strings.Contains(stdout, "slice 'SLICE-003 orphan'") {
|
|
20
|
+
t.Fatalf("exit=%d stdout=%q stderr=%q", code, stdout, stderr)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
t.Run("coverage", func(t *testing.T) {
|
|
25
|
+
stdout, stderr, code := runGo(t, work, "coverage", "canonical")
|
|
26
|
+
if code != 0 || !strings.Contains(stdout, "| AC-001 | SLICE-001 alpha | yes |") {
|
|
27
|
+
t.Fatalf("exit=%d stdout=%q stderr=%q", code, stdout, stderr)
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
t.Run("check-acceptance", func(t *testing.T) {
|
|
32
|
+
stdout, stderr, code := runGo(t, work, "check-acceptance", ".devrites/features/canonical")
|
|
33
|
+
if code != 0 || !strings.Contains(stdout, "AC-001 AC-002") {
|
|
34
|
+
t.Fatalf("exit=%d stdout=%q stderr=%q", code, stdout, stderr)
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
t.Run("lanes", func(t *testing.T) {
|
|
39
|
+
stdout, stderr, code := runGo(t, work, "lanes", "plan", "canonical")
|
|
40
|
+
if code != 0 || !strings.Contains(stdout, "- SLICE-001 alpha:") {
|
|
41
|
+
t.Fatalf("exit=%d stdout=%q stderr=%q", code, stdout, stderr)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
}
|
|
@@ -119,6 +119,12 @@ Pull these via `Read` when shaping the plan:
|
|
|
119
119
|
as a blocking gate at `/rite-vet`; no file → none declared → nothing to check.)
|
|
120
120
|
6. **Write** `architecture.md`, `plan.md`, `tasks.md`, and `traceability.md`; update
|
|
121
121
|
`state.md` (phase: plan → next `/rite-vet`).
|
|
122
|
+
6a. **Cross-artifact gate — now that `tasks.md` exists.** Run the deterministic
|
|
123
|
+
spec↔tasks coverage/consistency check; any non-zero result blocks plan readiness:
|
|
124
|
+
```bash
|
|
125
|
+
S="$(cat .devrites/ACTIVE 2>/dev/null)"
|
|
126
|
+
devrites-engine analyze "$S"
|
|
127
|
+
```
|
|
122
128
|
7. **Readiness gate** (bottom of plan-template): every acceptance criterion covered by a
|
|
123
129
|
slice, dependency order acyclic + risk-first, no unjustified deviation, rollback for
|
|
124
130
|
every destructive/migration step. **Stop and confirm** before code. Render the review-before-code
|
|
@@ -185,6 +185,8 @@ stay flat `AC-###` bullets; the grammar is opt-in by rigor, never forced.
|
|
|
185
185
|
devrites-engine spec-skeleton ".devrites/work/<slug>"
|
|
186
186
|
devrites-engine spec-validate ".devrites/work/<slug>" --against .devrites/specs
|
|
187
187
|
```
|
|
188
|
+
**Do not run `devrites-engine analyze` in this phase:** `tasks.md` deliberately does not
|
|
189
|
+
exist yet. `/rite-define` owns the first analyze pass after it writes the slices.
|
|
188
190
|
Treat edge/prohibition findings as blocking just like grammar findings. When it passes, write `Spec gate: passed <iso>` to `state.md`.
|
|
189
191
|
6a. **Review-before-code digest.** Before handing off to planning, render the cheap human review:
|
|
190
192
|
`Intent` (one sentence), `Done means` (top acceptance/scenario IDs), `Scope/risk` (what is in/out
|
|
@@ -140,7 +140,12 @@ Audit for a plan resting on unstated beliefs).
|
|
|
140
140
|
(`/rite-build` and `/rite-prove` read it). Harden `plan.md` / `tasks.md` directly for
|
|
141
141
|
behavior-preserving refinements; route every acceptance/behavior-changing delta through the
|
|
142
142
|
**Spec Drift Guard** (`drift.md` + recorded decision + `/rite-plan repair`). Append
|
|
143
|
-
`decisions.md` (one ADR per material call) and `assumptions.md`.
|
|
143
|
+
`decisions.md` (one ADR per material call) and `assumptions.md`. Re-run the gate after
|
|
144
|
+
every fold-back so a task edit cannot invalidate the earlier pass:
|
|
145
|
+
```bash
|
|
146
|
+
devrites-engine analyze; echo "final analyze rc=$?"
|
|
147
|
+
```
|
|
148
|
+
Any non-zero result blocks the handoff. Then update `state.md`:
|
|
144
149
|
`Phase: vet`, `Next step: /rite-build`; on a blocking pause write the `Awaiting human` block +
|
|
145
150
|
`Status: awaiting_human` before stopping.
|
|
146
151
|
6. **Adversarial verification loop.** Apply the light/full branching contract in
|
|
@@ -119,6 +119,12 @@ Pull these via `Read` when shaping the plan:
|
|
|
119
119
|
as a blocking gate at `/rite-vet`; no file → none declared → nothing to check.)
|
|
120
120
|
6. **Write** `architecture.md`, `plan.md`, `tasks.md`, and `traceability.md`; update
|
|
121
121
|
`state.md` (phase: plan → next `/rite-vet`).
|
|
122
|
+
6a. **Cross-artifact gate — now that `tasks.md` exists.** Run the deterministic
|
|
123
|
+
spec↔tasks coverage/consistency check; any non-zero result blocks plan readiness:
|
|
124
|
+
```bash
|
|
125
|
+
S="$(cat .devrites/ACTIVE 2>/dev/null)"
|
|
126
|
+
devrites-engine analyze "$S"
|
|
127
|
+
```
|
|
122
128
|
7. **Readiness gate** (bottom of plan-template): every acceptance criterion covered by a
|
|
123
129
|
slice, dependency order acyclic + risk-first, no unjustified deviation, rollback for
|
|
124
130
|
every destructive/migration step. **Stop and confirm** before code. Render the review-before-code
|
|
@@ -185,6 +185,8 @@ stay flat `AC-###` bullets; the grammar is opt-in by rigor, never forced.
|
|
|
185
185
|
devrites-engine spec-skeleton ".devrites/work/<slug>"
|
|
186
186
|
devrites-engine spec-validate ".devrites/work/<slug>" --against .devrites/specs
|
|
187
187
|
```
|
|
188
|
+
**Do not run `devrites-engine analyze` in this phase:** `tasks.md` deliberately does not
|
|
189
|
+
exist yet. `/rite-define` owns the first analyze pass after it writes the slices.
|
|
188
190
|
Treat edge/prohibition findings as blocking just like grammar findings. When it passes, write `Spec gate: passed <iso>` to `state.md`.
|
|
189
191
|
6a. **Review-before-code digest.** Before handing off to planning, render the cheap human review:
|
|
190
192
|
`Intent` (one sentence), `Done means` (top acceptance/scenario IDs), `Scope/risk` (what is in/out
|
|
@@ -140,7 +140,12 @@ Audit for a plan resting on unstated beliefs).
|
|
|
140
140
|
(`/rite-build` and `/rite-prove` read it). Harden `plan.md` / `tasks.md` directly for
|
|
141
141
|
behavior-preserving refinements; route every acceptance/behavior-changing delta through the
|
|
142
142
|
**Spec Drift Guard** (`drift.md` + recorded decision + `/rite-plan repair`). Append
|
|
143
|
-
`decisions.md` (one ADR per material call) and `assumptions.md`.
|
|
143
|
+
`decisions.md` (one ADR per material call) and `assumptions.md`. Re-run the gate after
|
|
144
|
+
every fold-back so a task edit cannot invalidate the earlier pass:
|
|
145
|
+
```bash
|
|
146
|
+
devrites-engine analyze; echo "final analyze rc=$?"
|
|
147
|
+
```
|
|
148
|
+
Any non-zero result blocks the handoff. Then update `state.md`:
|
|
144
149
|
`Phase: vet`, `Next step: /rite-build`; on a blocking pause write the `Awaiting human` block +
|
|
145
150
|
`Status: awaiting_human` before stopping.
|
|
146
151
|
6. **Adversarial verification loop.** Apply the light/full branching contract in
|
|
@@ -131,6 +131,12 @@ Pull these via `Read` when shaping the plan:
|
|
|
131
131
|
as a blocking gate at `$rite-vet`; no file → none declared → nothing to check.)
|
|
132
132
|
6. **Write** `architecture.md`, `plan.md`, `tasks.md`, and `traceability.md`; update
|
|
133
133
|
`state.md` (phase: plan → next `$rite-vet`).
|
|
134
|
+
6a. **Cross-artifact gate — now that `tasks.md` exists.** Run the deterministic
|
|
135
|
+
spec↔tasks coverage/consistency check; any non-zero result blocks plan readiness:
|
|
136
|
+
```bash
|
|
137
|
+
S="$(cat .devrites/ACTIVE 2>/dev/null)"
|
|
138
|
+
devrites-engine analyze "$S"
|
|
139
|
+
```
|
|
134
140
|
7. **Readiness gate** (bottom of plan-template): every acceptance criterion covered by a
|
|
135
141
|
slice, dependency order acyclic + risk-first, no unjustified deviation, rollback for
|
|
136
142
|
every destructive/migration step. **Stop and confirm** before code. Render the review-before-code
|
|
@@ -197,6 +197,8 @@ stay flat `AC-###` bullets; the grammar is opt-in by rigor, never forced.
|
|
|
197
197
|
devrites-engine spec-skeleton ".devrites/work/<slug>"
|
|
198
198
|
devrites-engine spec-validate ".devrites/work/<slug>" --against .devrites/specs
|
|
199
199
|
```
|
|
200
|
+
**Do not run `devrites-engine analyze` in this phase:** `tasks.md` deliberately does not
|
|
201
|
+
exist yet. `$rite-define` owns the first analyze pass after it writes the slices.
|
|
200
202
|
Treat edge/prohibition findings as blocking just like grammar findings. When it passes, write `Spec gate: passed <iso>` to `state.md`.
|
|
201
203
|
6a. **Review-before-code digest.** Before handing off to planning, render the cheap human review:
|
|
202
204
|
`Intent` (one sentence), `Done means` (top acceptance/scenario IDs), `Scope/risk` (what is in/out
|
|
@@ -152,7 +152,12 @@ Audit for a plan resting on unstated beliefs).
|
|
|
152
152
|
(`$rite-build` and `$rite-prove` read it). Harden `plan.md` / `tasks.md` directly for
|
|
153
153
|
behavior-preserving refinements; route every acceptance/behavior-changing delta through the
|
|
154
154
|
**Spec Drift Guard** (`drift.md` + recorded decision + `$rite-plan repair`). Append
|
|
155
|
-
`decisions.md` (one ADR per material call) and `assumptions.md`.
|
|
155
|
+
`decisions.md` (one ADR per material call) and `assumptions.md`. Re-run the gate after
|
|
156
|
+
every fold-back so a task edit cannot invalidate the earlier pass:
|
|
157
|
+
```bash
|
|
158
|
+
devrites-engine analyze; echo "final analyze rc=$?"
|
|
159
|
+
```
|
|
160
|
+
Any non-zero result blocks the handoff. Then update `state.md`:
|
|
156
161
|
`Phase: vet`, `Next step: $rite-build`; on a blocking pause write the `Awaiting human` block +
|
|
157
162
|
`Status: awaiting_human` before stopping.
|
|
158
163
|
6. **Adversarial verification loop.** Apply the light/full branching contract in
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devrites",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "DevRites — disciplined senior-engineer workflow skills pack for Claude Code and Codex",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://github.com/ViktorsBaikers/DevRites#readme",
|