devrites 7.0.8 → 7.0.10
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 +12 -0
- package/README.md +1 -1
- package/docs/cli.md +3 -2
- package/docs/engine/commands.md +1 -1
- package/engine/internal/acceptance/approve.go +31 -2
- package/engine/internal/acceptance/preconditions.go +11 -5
- package/engine/internal/lib/apply_bundle.go +14 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
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
|
+
## [7.0.10](https://github.com/ViktorsBaikers/DevRites/compare/v7.0.9...v7.0.10) (2026-10-23)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
* **devrites:** apply-bundle refuses an unrunnable preflight row ([#166](https://github.com/ViktorsBaikers/DevRites/issues/166)) ([6bc06a4](https://github.com/ViktorsBaikers/DevRites/commit/6bc06a40530e3456b80883303e345fb25d45357a))
|
|
10
|
+
|
|
11
|
+
## [7.0.9](https://github.com/ViktorsBaikers/DevRites/compare/v7.0.8...v7.0.9) (2026-10-22)
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
* **devrites:** keep a passed pre-dispatch row on re-dispatch ([#165](https://github.com/ViktorsBaikers/DevRites/issues/165)) ([927f5a4](https://github.com/ViktorsBaikers/DevRites/commit/927f5a482cfb70c84255a9ead467251483de7fee))
|
|
16
|
+
|
|
5
17
|
## [7.0.8](https://github.com/ViktorsBaikers/DevRites/compare/v7.0.7...v7.0.8) (2026-10-22)
|
|
6
18
|
|
|
7
19
|
### Fixed
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ history can't lose it. After you clear the conversation, switch hosts, or hand
|
|
|
31
31
|
the feature to another engineer, the next session reads the same plan,
|
|
32
32
|
decisions, and evidence before it continues.
|
|
33
33
|
|
|
34
|
-
**Status:** [`v7.0.
|
|
34
|
+
**Status:** [`v7.0.10`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v7.0.10): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
|
|
35
35
|
|
|
36
36
|
This is the latest published release; `main` may contain unreleased work.
|
|
37
37
|
|
package/docs/cli.md
CHANGED
|
@@ -128,8 +128,9 @@ self-contained updater.
|
|
|
128
128
|
- `check readiness --emit-binding <slug>` renders the exact stable Build-input
|
|
129
129
|
binding for Vet to record after semantic review.
|
|
130
130
|
- `check preconditions <slug>` runs the preflight rows whose `Run at` is `vet`
|
|
131
|
-
(`--slice <SLICE>` runs that slice's `pre-dispatch` rows
|
|
132
|
-
candidate-bound transcript per row; exit 3 on
|
|
131
|
+
(`--slice <SLICE>` runs that slice's `pre-dispatch` rows, keeping one that
|
|
132
|
+
already passed) and records a candidate-bound transcript per row; exit 3 on
|
|
133
|
+
failure.
|
|
133
134
|
- `check seal <slug>` checks final required files, open human gates, and the
|
|
134
135
|
`tasks.md` slice graph. Once those files are complete, it verifies the stable
|
|
135
136
|
readiness binding; only after that aggregate check passes does it verify that
|
package/docs/engine/commands.md
CHANGED
|
@@ -14,7 +14,7 @@ workflow strategy.
|
|
|
14
14
|
| `check candidate <slug>` | Validate the strict manifest and compute the content-bound project-candidate identity. Exits `3` when the manifest omits a path the feature's `WIP(<slug>):` checkpoint commits changed (net from the feature base to HEAD). Also exits `3` with `unattributed root edit: <paths>` while the feature's `tree-baseline.json` tripwire flags a product path (ADR-0038; see `dispatch`). |
|
|
15
15
|
| `check readiness <slug>` | Check target-Phase files, open human gates from Clarify onward, the `tasks.md` slice graph when that artifact is required, canonical `AC-###` presence in `tasks.md`/`test-plan.md` when those artifacts are required, and the current stable Build-input binding when applicable. Leaving Vet with a `Run at` column, a slice whose `Files likely touched`/`Writer allowlist` names a dependency manifest or lockfile needs a `dependency-dry-run SLICE-###` row at `vet` with a passing transcript. Leaving Vet, every slice `state.md` does not check built (a deferred `Satisfies: none` slice excepted) must also pass the Build-entry `check slice` contract; each failure prints one `invariant: slice-check:` line naming the slice and its exact `check slice` reason, to fix in `/rite-plan` before READY. |
|
|
16
16
|
| `check readiness --emit-binding <slug>` | Render the exact stable Build-input binding for Vet to record after review. |
|
|
17
|
-
| `check preconditions <slug> [--slice SLICE-###] [--timeout N]` | Run the `test-plan.md` Build-entry preflight rows whose optional `Run at` cell is `vet` (with `--slice`, `pre-dispatch <SLICE>`); `action` rows and rows without the cell never run. Writes one candidate-bound transcript per row under `references/gates/preflight-*/`; a row passes on exit 0. Exit `3` on any failure or an invalid `Run at` value. |
|
|
17
|
+
| `check preconditions <slug> [--slice SLICE-###] [--timeout N]` | Run the `test-plan.md` Build-entry preflight rows whose optional `Run at` cell is `vet` (with `--slice`, `pre-dispatch <SLICE>`); `action` rows and rows without the cell never run. Writes one candidate-bound transcript per row under `references/gates/preflight-*/`; a row passes on exit 0. A `pre-dispatch` row whose newest transcript passed is kept, not re-run: it is a pre-edit baseline, so a re-dispatch never runs it against the slice's own edits; changing its command or cwd retires the pass. Exit `3` on any failure or an invalid `Run at` value. |
|
|
18
18
|
| `check seal <slug>` | Check files required by target Phase `seal`, open human gates, the `tasks.md` slice graph, canonical `AC-###` presence when `tasks.md`/`test-plan.md` are required, the stable Build-input binding, and exact candidate bindings. `check readiness` and `check seal` also block a `scripts/` test driver that a tasks.md Writer allowlist plans or touched-files.md lists when no gates.md `CHECK` runs it and no `Driver waiver: <path> — DEC-### <reason>` line cites a recorded decision; waived drivers print as `note: unexecuted driver (waived by DEC-###): <path>` lines, on pass or block. |
|
|
19
19
|
| `check path-disjoint [--root <dir>] [<json-file> | -]` | Verify slice path sets are pairwise disjoint. |
|
|
20
20
|
| `parallel select --cap <1-10> [--root <dir>] [--slug <slug>] [--from-plan \| <json-file> \| -]` | Choose the greedy path-disjoint subset ≤ cap from supplied ready slices, or with `--from-plan` (needs `--slug`) from the plan: tasks.md slices whose dependencies state.md checks `built`, with Writer allowlist (else Files likely touched) paths plus the nearest manifest/lockfile when `External dependencies:` is not none. With `--slug` and a running lease, the lease's not-yet-landed slices hold cap slots and paths, slices it names are never picked, and only new picks print. |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package acceptance
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"fmt"
|
|
4
5
|
"path/filepath"
|
|
5
6
|
"strings"
|
|
6
7
|
|
|
@@ -25,6 +26,9 @@ type PreflightRow struct {
|
|
|
25
26
|
// RunAt is the optional `Run at` cell (`vet`, `pre-dispatch SLICE-###`,
|
|
26
27
|
// `action`), whitespace-collapsed; "" when the row or table has none.
|
|
27
28
|
RunAt string
|
|
29
|
+
// Width and HeaderWidth are the row's and its table header's cell counts;
|
|
30
|
+
// they differ when a bare `|` in a command splits the row.
|
|
31
|
+
Width, HeaderWidth int
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
// ApprovedCommands extracts the approved command surface of test-plan.md: the
|
|
@@ -56,7 +60,7 @@ func PreflightRows(testPlan []byte) (rows []PreflightRow, hasRunAt bool) {
|
|
|
56
60
|
return nil, false
|
|
57
61
|
}
|
|
58
62
|
inSection := false
|
|
59
|
-
commandCol, cwdCol, gateCol, expectedCol, runAtCol := -1, -1, -1, -1, -1
|
|
63
|
+
commandCol, cwdCol, gateCol, expectedCol, runAtCol, headerWidth := -1, -1, -1, -1, -1, 0
|
|
60
64
|
for _, line := range strings.Split(string(structural), "\n") {
|
|
61
65
|
trimmed := strings.TrimSpace(line)
|
|
62
66
|
if strings.HasPrefix(trimmed, "##") {
|
|
@@ -77,6 +81,7 @@ func PreflightRows(testPlan []byte) (rows []PreflightRow, hasRunAt bool) {
|
|
|
77
81
|
gateCol = columnIndex(cells, "gate")
|
|
78
82
|
expectedCol = columnIndex(cells, "expected")
|
|
79
83
|
runAtCol = columnIndex(cells, "run at")
|
|
84
|
+
headerWidth = len(cells)
|
|
80
85
|
hasRunAt = hasRunAt || (commandCol >= 0 && runAtCol >= 0)
|
|
81
86
|
continue
|
|
82
87
|
}
|
|
@@ -87,7 +92,7 @@ func PreflightRows(testPlan []byte) (rows []PreflightRow, hasRunAt bool) {
|
|
|
87
92
|
if command == "" {
|
|
88
93
|
continue
|
|
89
94
|
}
|
|
90
|
-
row := PreflightRow{ApprovedCommand: ApprovedCommand{Command: command}}
|
|
95
|
+
row := PreflightRow{ApprovedCommand: ApprovedCommand{Command: command}, Width: len(cells), HeaderWidth: headerWidth}
|
|
91
96
|
if cwdCol >= 0 && cwdCol < len(cells) {
|
|
92
97
|
row.Cwd = normalizeApprovedCwd(cells[cwdCol])
|
|
93
98
|
}
|
|
@@ -99,6 +104,30 @@ func PreflightRows(testPlan []byte) (rows []PreflightRow, hasRunAt bool) {
|
|
|
99
104
|
return rows, hasRunAt
|
|
100
105
|
}
|
|
101
106
|
|
|
107
|
+
// PreflightShapeProblems names each Build-entry preflight row that cannot run
|
|
108
|
+
// as written: a cell count that differs from its header (an unescaped `|`
|
|
109
|
+
// in the command, which must be written `\|`) or an invalid `Run at` value.
|
|
110
|
+
// `check preconditions` would otherwise find it only when Vet runs the row.
|
|
111
|
+
func PreflightShapeProblems(testPlan []byte) []string {
|
|
112
|
+
rows, _ := PreflightRows(testPlan)
|
|
113
|
+
var problems []string
|
|
114
|
+
for _, row := range rows {
|
|
115
|
+
name := valueOr(row.Gate, row.Command)
|
|
116
|
+
if row.Width != row.HeaderWidth {
|
|
117
|
+
problems = append(problems, fmt.Sprintf("preflight row %q has %d cells but its header has %d; escape each `|` in the command as `\\|`", name, row.Width, row.HeaderWidth))
|
|
118
|
+
} else if !ValidRunAt(row.RunAt) {
|
|
119
|
+
problems = append(problems, fmt.Sprintf("preflight row %q has invalid Run at %q (want vet, pre-dispatch SLICE-###, or action)", name, row.RunAt))
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return problems
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ValidRunAt reports whether a `Run at` cell is empty or a known value.
|
|
126
|
+
func ValidRunAt(runAt string) bool {
|
|
127
|
+
lower := strings.ToLower(runAt)
|
|
128
|
+
return lower == "" || lower == "vet" || lower == "action" || preDispatchRunAtRE.MatchString(runAt)
|
|
129
|
+
}
|
|
130
|
+
|
|
102
131
|
// cellText returns a cell's trimmed text without code backticks, "" when the
|
|
103
132
|
// column is absent or the row is short.
|
|
104
133
|
func cellText(cells []string, col int) string {
|
|
@@ -77,7 +77,9 @@ func PreflightPassed(work string, row PreflightRow) bool {
|
|
|
77
77
|
// RunPreconditions is `check preconditions <slug> [--slice S]`: it executes
|
|
78
78
|
// the Build-entry preflight rows whose `Run at` is `vet` (or, with --slice,
|
|
79
79
|
// `pre-dispatch <S>`), writes one candidate-bound transcript per row, and exits
|
|
80
|
-
// ExitBlocked on any failure.
|
|
80
|
+
// ExitBlocked on any failure. A pre-dispatch row that already passed keeps that
|
|
81
|
+
// transcript: it is a pre-edit baseline, and a repair round's re-dispatch would
|
|
82
|
+
// otherwise re-run it against the slice's own unfinished edits. Rows are the approved surface itself, so nothing
|
|
81
83
|
// outside test-plan.md runs; `action` rows and rows without a `Run at` cell
|
|
82
84
|
// never run here.
|
|
83
85
|
func RunPreconditions(root string, args []string, stdout, stderr io.Writer) int {
|
|
@@ -157,12 +159,11 @@ func RunPreconditions(root string, args []string, stdout, stderr io.Writer) int
|
|
|
157
159
|
// and whether any row ran.
|
|
158
160
|
func runPreflightRows(root, slug, work, project, runAt string, rows []PreflightRow, timeout time.Duration, stdout io.Writer) (bool, bool, error) {
|
|
159
161
|
before := candidateLabel(root, slug)
|
|
160
|
-
failed := false
|
|
162
|
+
failed, kept := false, false
|
|
161
163
|
doc := &Document{}
|
|
162
164
|
var results []GateResult
|
|
163
165
|
for _, row := range rows {
|
|
164
|
-
|
|
165
|
-
if lower != "" && lower != "vet" && lower != "action" && !preDispatchRunAtRE.MatchString(row.RunAt) {
|
|
166
|
+
if !ValidRunAt(row.RunAt) {
|
|
166
167
|
fmt.Fprintf(stdout, "row %s: invalid Run at %q (want vet, pre-dispatch SLICE-###, or action)\n", valueOr(row.Gate, row.Command), row.RunAt)
|
|
167
168
|
failed = true
|
|
168
169
|
continue
|
|
@@ -170,6 +171,11 @@ func runPreflightRows(root, slug, work, project, runAt string, rows []PreflightR
|
|
|
170
171
|
if !strings.EqualFold(row.RunAt, runAt) {
|
|
171
172
|
continue
|
|
172
173
|
}
|
|
174
|
+
if preDispatchRunAtRE.MatchString(runAt) && PreflightPassed(work, row) {
|
|
175
|
+
fmt.Fprintf(stdout, "row %s: pass (kept; a pre-dispatch row runs once, before the slice's first edit)\n", PreflightGateID(row))
|
|
176
|
+
kept = true
|
|
177
|
+
continue
|
|
178
|
+
}
|
|
173
179
|
gate := &Gate{ID: PreflightGateID(row), Title: row.Gate, Check: row.Command, Cwd: row.Cwd,
|
|
174
180
|
Expect: "exit 0 (Expected: " + valueOr(row.Expected, "unstated") + ")"}
|
|
175
181
|
cwd := project
|
|
@@ -197,5 +203,5 @@ func runPreflightRows(root, slug, work, project, runAt string, rows []PreflightR
|
|
|
197
203
|
return failed, true, fmt.Errorf("transcript: %w", err)
|
|
198
204
|
}
|
|
199
205
|
}
|
|
200
|
-
return failed, len(results) > 0, nil
|
|
206
|
+
return failed, kept || len(results) > 0, nil
|
|
201
207
|
}
|
|
@@ -15,6 +15,7 @@ import (
|
|
|
15
15
|
"sort"
|
|
16
16
|
"strings"
|
|
17
17
|
|
|
18
|
+
"github.com/devrites/devrites/internal/acceptance"
|
|
18
19
|
"github.com/devrites/devrites/internal/devritespaths"
|
|
19
20
|
"github.com/devrites/devrites/internal/fsutil"
|
|
20
21
|
"github.com/devrites/devrites/internal/state"
|
|
@@ -360,7 +361,8 @@ func applyHunks(base string, hunks []candidateHunk) (string, []string) {
|
|
|
360
361
|
|
|
361
362
|
// checkBundleResult rejects an outcome the root would otherwise discover only
|
|
362
363
|
// at Vet: a tracked ID that vanished from every touched file without being
|
|
363
|
-
// listed in retired_ids, a new tasks.md graph problem,
|
|
364
|
+
// listed in retired_ids, a new tasks.md graph problem, a new test-plan.md
|
|
365
|
+
// preflight row that cannot run as written, or a budgeted artifact
|
|
364
366
|
// that grew into material overshoot without a covering
|
|
365
367
|
// `Budget override: <=N lines; <reason>` line.
|
|
366
368
|
func checkBundleResult(targets []*bundleTarget, retired map[string]bool) []string {
|
|
@@ -400,6 +402,17 @@ func checkBundleResult(targets []*bundleTarget, retired map[string]bool) []strin
|
|
|
400
402
|
}
|
|
401
403
|
}
|
|
402
404
|
}
|
|
405
|
+
if name == "test-plan.md" && filepath.Dir(target.rel) == "." {
|
|
406
|
+
old := map[string]bool{}
|
|
407
|
+
for _, p := range acceptance.PreflightShapeProblems(target.before) {
|
|
408
|
+
old[p] = true
|
|
409
|
+
}
|
|
410
|
+
for _, p := range acceptance.PreflightShapeProblems(target.after) {
|
|
411
|
+
if !old[p] {
|
|
412
|
+
problems = append(problems, "test-plan.md: "+p)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
403
416
|
if filepath.Dir(target.rel) == "." && currentStatePlanning[name] &&
|
|
404
417
|
len(datedHeadingRE.FindAllIndex(target.after, -1)) > len(datedHeadingRE.FindAllIndex(target.before, -1)) {
|
|
405
418
|
problems = append(problems, target.rel+": adds a dated delta section; rewrite the owning clause in place (rationale to decisions.md, closed narrative to history/)")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devrites",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.10",
|
|
4
4
|
"description": "DevRites: a disciplined senior-engineer workflow pack for Claude Code, Codex, omp, pi, and Devin CLI",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://github.com/ViktorsBaikers/DevRites#readme",
|