devrites 6.0.3 → 6.0.4
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 +4 -4
- package/engine/internal/lib/context.go +11 -5
- package/engine/internal/lib/context_next_test.go +52 -0
- package/engine/internal/lib/dispatch.go +44 -5
- package/engine/internal/lib/metrics.go +74 -1
- package/engine/internal/lib/observe_budgets_test.go +32 -0
- package/engine/internal/lib/observe_summary.go +8 -0
- package/engine/main.go +1 -1
- package/pack/.claude/agents/devrites-slice-wright.md +4 -1
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +3 -1
- package/pack/.claude/skills/rite-build/reference/output.md +1 -1
- package/pack/.claude/skills/rite-build/reference/phase-contract.md +2 -0
- package/pack/generated/claude/agents/devrites-slice-wright.md +4 -1
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +3 -1
- package/pack/generated/claude/skills/rite-build/reference/output.md +1 -1
- package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +2 -0
- package/pack/generated/codex/agents/devrites-slice-wright.toml +4 -1
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +3 -1
- package/pack/generated/codex/skills/rite-build/reference/output.md +1 -1
- package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +2 -0
- package/pack/generated/devin/agents/devrites-slice-wright.md +4 -1
- package/pack/generated/devin/skills/rite-build/reference/afk-discipline.md +3 -1
- package/pack/generated/devin/skills/rite-build/reference/output.md +1 -1
- package/pack/generated/devin/skills/rite-build/reference/phase-contract.md +2 -0
- package/pack/generated/omp/agents/devrites-slice-wright.md +4 -1
- package/pack/generated/omp/skills/rite-build/reference/afk-discipline.md +3 -1
- package/pack/generated/omp/skills/rite-build/reference/output.md +1 -1
- package/pack/generated/omp/skills/rite-build/reference/phase-contract.md +2 -0
- package/pack/generated/pi/agents/devrites-slice-wright.md +4 -1
- package/pack/generated/pi/skills/rite-build/reference/afk-discipline.md +3 -1
- package/pack/generated/pi/skills/rite-build/reference/output.md +1 -1
- package/pack/generated/pi/skills/rite-build/reference/phase-contract.md +2 -0
- 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
|
+
## [6.0.4](https://github.com/ViktorsBaikers/DevRites/compare/v6.0.3...v6.0.4) (2026-10-15)
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
* **devrites:** anchor root bundles, wright self-review, per-slice cost ([#105](https://github.com/ViktorsBaikers/DevRites/issues/105)) ([3569ab3](https://github.com/ViktorsBaikers/DevRites/commit/3569ab3d7c313a05c6371c696df9b2b40971ce2b))
|
|
10
|
+
|
|
5
11
|
## [6.0.3](https://github.com/ViktorsBaikers/DevRites/compare/v6.0.2...v6.0.3) (2026-10-14)
|
|
6
12
|
|
|
7
13
|
### Changed
|
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:** [`v6.0.
|
|
34
|
+
**Status:** [`v6.0.4`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v6.0.4): 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/engine/commands.md
CHANGED
|
@@ -27,15 +27,15 @@ workflow strategy.
|
|
|
27
27
|
| `check diff-scope <slug> --allow <csv>\|--allow-file <path> [--worktree\|--staged\|--base <ref>]` | Verify the changed-path set stays inside the declared allowlist; the mechanical gate run before reviewer dispatch. |
|
|
28
28
|
| `check skill-trust <path>` | Scan one skill/agent Markdown file for structural trust violations. |
|
|
29
29
|
| `check indexes [--root <dir>]` | Report install manifest and code-index presence as JSON. |
|
|
30
|
-
| `observe summary <slug>` | Emit sanitized JSON workspace summary from one retained observation. `task_graph.ok` is true iff `task_graph.problems` is empty; `problems` lists cycle, unknown-dep, malformed-token, duplicate-id, and missing-`Dependencies` blockers. |
|
|
30
|
+
| `observe summary <slug>` | Emit sanitized JSON workspace summary from one retained observation. `task_graph.ok` is true iff `task_graph.problems` is empty; `problems` lists cycle, unknown-dep, malformed-token, duplicate-id, and missing-`Dependencies` blockers. `inflight_waves` lists dispatch waves still `open` or `sealed`, with their pending roles. |
|
|
31
31
|
| `orient <slug>` | Alias for `observe summary`. |
|
|
32
32
|
| `observe slice <slug> <SLICE-ID>` | Print one `SLICE-###` section of `tasks.md` so Build reads a slice, not the file. |
|
|
33
33
|
| `next [slug]` | Print the minimal remaining lifecycle path plus advisory skips. |
|
|
34
34
|
| `handoff [slug]` | Emit the deterministic resume record: cursor fields, `awaiting_human`, open `questions.md` gate kinds, the `gates.md` reduction with unmet/stale/abandoned ids, `decisions.md` dead ends, missing required files, and the canonical read-next order. Read-only; the `/rite-handoff` spine and the compaction-fallback entry point. |
|
|
35
|
-
| `context [slug] (--phase <p>\|--skill <name>) [--role <r>] [--slice <SLICE-ID>] [--trigger a,b]` | Emit one deduplicated read-set bundle per phase/role/skill from each skill's `loads:` manifest; `--skill` works without a workspace when `--out` is given. A `--role` bundle leads with the role's agent contract and names the manifest's `rootOnly` paths instead of inlining them. `--slice` inlines that slice's `tasks.md` block and turns each over-budget artifact into a by-reference index (path, SHA-256, headings, line hits for every ID the block cites); repeat `--slice` for several slices; an unknown slice exits `3`. |
|
|
36
|
-
| `dispatch <slug> <open\|start\|seal\|return\|status\|abandon>` | Launch-wave barrier for parallel dispatch: seal needs a handle per role, return needs seal; start/return auto-record metrics. A build wave with a wright role needs `--slice <SLICE-ID>` per slice, and a slice-bound vet wave takes it too; every 5 rounds of either kind on one slice, `open` exits `3` until `--reslice-review <DRIFT-ID>` names an unused `drift.md` `## <DRIFT-ID>` heading for that slice. |
|
|
35
|
+
| `context [slug] (--phase <p>\|--skill <name>) [--role <r>] [--slice <SLICE-ID>] [--trigger a,b]` | Emit one deduplicated read-set bundle per phase/role/skill from each skill's `loads:` manifest; `--skill` works without a workspace when `--out` is given. A bundle without `--role` (the root's own read) gives over-budget artifacts as by-reference indexes; a `--role` bundle leads with the role's agent contract and names the manifest's `rootOnly` paths instead of inlining them. `--slice` inlines that slice's `tasks.md` block and turns each over-budget artifact into a by-reference index (path, SHA-256, headings, line hits for every ID the block cites); repeat `--slice` for several slices; an unknown slice exits `3`. |
|
|
36
|
+
| `dispatch <slug> <open\|start\|seal\|return\|status\|abandon>` | Launch-wave barrier for parallel dispatch: seal needs a handle per role, return needs seal; start/return auto-record metrics. A build wave with a wright role needs `--slice <SLICE-ID>` per slice, and a slice-bound vet wave takes it too; every 5 rounds of either kind on one slice, `open` exits `3` until `--reslice-review <DRIFT-ID>` names an unused `drift.md` `## <DRIFT-ID>` heading for that slice. `open` also warns when an earlier wave for the same slice never returned. |
|
|
37
37
|
| `metrics record <slug> --phase <p> --event <e> [--role r] [--bytes n] [--note s]` | Append an event to the workspace's `metrics.jsonl` ledger. |
|
|
38
|
-
| `metrics summary [slug]` | Reduce `metrics.jsonl` into a per-phase roll-up. |
|
|
38
|
+
| `metrics summary [slug] [--by-slice]` | Reduce `metrics.jsonl` into a per-phase roll-up. `--by-slice` reads `dispatch.json` instead: per slice, build wright rounds, slice-bound Vet rechecks, and agent-minutes (role start to `returned_at`; abandoned waves excluded). |
|
|
39
39
|
| `claim <add\|release\|list\|check>` | Advisory session-scoped file claims in `.devrites/claims.jsonl` (append-only). `add` records intent to write a path set with a TTL (1–240 min, default 30); a live claim by another session exits `3` and names the holder. `release` appends a release event owned by the claiming session. `list` shows live claims (`--all` includes expired/released). `check` is the read-only preflight for a path set. Coordination aid only — never a substitute for one-writer-per-worktree. |
|
|
40
40
|
| `note <add\|list\|check\|rm> <slug> ...` | Anchored workspace notes in `notes.md`: `add <slug> <subject> <quote> <title> [body]` binds a note to a verbatim quote in one repository file and assigns `NOTE-###`; `list` and `check` regrade each anchor `exact`/`moved`/`stale`/`ambiguous`/`lost` by searching the repo for the quote (dependency and workspace trees excluded); `check --repair` rewrites a `moved` subject to its new file; `rm` deletes by ID. `check seal` refuses a malformed `notes.md` and any non-`exact` anchor. |
|
|
41
41
|
| `gates <scaffold\|status\|run\|reverify\|lint\|attest\|abandon> <slug>` | Operate the `gates.md` acceptance ledger: seed per-AC gates, execute runnable gates bound to `test-plan.md` preflight rows, attest manual gates, and reduce to `all-met`/`not-met`/`handoff`/`malformed`. |
|
|
@@ -204,7 +204,10 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
204
204
|
}
|
|
205
205
|
for _, rel := range workspaceList {
|
|
206
206
|
abs := filepath.Join(featureDir, filepath.FromSlash(rel))
|
|
207
|
-
|
|
207
|
+
// Role bundles without --slice (full Vet, Review, Seal) read
|
|
208
|
+
// everything; root and --slice bundles take over-budget
|
|
209
|
+
// artifacts by anchor.
|
|
210
|
+
if opts.slice == "" && opts.role != "" {
|
|
208
211
|
if writeContextEntry(&bundle, abs, "workspace/"+rel, seen, false) {
|
|
209
212
|
loaded++
|
|
210
213
|
}
|
|
@@ -269,6 +272,8 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
269
272
|
fmt.Fprintln(stdout, ")")
|
|
270
273
|
if opts.slice != "" {
|
|
271
274
|
fmt.Fprintf(stdout, "scope: slice %s (%d whole, %d by-reference)\n", opts.slice, whole, byReference)
|
|
275
|
+
} else if byReference > 0 {
|
|
276
|
+
fmt.Fprintf(stdout, "scope: root (%d whole, %d by-reference over budget)\n", whole, byReference)
|
|
272
277
|
}
|
|
273
278
|
if workspaceScoped {
|
|
274
279
|
fmt.Fprintf(stdout, "workspace: role-scoped for %s (%d of %d declared artifacts)\n",
|
|
@@ -334,16 +339,17 @@ func expandIncludes(abs string, data []byte) []byte {
|
|
|
334
339
|
})
|
|
335
340
|
}
|
|
336
341
|
|
|
337
|
-
// writeSliceEntry writes one workspace artifact of a --slice bundle:
|
|
338
|
-
// as the slice block plus a by-reference index
|
|
339
|
-
// over its line/byte budget as a by-reference index
|
|
342
|
+
// writeSliceEntry writes one workspace artifact of a --slice or root bundle:
|
|
343
|
+
// with a slice, tasks.md as the slice block plus a by-reference index; any
|
|
344
|
+
// canonical artifact over its line/byte budget as a by-reference index;
|
|
345
|
+
// everything else whole.
|
|
340
346
|
func writeSliceEntry(bundle *strings.Builder, project, abs, label, slice, block string, ids []citedID, seen map[string]bool) (loaded, byRef bool) {
|
|
341
347
|
if seen[abs] {
|
|
342
348
|
return false, false
|
|
343
349
|
}
|
|
344
350
|
data, err := os.ReadFile(abs) // #nosec G304 -- abs is resolved from the validated feature workspace
|
|
345
351
|
name := filepath.Base(abs)
|
|
346
|
-
if err == nil && name == "tasks.md" {
|
|
352
|
+
if err == nil && name == "tasks.md" && slice != "" {
|
|
347
353
|
seen[abs] = true
|
|
348
354
|
fmt.Fprintf(bundle, "===== %s (%s block) =====\n%s\n", label, slice, block)
|
|
349
355
|
} else if status, ok := state.ArtifactBudget(name, data); err != nil || !ok || !status.Over {
|
|
@@ -858,3 +858,55 @@ func TestContextRepeatedSliceUnion(t *testing.T) {
|
|
|
858
858
|
t.Fatalf("union of slice blocks wrong:\n%s", text)
|
|
859
859
|
}
|
|
860
860
|
}
|
|
861
|
+
|
|
862
|
+
func TestContextRootBundleAnchorsOverBudgetArtifacts(t *testing.T) {
|
|
863
|
+
root, featureDir := newWorkspace(t, "build")
|
|
864
|
+
skills := newSkillsRoot(t)
|
|
865
|
+
writeFile(t, filepath.Join(skills, "rite-build", "SKILL.md"), "---\nname: rite-build\n---\n<!-- loads: {\"always\":[\"devrites-lib/reference/standards/core.md\"],\"workspace\":[\"decisions.md\",\"spec.md\"]} -->\n# build\n")
|
|
866
|
+
writeFile(t, filepath.Join(featureDir, "decisions.md"), "# Decisions\n"+strings.Repeat("filler decision row\n", 250)+"## DEC-010 Use the store\n")
|
|
867
|
+
writeFile(t, filepath.Join(featureDir, "spec.md"), "# spec\nsmall spec body\n")
|
|
868
|
+
run := func(args ...string) string {
|
|
869
|
+
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
|
|
870
|
+
if code := RunContext(root, append([]string{"feat", "--phase", "build", "--skills-root", skills}, args...), stdout, stderr); code != 0 {
|
|
871
|
+
t.Fatalf("code=%d\n%s%s", code, stdout, stderr)
|
|
872
|
+
}
|
|
873
|
+
return stdout.String()
|
|
874
|
+
}
|
|
875
|
+
out := run()
|
|
876
|
+
if !strings.Contains(out, "scope: root (1 whole, 1 by-reference over budget)") {
|
|
877
|
+
t.Fatalf("root scope line missing:\n%s", out)
|
|
878
|
+
}
|
|
879
|
+
rootBundle, _ := os.ReadFile(filepath.Join(featureDir, "ctx", "build.bundle.md"))
|
|
880
|
+
if text := string(rootBundle); !strings.Contains(text, "workspace/decisions.md (by reference)") || strings.Contains(text, "filler decision row") || !strings.Contains(text, "small spec body") {
|
|
881
|
+
t.Fatalf("root bundle must anchor only the over-budget artifact:\n%s", text)
|
|
882
|
+
}
|
|
883
|
+
run("--role", "code-reviewer")
|
|
884
|
+
roleBundle, _ := os.ReadFile(filepath.Join(featureDir, "ctx", "build-code-reviewer.bundle.md"))
|
|
885
|
+
if !strings.Contains(string(roleBundle), "filler decision row") {
|
|
886
|
+
t.Fatalf("a role bundle without --slice must stay whole:\n%s", roleBundle)
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
func TestMetricsSummaryBySlice(t *testing.T) {
|
|
891
|
+
root, featureDir := newWorkspace(t, "build")
|
|
892
|
+
writeFile(t, filepath.Join(featureDir, "dispatch.json"), `{"waves":{
|
|
893
|
+
"w1":{"phase":"build","state":"complete","slices":["SLICE-001"],"roles":{"slice-wright":{"handle":"a","started":"2026-10-15T10:00:00Z","returned":true,"returned_at":"2026-10-15T10:30:00Z"}}},
|
|
894
|
+
"w2":{"phase":"build","state":"complete","slices":["SLICE-001","SLICE-002"],"roles":{"slice-wright":{"handle":"b","started":"2026-10-15T11:00:00Z","returned":true,"returned_at":"2026-10-15T11:20:00Z"}}},
|
|
895
|
+
"w3":{"phase":"build","state":"abandoned","slices":["SLICE-001"],"roles":{"slice-wright":{"handle":"c"}}},
|
|
896
|
+
"v1":{"phase":"vet","state":"complete","slices":["SLICE-002"],"roles":{"plan-reviewer":{"handle":"d","started":"2026-10-15T12:00:00Z","returned":true}}},
|
|
897
|
+
"f1":{"phase":"vet","state":"complete","roles":{"plan-reviewer":{"handle":"e"}}}
|
|
898
|
+
}}`)
|
|
899
|
+
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
|
|
900
|
+
if code := RunMetrics(root, []string{"summary", "feat", "--by-slice"}, stdout, stderr); code != 0 {
|
|
901
|
+
t.Fatalf("code=%d\n%s", code, stderr)
|
|
902
|
+
}
|
|
903
|
+
out := stdout.String()
|
|
904
|
+
for _, want := range []string{
|
|
905
|
+
"metrics: SLICE-001 wright_rounds=2 vet_rechecks=0 agent_minutes=40\n",
|
|
906
|
+
"metrics: SLICE-002 wright_rounds=1 vet_rechecks=1 agent_minutes=10\n",
|
|
907
|
+
} {
|
|
908
|
+
if !strings.Contains(out, want) {
|
|
909
|
+
t.Fatalf("missing %q:\n%s", want, out)
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
@@ -56,9 +56,10 @@ type dispatchWave struct {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
type dispatchRole struct {
|
|
59
|
-
Handle
|
|
60
|
-
StartedAt
|
|
61
|
-
Returned
|
|
59
|
+
Handle string `json:"handle,omitempty"`
|
|
60
|
+
StartedAt string `json:"started,omitempty"`
|
|
61
|
+
Returned bool `json:"returned,omitempty"`
|
|
62
|
+
ReturnedAt string `json:"returned_at,omitempty"`
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
func dispatchPath(root, slug string) string {
|
|
@@ -270,7 +271,7 @@ func dispatchLocked(root, slug, sub, phase, wave, role, handle, reason string, r
|
|
|
270
271
|
if r.Returned {
|
|
271
272
|
return fail("role %q already returned", role)
|
|
272
273
|
}
|
|
273
|
-
r.Returned = true
|
|
274
|
+
r.Returned, r.ReturnedAt = true, nowStamp()
|
|
274
275
|
all := true
|
|
275
276
|
for _, o := range w.Roles {
|
|
276
277
|
if !o.Returned {
|
|
@@ -400,12 +401,16 @@ func admitWrightRounds(root, slug string, f *dispatchFile, w *dispatchWave, revi
|
|
|
400
401
|
var lines, overCap []string
|
|
401
402
|
for _, id := range w.Slices {
|
|
402
403
|
prior, reviews := 0, 0
|
|
403
|
-
|
|
404
|
+
var unreturned []string
|
|
405
|
+
for name, other := range f.Waves {
|
|
404
406
|
if other.State == "abandoned" || roundKind(other) != kind {
|
|
405
407
|
continue
|
|
406
408
|
}
|
|
407
409
|
if slices.Contains(other.Slices, id) {
|
|
408
410
|
prior++
|
|
411
|
+
if other.State == "open" || other.State == "sealed" {
|
|
412
|
+
unreturned = append(unreturned, name)
|
|
413
|
+
}
|
|
409
414
|
}
|
|
410
415
|
if slices.Contains(other.ResliceReviewed, id) {
|
|
411
416
|
reviews++
|
|
@@ -416,6 +421,10 @@ func admitWrightRounds(root, slug string, f *dispatchFile, w *dispatchWave, revi
|
|
|
416
421
|
overCap = append(overCap, id)
|
|
417
422
|
}
|
|
418
423
|
lines = append(lines, fmt.Sprintf("dispatch: %s %s round %d (reslice review every %d)", id, kind, round, wrightRoundsPerReview))
|
|
424
|
+
if len(unreturned) > 0 {
|
|
425
|
+
sort.Strings(unreturned)
|
|
426
|
+
lines = append(lines, fmt.Sprintf("dispatch: WARNING: %s earlier wave(s) %s never returned; record `dispatch return` or `dispatch abandon --reason`", id, strings.Join(unreturned, ",")))
|
|
427
|
+
}
|
|
419
428
|
}
|
|
420
429
|
if len(overCap) == 0 {
|
|
421
430
|
if review != "" {
|
|
@@ -457,6 +466,36 @@ func driftHeading(drift, id string) string {
|
|
|
457
466
|
return re.FindString(drift)
|
|
458
467
|
}
|
|
459
468
|
|
|
469
|
+
// InflightWave is a dispatch wave whose agents have not all returned.
|
|
470
|
+
type InflightWave struct {
|
|
471
|
+
Wave string `json:"wave"`
|
|
472
|
+
Phase string `json:"phase"`
|
|
473
|
+
State string `json:"state"`
|
|
474
|
+
Pending []string `json:"pending"`
|
|
475
|
+
Slices []string `json:"slices,omitempty"`
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// inflightWaves lists open or sealed waves with the roles still awaiting a
|
|
479
|
+
// return, sorted by wave name.
|
|
480
|
+
func inflightWaves(f *dispatchFile) []InflightWave {
|
|
481
|
+
var out []InflightWave
|
|
482
|
+
for name, w := range f.Waves {
|
|
483
|
+
if w.State != "open" && w.State != "sealed" {
|
|
484
|
+
continue
|
|
485
|
+
}
|
|
486
|
+
var pending []string
|
|
487
|
+
for role, r := range w.Roles {
|
|
488
|
+
if !r.Returned {
|
|
489
|
+
pending = append(pending, role)
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
sort.Strings(pending)
|
|
493
|
+
out = append(out, InflightWave{Wave: name, Phase: w.Phase, State: w.State, Pending: pending, Slices: w.Slices})
|
|
494
|
+
}
|
|
495
|
+
sort.Slice(out, func(i, j int) bool { return out[i].Wave < out[j].Wave })
|
|
496
|
+
return out
|
|
497
|
+
}
|
|
498
|
+
|
|
460
499
|
func waveState(w *dispatchWave) string {
|
|
461
500
|
if w == nil {
|
|
462
501
|
return "absent"
|
|
@@ -7,6 +7,7 @@ import (
|
|
|
7
7
|
"io"
|
|
8
8
|
"os"
|
|
9
9
|
"path/filepath"
|
|
10
|
+
"slices"
|
|
10
11
|
"sort"
|
|
11
12
|
"strconv"
|
|
12
13
|
"strings"
|
|
@@ -23,7 +24,7 @@ const MetricsFile = "metrics.jsonl"
|
|
|
23
24
|
const metricsUsage = `usage: devrites-engine metrics <record|summary> ...
|
|
24
25
|
|
|
25
26
|
metrics record <slug> --phase <p> --event <e> [--role r] [--bytes n] [--note s]
|
|
26
|
-
metrics summary [slug]
|
|
27
|
+
metrics summary [slug] [--by-slice]
|
|
27
28
|
`
|
|
28
29
|
|
|
29
30
|
type metricEvent struct {
|
|
@@ -150,6 +151,8 @@ func recordMetric(root, slug, phase, event, role string, bytes int64) {
|
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
func metricsSummary(root string, args []string, stdout, stderr io.Writer) int {
|
|
154
|
+
bySlice := slices.Contains(args, "--by-slice")
|
|
155
|
+
args = slices.DeleteFunc(slices.Clone(args), func(a string) bool { return a == "--by-slice" })
|
|
153
156
|
slug, code, err := ActiveSlug(root, args)
|
|
154
157
|
if err != nil {
|
|
155
158
|
fmt.Fprintf(stderr, "metrics: %v\n", err)
|
|
@@ -163,6 +166,9 @@ func metricsSummary(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
163
166
|
fmt.Fprintf(stderr, "metrics: %v\n", err)
|
|
164
167
|
return 2
|
|
165
168
|
}
|
|
169
|
+
if bySlice {
|
|
170
|
+
return sliceCostSummary(root, slug, stdout, stderr)
|
|
171
|
+
}
|
|
166
172
|
f, err := os.Open(filepath.Join(dir, MetricsFile)) // #nosec G304 -- dir is a validated workspace and filename is fixed
|
|
167
173
|
if err != nil {
|
|
168
174
|
fmt.Fprintf(stdout, "metrics: no events recorded for %s\n", slug)
|
|
@@ -258,3 +264,70 @@ func metricsSummary(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
258
264
|
fmt.Fprintln(stdout)
|
|
259
265
|
return 0
|
|
260
266
|
}
|
|
267
|
+
|
|
268
|
+
// sliceCostSummary reports, per slice named on dispatch waves, the build wright
|
|
269
|
+
// rounds, slice-bound Vet rechecks, and agent-minutes (role start to return; a
|
|
270
|
+
// wave naming several slices splits its minutes evenly). Abandoned waves are
|
|
271
|
+
// excluded, and waves recorded before returns were timestamped count rounds only.
|
|
272
|
+
func sliceCostSummary(root, slug string, stdout, stderr io.Writer) int {
|
|
273
|
+
f, _, err := loadDispatch(root, slug)
|
|
274
|
+
if err != nil {
|
|
275
|
+
fmt.Fprintf(stderr, "metrics: %v\n", err)
|
|
276
|
+
return 2
|
|
277
|
+
}
|
|
278
|
+
type cost struct {
|
|
279
|
+
wright, vet int
|
|
280
|
+
minutes float64
|
|
281
|
+
timed bool
|
|
282
|
+
}
|
|
283
|
+
per := map[string]*cost{}
|
|
284
|
+
for _, w := range f.Waves {
|
|
285
|
+
kind := roundKind(w)
|
|
286
|
+
if kind == "" || w.State == "abandoned" {
|
|
287
|
+
continue
|
|
288
|
+
}
|
|
289
|
+
minutes, timed := 0.0, false
|
|
290
|
+
for _, r := range w.Roles {
|
|
291
|
+
start, startErr := time.Parse(time.RFC3339, r.StartedAt)
|
|
292
|
+
end, endErr := time.Parse(time.RFC3339, r.ReturnedAt)
|
|
293
|
+
if startErr == nil && endErr == nil && !end.Before(start) {
|
|
294
|
+
minutes += end.Sub(start).Minutes()
|
|
295
|
+
timed = true
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
for _, id := range w.Slices {
|
|
299
|
+
c := per[id]
|
|
300
|
+
if c == nil {
|
|
301
|
+
c = &cost{}
|
|
302
|
+
per[id] = c
|
|
303
|
+
}
|
|
304
|
+
if kind == "wright" {
|
|
305
|
+
c.wright++
|
|
306
|
+
} else {
|
|
307
|
+
c.vet++
|
|
308
|
+
}
|
|
309
|
+
if timed {
|
|
310
|
+
c.minutes += minutes / float64(len(w.Slices))
|
|
311
|
+
c.timed = true
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if len(per) == 0 {
|
|
316
|
+
fmt.Fprintf(stdout, "metrics: no slice-tagged dispatch waves for %s\n", slug)
|
|
317
|
+
return 0
|
|
318
|
+
}
|
|
319
|
+
ids := make([]string, 0, len(per))
|
|
320
|
+
for id := range per {
|
|
321
|
+
ids = append(ids, id)
|
|
322
|
+
}
|
|
323
|
+
sort.Strings(ids)
|
|
324
|
+
for _, id := range ids {
|
|
325
|
+
c := per[id]
|
|
326
|
+
minutes := "n/a"
|
|
327
|
+
if c.timed {
|
|
328
|
+
minutes = strconv.FormatFloat(c.minutes, 'f', 0, 64)
|
|
329
|
+
}
|
|
330
|
+
fmt.Fprintf(stdout, "metrics: %s wright_rounds=%d vet_rechecks=%d agent_minutes=%s\n", id, c.wright, c.vet, minutes)
|
|
331
|
+
}
|
|
332
|
+
return 0
|
|
333
|
+
}
|
|
@@ -394,3 +394,35 @@ func TestObserveArtifactBudgetsOversizedArtifactIsOverWithoutFullRead(t *testing
|
|
|
394
394
|
t.Fatalf("allocated %d bytes for a %d byte artifact; the read must be bounded", allocated, size)
|
|
395
395
|
}
|
|
396
396
|
}
|
|
397
|
+
|
|
398
|
+
func TestUnreturnedWavesWarnAndSurfaceInOrient(t *testing.T) {
|
|
399
|
+
root, _ := writeBudgetWorkspace(t, map[string]string{
|
|
400
|
+
"state.md": "| phase | build |\n| schema | 4 |\n",
|
|
401
|
+
"tasks.md": budgetTasks,
|
|
402
|
+
})
|
|
403
|
+
open := func(wave string) string {
|
|
404
|
+
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
|
|
405
|
+
args := []string{"feature", "open", "--phase", "build", "--wave", wave, "--role", "slice-wright", "--slice", "SLICE-001"}
|
|
406
|
+
if code := RunDispatch(root, args, stdout, stderr); code != 0 {
|
|
407
|
+
t.Fatalf("open %s: code=%d\n%s%s", wave, code, stdout, stderr)
|
|
408
|
+
}
|
|
409
|
+
return stdout.String()
|
|
410
|
+
}
|
|
411
|
+
if out := open("r1"); strings.Contains(out, "WARNING") {
|
|
412
|
+
t.Fatalf("first wave must not warn:\n%s", out)
|
|
413
|
+
}
|
|
414
|
+
if out := open("r2"); !strings.Contains(out, "WARNING: SLICE-001 earlier wave(s) r1 never returned") {
|
|
415
|
+
t.Fatalf("unreturned earlier wave not warned:\n%s", out)
|
|
416
|
+
}
|
|
417
|
+
summary, err := ObserveSummaryFor(root, "feature")
|
|
418
|
+
if err != nil {
|
|
419
|
+
t.Fatal(err)
|
|
420
|
+
}
|
|
421
|
+
if len(summary.InflightWaves) != 2 {
|
|
422
|
+
t.Fatalf("inflight waves=%+v", summary.InflightWaves)
|
|
423
|
+
}
|
|
424
|
+
first := summary.InflightWaves[0]
|
|
425
|
+
if first.Wave != "r1" || first.State != "open" || len(first.Pending) != 1 || first.Pending[0] != "slice-wright" || first.Slices[0] != "SLICE-001" {
|
|
426
|
+
t.Fatalf("inflight wave=%+v", first)
|
|
427
|
+
}
|
|
428
|
+
}
|
|
@@ -51,6 +51,7 @@ type ObserveSummary struct {
|
|
|
51
51
|
ArtifactBudgets []ArtifactBudget `json:"artifact_budgets,omitempty"`
|
|
52
52
|
BulkFiles []BulkFile `json:"bulk_files,omitempty"`
|
|
53
53
|
Sequence *ObserveSequenceCursor `json:"sequence,omitempty"`
|
|
54
|
+
InflightWaves []InflightWave `json:"inflight_waves,omitempty"`
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
// ObserveSummaryFor builds a summary for one feature slug.
|
|
@@ -91,6 +92,13 @@ func ObserveSummaryFor(root, slug string) (ObserveSummary, error) {
|
|
|
91
92
|
OK: len(graph.Problems) == 0,
|
|
92
93
|
}
|
|
93
94
|
}
|
|
95
|
+
// Open or sealed waves are agents whose results the root never recorded;
|
|
96
|
+
// a resume reconciles them before starting new work.
|
|
97
|
+
if waves, _, err := loadDispatch(root, slug); err == nil {
|
|
98
|
+
summary.InflightWaves = inflightWaves(waves)
|
|
99
|
+
} else {
|
|
100
|
+
summary.Unreadable = append(summary.Unreadable, "dispatch.json")
|
|
101
|
+
}
|
|
94
102
|
if report.SequenceParent != "" || report.SequencePosition != "" || report.SequenceWorkspacesRemaining != "" || report.SequenceRole != "" {
|
|
95
103
|
summary.Sequence = &ObserveSequenceCursor{
|
|
96
104
|
Parent: report.SequenceParent,
|
package/engine/main.go
CHANGED
|
@@ -51,7 +51,7 @@ Usage:
|
|
|
51
51
|
devrites-engine context [slug] (--phase <p> | --skill <name>) [--role <r>] [--slice <id>] [--trigger a,b]
|
|
52
52
|
Emit one deduplicated read-set bundle for a phase, skill, or dispatch role
|
|
53
53
|
devrites-engine metrics record <slug> --phase <p> --event <e> [--role r] [--bytes n] [--note s]
|
|
54
|
-
devrites-engine metrics summary [slug]
|
|
54
|
+
devrites-engine metrics summary [slug] [--by-slice] Summarize the metrics ledger, or per-slice rounds and agent-minutes
|
|
55
55
|
devrites-engine dispatch <slug> <sub> Launch-wave barrier for parallel dispatch: open/start/seal/return/status/abandon (build wright waves: --slice, round cap)
|
|
56
56
|
devrites-engine claim <add|release|list|check> Advisory session-scoped file claims for same-tree concurrent sessions (.devrites/claims.jsonl)
|
|
57
57
|
devrites-engine note <subcommand> <slug> Anchored workspace notes bound to verbatim code quotes; drift regrades them (notes.md)
|
|
@@ -120,7 +120,10 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
|
|
|
120
120
|
new fingerprint. At the limit return the gate and repro. `Escalation` is only for product/irreversible choices
|
|
121
121
|
or human-only access; technical failure is a blocker.
|
|
122
122
|
Before handoff, inspect your cumulative slice diff against the contract and
|
|
123
|
-
scenario matrix, including correction-affected adjacent variants
|
|
123
|
+
scenario matrix, including correction-affected adjacent variants, then against
|
|
124
|
+
the reviewers' own checks: [code review § What to check](../skills/devrites-lib/reference/standards/code-review.md#what-to-check-tests-first)
|
|
125
|
+
and test analysis (each acceptance criterion has a test that fails without the
|
|
126
|
+
change; no assertion-free, tautological, or mock-asserting test). Fix supported
|
|
124
127
|
in-scope gaps together, rerun affected proof, and report remaining gaps. Self-check
|
|
125
128
|
never replaces independent review; green tests do not prove all possible cases.
|
|
126
129
|
5. **TRANSFER when isolated.** After green writer-safe proof, verify the diff again,
|
|
@@ -9,7 +9,9 @@ contract for the sentinel schema, defaults, gate ceiling, and mutable-counter
|
|
|
9
9
|
ownership. This file owns only Build's dispatch, charging, and red-path behavior.
|
|
10
10
|
|
|
11
11
|
Rules: green before `built`; hard `max_slices` cap; gates before the action they
|
|
12
|
-
control; irreversible work (destructive/auth/public API) always pauses
|
|
12
|
+
control; irreversible work (destructive/auth/public API) always pauses; a tick
|
|
13
|
+
never ends with an open or sealed wave — wait for its returns or
|
|
14
|
+
`dispatch abandon --reason`.
|
|
13
15
|
|
|
14
16
|
## Iteration cap
|
|
15
17
|
|
|
@@ -9,7 +9,7 @@ Evidence: acceptance met; tests <cmd> → pass; browser <summary|n/a>; drift <no
|
|
|
9
9
|
Open: <none|blockers|questions>
|
|
10
10
|
Next: /rite-build
|
|
11
11
|
Record: .devrites/work/<slug>/evidence.md
|
|
12
|
-
↻ Hygiene: /clear between slices; /rite-handoff if away > a few hours
|
|
12
|
+
↻ Hygiene: /clear between slices (none in `orient` `inflight_waves`); /rite-handoff if away > a few hours
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
**All built:**
|
|
@@ -14,6 +14,8 @@ Spec Drift Guard still applies.
|
|
|
14
14
|
`Implementation readiness: READY`, current `Readiness inputs SHA-256`, exact
|
|
15
15
|
required accounts, ID/meaning traceability and green proof preflight.
|
|
16
16
|
Run `devrites-engine check readiness <slug>`; any miss/nonzero blocks.
|
|
17
|
+
Non-empty `orient` `inflight_waves` is reconciled first: `dispatch return` each
|
|
18
|
+
role whose account is in hand, else `dispatch abandon --reason` and re-dispatch.
|
|
17
19
|
2. **Select.** Pick the lowest pending slice whose `depends_on` are all built, using
|
|
18
20
|
`orient` `task_graph.slices` plus the `state.md` built list — never by scanning
|
|
19
21
|
`tasks.md`; then `observe slice` it. Restate its goal, ACs, exclusions and exact paths. Include
|
|
@@ -120,7 +120,10 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
|
|
|
120
120
|
new fingerprint. At the limit return the gate and repro. `Escalation` is only for product/irreversible choices
|
|
121
121
|
or human-only access; technical failure is a blocker.
|
|
122
122
|
Before handoff, inspect your cumulative slice diff against the contract and
|
|
123
|
-
scenario matrix, including correction-affected adjacent variants
|
|
123
|
+
scenario matrix, including correction-affected adjacent variants, then against
|
|
124
|
+
the reviewers' own checks: [code review § What to check](../skills/devrites-lib/reference/standards/code-review.md#what-to-check-tests-first)
|
|
125
|
+
and test analysis (each acceptance criterion has a test that fails without the
|
|
126
|
+
change; no assertion-free, tautological, or mock-asserting test). Fix supported
|
|
124
127
|
in-scope gaps together, rerun affected proof, and report remaining gaps. Self-check
|
|
125
128
|
never replaces independent review; green tests do not prove all possible cases.
|
|
126
129
|
5. **TRANSFER when isolated.** After green writer-safe proof, verify the diff again,
|
|
@@ -9,7 +9,9 @@ contract for the sentinel schema, defaults, gate ceiling, and mutable-counter
|
|
|
9
9
|
ownership. This file owns only Build's dispatch, charging, and red-path behavior.
|
|
10
10
|
|
|
11
11
|
Rules: green before `built`; hard `max_slices` cap; gates before the action they
|
|
12
|
-
control; irreversible work (destructive/auth/public API) always pauses
|
|
12
|
+
control; irreversible work (destructive/auth/public API) always pauses; a tick
|
|
13
|
+
never ends with an open or sealed wave — wait for its returns or
|
|
14
|
+
`dispatch abandon --reason`.
|
|
13
15
|
|
|
14
16
|
## Iteration cap
|
|
15
17
|
|
|
@@ -9,7 +9,7 @@ Evidence: acceptance met; tests <cmd> → pass; browser <summary|n/a>; drift <no
|
|
|
9
9
|
Open: <none|blockers|questions>
|
|
10
10
|
Next: /rite-build
|
|
11
11
|
Record: .devrites/work/<slug>/evidence.md
|
|
12
|
-
↻ Hygiene: /clear between slices; /rite-handoff if away > a few hours
|
|
12
|
+
↻ Hygiene: /clear between slices (none in `orient` `inflight_waves`); /rite-handoff if away > a few hours
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
**All built:**
|
|
@@ -14,6 +14,8 @@ Spec Drift Guard still applies.
|
|
|
14
14
|
`Implementation readiness: READY`, current `Readiness inputs SHA-256`, exact
|
|
15
15
|
required accounts, ID/meaning traceability and green proof preflight.
|
|
16
16
|
Run `devrites-engine check readiness <slug>`; any miss/nonzero blocks.
|
|
17
|
+
Non-empty `orient` `inflight_waves` is reconciled first: `dispatch return` each
|
|
18
|
+
role whose account is in hand, else `dispatch abandon --reason` and re-dispatch.
|
|
17
19
|
2. **Select.** Pick the lowest pending slice whose `depends_on` are all built, using
|
|
18
20
|
`orient` `task_graph.slices` plus the `state.md` built list — never by scanning
|
|
19
21
|
`tasks.md`; then `observe slice` it. Restate its goal, ACs, exclusions and exact paths. Include
|
|
@@ -118,7 +118,10 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
|
|
|
118
118
|
new fingerprint. At the limit return the gate and repro. `Escalation` is only for product/irreversible choices
|
|
119
119
|
or human-only access; technical failure is a blocker.
|
|
120
120
|
Before handoff, inspect your cumulative slice diff against the contract and
|
|
121
|
-
scenario matrix, including correction-affected adjacent variants
|
|
121
|
+
scenario matrix, including correction-affected adjacent variants, then against
|
|
122
|
+
the reviewers' own checks: [code review § What to check](../skills/devrites-lib/reference/standards/code-review.md#what-to-check-tests-first)
|
|
123
|
+
and test analysis (each acceptance criterion has a test that fails without the
|
|
124
|
+
change; no assertion-free, tautological, or mock-asserting test). Fix supported
|
|
122
125
|
in-scope gaps together, rerun affected proof, and report remaining gaps. Self-check
|
|
123
126
|
never replaces independent review; green tests do not prove all possible cases.
|
|
124
127
|
5. **TRANSFER when isolated.** After green writer-safe proof, verify the diff again,
|
|
@@ -9,7 +9,9 @@ contract for the sentinel schema, defaults, gate ceiling, and mutable-counter
|
|
|
9
9
|
ownership. This file owns only Build's dispatch, charging, and red-path behavior.
|
|
10
10
|
|
|
11
11
|
Rules: green before `built`; hard `max_slices` cap; gates before the action they
|
|
12
|
-
control; irreversible work (destructive/auth/public API) always pauses
|
|
12
|
+
control; irreversible work (destructive/auth/public API) always pauses; a tick
|
|
13
|
+
never ends with an open or sealed wave — wait for its returns or
|
|
14
|
+
`dispatch abandon --reason`.
|
|
13
15
|
|
|
14
16
|
## Iteration cap
|
|
15
17
|
|
|
@@ -9,7 +9,7 @@ Evidence: acceptance met; tests <cmd> → pass; browser <summary|n/a>; drift <no
|
|
|
9
9
|
Open: <none|blockers|questions>
|
|
10
10
|
Next: $rite-build
|
|
11
11
|
Record: .devrites/work/<slug>/evidence.md
|
|
12
|
-
↻ Hygiene: /clear between slices; $rite-handoff if away > a few hours
|
|
12
|
+
↻ Hygiene: /clear between slices (none in `orient` `inflight_waves`); $rite-handoff if away > a few hours
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
**All built:**
|
|
@@ -14,6 +14,8 @@ Spec Drift Guard still applies.
|
|
|
14
14
|
`Implementation readiness: READY`, current `Readiness inputs SHA-256`, exact
|
|
15
15
|
required accounts, ID/meaning traceability and green proof preflight.
|
|
16
16
|
Run `devrites-engine check readiness <slug>`; any miss/nonzero blocks.
|
|
17
|
+
Non-empty `orient` `inflight_waves` is reconciled first: `dispatch return` each
|
|
18
|
+
role whose account is in hand, else `dispatch abandon --reason` and re-dispatch.
|
|
17
19
|
2. **Select.** Pick the lowest pending slice whose `depends_on` are all built, using
|
|
18
20
|
`orient` `task_graph.slices` plus the `state.md` built list — never by scanning
|
|
19
21
|
`tasks.md`; then `observe slice` it. Restate its goal, ACs, exclusions and exact paths. Include
|
|
@@ -125,7 +125,10 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
|
|
|
125
125
|
new fingerprint. At the limit return the gate and repro. `Escalation` is only for product/irreversible choices
|
|
126
126
|
or human-only access; technical failure is a blocker.
|
|
127
127
|
Before handoff, inspect your cumulative slice diff against the contract and
|
|
128
|
-
scenario matrix, including correction-affected adjacent variants
|
|
128
|
+
scenario matrix, including correction-affected adjacent variants, then against
|
|
129
|
+
the reviewers' own checks: [code review § What to check](../skills/devrites-lib/reference/standards/code-review.md#what-to-check-tests-first)
|
|
130
|
+
and test analysis (each acceptance criterion has a test that fails without the
|
|
131
|
+
change; no assertion-free, tautological, or mock-asserting test). Fix supported
|
|
129
132
|
in-scope gaps together, rerun affected proof, and report remaining gaps. Self-check
|
|
130
133
|
never replaces independent review; green tests do not prove all possible cases.
|
|
131
134
|
5. **TRANSFER when isolated.** After green writer-safe proof, verify the diff again,
|
|
@@ -9,7 +9,9 @@ contract for the sentinel schema, defaults, gate ceiling, and mutable-counter
|
|
|
9
9
|
ownership. This file owns only Build's dispatch, charging, and red-path behavior.
|
|
10
10
|
|
|
11
11
|
Rules: green before `built`; hard `max_slices` cap; gates before the action they
|
|
12
|
-
control; irreversible work (destructive/auth/public API) always pauses
|
|
12
|
+
control; irreversible work (destructive/auth/public API) always pauses; a tick
|
|
13
|
+
never ends with an open or sealed wave — wait for its returns or
|
|
14
|
+
`dispatch abandon --reason`.
|
|
13
15
|
|
|
14
16
|
## Iteration cap
|
|
15
17
|
|
|
@@ -9,7 +9,7 @@ Evidence: acceptance met; tests <cmd> → pass; browser <summary|n/a>; drift <no
|
|
|
9
9
|
Open: <none|blockers|questions>
|
|
10
10
|
Next: /rite-build
|
|
11
11
|
Record: .devrites/work/<slug>/evidence.md
|
|
12
|
-
↻ Hygiene: /clear between slices; /rite-handoff if away > a few hours
|
|
12
|
+
↻ Hygiene: /clear between slices (none in `orient` `inflight_waves`); /rite-handoff if away > a few hours
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
**All built:**
|
|
@@ -14,6 +14,8 @@ Spec Drift Guard still applies.
|
|
|
14
14
|
`Implementation readiness: READY`, current `Readiness inputs SHA-256`, exact
|
|
15
15
|
required accounts, ID/meaning traceability and green proof preflight.
|
|
16
16
|
Run `devrites-engine check readiness <slug>`; any miss/nonzero blocks.
|
|
17
|
+
Non-empty `orient` `inflight_waves` is reconciled first: `dispatch return` each
|
|
18
|
+
role whose account is in hand, else `dispatch abandon --reason` and re-dispatch.
|
|
17
19
|
2. **Select.** Pick the lowest pending slice whose `depends_on` are all built, using
|
|
18
20
|
`orient` `task_graph.slices` plus the `state.md` built list — never by scanning
|
|
19
21
|
`tasks.md`; then `observe slice` it. Restate its goal, ACs, exclusions and exact paths. Include
|
|
@@ -118,7 +118,10 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
|
|
|
118
118
|
new fingerprint. At the limit return the gate and repro. `Escalation` is only for product/irreversible choices
|
|
119
119
|
or human-only access; technical failure is a blocker.
|
|
120
120
|
Before handoff, inspect your cumulative slice diff against the contract and
|
|
121
|
-
scenario matrix, including correction-affected adjacent variants
|
|
121
|
+
scenario matrix, including correction-affected adjacent variants, then against
|
|
122
|
+
the reviewers' own checks: [code review § What to check](../skills/devrites-lib/reference/standards/code-review.md#what-to-check-tests-first)
|
|
123
|
+
and test analysis (each acceptance criterion has a test that fails without the
|
|
124
|
+
change; no assertion-free, tautological, or mock-asserting test). Fix supported
|
|
122
125
|
in-scope gaps together, rerun affected proof, and report remaining gaps. Self-check
|
|
123
126
|
never replaces independent review; green tests do not prove all possible cases.
|
|
124
127
|
5. **TRANSFER when isolated.** After green writer-safe proof, verify the diff again,
|
|
@@ -9,7 +9,9 @@ contract for the sentinel schema, defaults, gate ceiling, and mutable-counter
|
|
|
9
9
|
ownership. This file owns only Build's dispatch, charging, and red-path behavior.
|
|
10
10
|
|
|
11
11
|
Rules: green before `built`; hard `max_slices` cap; gates before the action they
|
|
12
|
-
control; irreversible work (destructive/auth/public API) always pauses
|
|
12
|
+
control; irreversible work (destructive/auth/public API) always pauses; a tick
|
|
13
|
+
never ends with an open or sealed wave — wait for its returns or
|
|
14
|
+
`dispatch abandon --reason`.
|
|
13
15
|
|
|
14
16
|
## Iteration cap
|
|
15
17
|
|
|
@@ -9,7 +9,7 @@ Evidence: acceptance met; tests <cmd> → pass; browser <summary|n/a>; drift <no
|
|
|
9
9
|
Open: <none|blockers|questions>
|
|
10
10
|
Next: /rite-build
|
|
11
11
|
Record: .devrites/work/<slug>/evidence.md
|
|
12
|
-
↻ Hygiene: /clear between slices; /rite-handoff if away > a few hours
|
|
12
|
+
↻ Hygiene: /clear between slices (none in `orient` `inflight_waves`); /rite-handoff if away > a few hours
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
**All built:**
|
|
@@ -14,6 +14,8 @@ Spec Drift Guard still applies.
|
|
|
14
14
|
`Implementation readiness: READY`, current `Readiness inputs SHA-256`, exact
|
|
15
15
|
required accounts, ID/meaning traceability and green proof preflight.
|
|
16
16
|
Run `devrites-engine check readiness <slug>`; any miss/nonzero blocks.
|
|
17
|
+
Non-empty `orient` `inflight_waves` is reconciled first: `dispatch return` each
|
|
18
|
+
role whose account is in hand, else `dispatch abandon --reason` and re-dispatch.
|
|
17
19
|
2. **Select.** Pick the lowest pending slice whose `depends_on` are all built, using
|
|
18
20
|
`orient` `task_graph.slices` plus the `state.md` built list — never by scanning
|
|
19
21
|
`tasks.md`; then `observe slice` it. Restate its goal, ACs, exclusions and exact paths. Include
|
|
@@ -119,7 +119,10 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
|
|
|
119
119
|
new fingerprint. At the limit return the gate and repro. `Escalation` is only for product/irreversible choices
|
|
120
120
|
or human-only access; technical failure is a blocker.
|
|
121
121
|
Before handoff, inspect your cumulative slice diff against the contract and
|
|
122
|
-
scenario matrix, including correction-affected adjacent variants
|
|
122
|
+
scenario matrix, including correction-affected adjacent variants, then against
|
|
123
|
+
the reviewers' own checks: [code review § What to check](../skills/devrites-lib/reference/standards/code-review.md#what-to-check-tests-first)
|
|
124
|
+
and test analysis (each acceptance criterion has a test that fails without the
|
|
125
|
+
change; no assertion-free, tautological, or mock-asserting test). Fix supported
|
|
123
126
|
in-scope gaps together, rerun affected proof, and report remaining gaps. Self-check
|
|
124
127
|
never replaces independent review; green tests do not prove all possible cases.
|
|
125
128
|
5. **TRANSFER when isolated.** After green writer-safe proof, verify the diff again,
|
|
@@ -9,7 +9,9 @@ contract for the sentinel schema, defaults, gate ceiling, and mutable-counter
|
|
|
9
9
|
ownership. This file owns only Build's dispatch, charging, and red-path behavior.
|
|
10
10
|
|
|
11
11
|
Rules: green before `built`; hard `max_slices` cap; gates before the action they
|
|
12
|
-
control; irreversible work (destructive/auth/public API) always pauses
|
|
12
|
+
control; irreversible work (destructive/auth/public API) always pauses; a tick
|
|
13
|
+
never ends with an open or sealed wave — wait for its returns or
|
|
14
|
+
`dispatch abandon --reason`.
|
|
13
15
|
|
|
14
16
|
## Iteration cap
|
|
15
17
|
|
|
@@ -9,7 +9,7 @@ Evidence: acceptance met; tests <cmd> → pass; browser <summary|n/a>; drift <no
|
|
|
9
9
|
Open: <none|blockers|questions>
|
|
10
10
|
Next: /rite-build
|
|
11
11
|
Record: .devrites/work/<slug>/evidence.md
|
|
12
|
-
↻ Hygiene: /clear between slices; /rite-handoff if away > a few hours
|
|
12
|
+
↻ Hygiene: /clear between slices (none in `orient` `inflight_waves`); /rite-handoff if away > a few hours
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
**All built:**
|
|
@@ -14,6 +14,8 @@ Spec Drift Guard still applies.
|
|
|
14
14
|
`Implementation readiness: READY`, current `Readiness inputs SHA-256`, exact
|
|
15
15
|
required accounts, ID/meaning traceability and green proof preflight.
|
|
16
16
|
Run `devrites-engine check readiness <slug>`; any miss/nonzero blocks.
|
|
17
|
+
Non-empty `orient` `inflight_waves` is reconciled first: `dispatch return` each
|
|
18
|
+
role whose account is in hand, else `dispatch abandon --reason` and re-dispatch.
|
|
17
19
|
2. **Select.** Pick the lowest pending slice whose `depends_on` are all built, using
|
|
18
20
|
`orient` `task_graph.slices` plus the `state.md` built list — never by scanning
|
|
19
21
|
`tasks.md`; then `observe slice` it. Restate its goal, ACs, exclusions and exact paths. Include
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devrites",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.4",
|
|
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",
|