devrites 6.0.1 → 6.0.3

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.
Files changed (156) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +15 -64
  3. package/SECURITY.md +7 -11
  4. package/bin/devrites.mjs +0 -8
  5. package/docs/adr/0034-workflow-artifact-byte-corrections-get-code-review.md +60 -0
  6. package/docs/adr/README.md +2 -1
  7. package/docs/architecture.md +2 -5
  8. package/docs/cli.md +4 -11
  9. package/docs/command-map.md +1 -2
  10. package/docs/engine/commands.md +6 -6
  11. package/docs/release.md +5 -6
  12. package/engine/help.go +12 -7
  13. package/engine/help_test.go +1 -0
  14. package/engine/internal/gate/readiness_binding.go +19 -0
  15. package/engine/internal/lib/cli_observe.go +16 -0
  16. package/engine/internal/lib/context.go +191 -19
  17. package/engine/internal/lib/context_next_test.go +186 -0
  18. package/engine/internal/lib/dispatch.go +146 -11
  19. package/engine/internal/lib/drift.go +10 -2
  20. package/engine/internal/lib/drift_test.go +29 -0
  21. package/engine/internal/lib/slicecheck.go +19 -0
  22. package/engine/internal/lib/slicecheck_test.go +41 -0
  23. package/engine/internal/lib/windows.go +109 -13
  24. package/engine/internal/lib/windows_test.go +85 -0
  25. package/engine/internal/release/release.go +3 -13
  26. package/engine/internal/release/release_test.go +6 -197
  27. package/engine/main.go +2 -2
  28. package/install.sh +1 -116
  29. package/pack/.claude/agents/devrites-slice-wright.md +3 -1
  30. package/pack/.claude/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
  31. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
  32. package/pack/.claude/skills/devrites-lib/reference/standards/README.md +1 -1
  33. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
  34. package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +3 -0
  35. package/pack/.claude/skills/devrites-lib/reference/standards/glossary.md +1 -1
  36. package/pack/.claude/skills/devrites-lib/reference/standards/security.md +1 -0
  37. package/pack/.claude/skills/devrites-lib/reference/standards/windows.md +11 -3
  38. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
  39. package/pack/.claude/skills/rite-build/SKILL.md +1 -1
  40. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +3 -2
  41. package/pack/.claude/skills/rite-build/reference/parallel-batch.md +5 -3
  42. package/pack/.claude/skills/rite-build/reference/phase-contract.md +5 -3
  43. package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +3 -1
  44. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +23 -9
  45. package/pack/.claude/skills/rite-define/SKILL.md +2 -1
  46. package/pack/.claude/skills/rite-plan/SKILL.md +2 -1
  47. package/pack/.claude/skills/rite-pressure-test/SKILL.md +6 -3
  48. package/pack/.claude/skills/rite-vet/SKILL.md +9 -1
  49. package/pack/.claude/skills/rite-vet/reference/review-axes.md +6 -2
  50. package/pack/generated/claude/agents/devrites-slice-wright.md +3 -1
  51. package/pack/generated/claude/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
  52. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
  53. package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +1 -1
  54. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
  55. package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +3 -0
  56. package/pack/generated/claude/skills/devrites-lib/reference/standards/glossary.md +1 -1
  57. package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +1 -0
  58. package/pack/generated/claude/skills/devrites-lib/reference/standards/windows.md +11 -3
  59. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
  60. package/pack/generated/claude/skills/rite-build/SKILL.md +1 -1
  61. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +3 -2
  62. package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +5 -3
  63. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +5 -3
  64. package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +3 -1
  65. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +23 -9
  66. package/pack/generated/claude/skills/rite-define/SKILL.md +2 -1
  67. package/pack/generated/claude/skills/rite-plan/SKILL.md +2 -1
  68. package/pack/generated/claude/skills/rite-pressure-test/SKILL.md +6 -3
  69. package/pack/generated/claude/skills/rite-vet/SKILL.md +9 -1
  70. package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +6 -2
  71. package/pack/generated/codex/agents/devrites-slice-wright.toml +3 -1
  72. package/pack/generated/codex/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
  73. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
  74. package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +1 -1
  75. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
  76. package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +3 -0
  77. package/pack/generated/codex/skills/devrites-lib/reference/standards/glossary.md +1 -1
  78. package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +1 -0
  79. package/pack/generated/codex/skills/devrites-lib/reference/standards/windows.md +11 -3
  80. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
  81. package/pack/generated/codex/skills/rite-build/SKILL.md +1 -1
  82. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +3 -2
  83. package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +5 -3
  84. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +5 -3
  85. package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +3 -1
  86. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +23 -9
  87. package/pack/generated/codex/skills/rite-define/SKILL.md +2 -1
  88. package/pack/generated/codex/skills/rite-plan/SKILL.md +2 -1
  89. package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +6 -3
  90. package/pack/generated/codex/skills/rite-vet/SKILL.md +9 -1
  91. package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +6 -2
  92. package/pack/generated/devin/agents/devrites-slice-wright.md +3 -1
  93. package/pack/generated/devin/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
  94. package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
  95. package/pack/generated/devin/skills/devrites-lib/reference/standards/README.md +1 -1
  96. package/pack/generated/devin/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
  97. package/pack/generated/devin/skills/devrites-lib/reference/standards/code-review.md +3 -0
  98. package/pack/generated/devin/skills/devrites-lib/reference/standards/glossary.md +1 -1
  99. package/pack/generated/devin/skills/devrites-lib/reference/standards/security.md +1 -0
  100. package/pack/generated/devin/skills/devrites-lib/reference/standards/windows.md +11 -3
  101. package/pack/generated/devin/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
  102. package/pack/generated/devin/skills/rite-build/SKILL.md +1 -1
  103. package/pack/generated/devin/skills/rite-build/reference/one-slice-cycle.md +3 -2
  104. package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +5 -3
  105. package/pack/generated/devin/skills/rite-build/reference/phase-contract.md +5 -3
  106. package/pack/generated/devin/skills/rite-build/reference/spec-drift-guard.md +3 -1
  107. package/pack/generated/devin/skills/rite-build/reference/wright-dispatch.md +23 -9
  108. package/pack/generated/devin/skills/rite-define/SKILL.md +2 -1
  109. package/pack/generated/devin/skills/rite-plan/SKILL.md +2 -1
  110. package/pack/generated/devin/skills/rite-pressure-test/SKILL.md +6 -3
  111. package/pack/generated/devin/skills/rite-vet/SKILL.md +9 -1
  112. package/pack/generated/devin/skills/rite-vet/reference/review-axes.md +6 -2
  113. package/pack/generated/omp/agents/devrites-slice-wright.md +3 -1
  114. package/pack/generated/omp/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
  115. package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
  116. package/pack/generated/omp/skills/devrites-lib/reference/standards/README.md +1 -1
  117. package/pack/generated/omp/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
  118. package/pack/generated/omp/skills/devrites-lib/reference/standards/code-review.md +3 -0
  119. package/pack/generated/omp/skills/devrites-lib/reference/standards/glossary.md +1 -1
  120. package/pack/generated/omp/skills/devrites-lib/reference/standards/security.md +1 -0
  121. package/pack/generated/omp/skills/devrites-lib/reference/standards/windows.md +11 -3
  122. package/pack/generated/omp/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
  123. package/pack/generated/omp/skills/rite-build/SKILL.md +1 -1
  124. package/pack/generated/omp/skills/rite-build/reference/one-slice-cycle.md +3 -2
  125. package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +5 -3
  126. package/pack/generated/omp/skills/rite-build/reference/phase-contract.md +5 -3
  127. package/pack/generated/omp/skills/rite-build/reference/spec-drift-guard.md +3 -1
  128. package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +23 -9
  129. package/pack/generated/omp/skills/rite-define/SKILL.md +2 -1
  130. package/pack/generated/omp/skills/rite-plan/SKILL.md +2 -1
  131. package/pack/generated/omp/skills/rite-pressure-test/SKILL.md +6 -3
  132. package/pack/generated/omp/skills/rite-vet/SKILL.md +9 -1
  133. package/pack/generated/omp/skills/rite-vet/reference/review-axes.md +6 -2
  134. package/pack/generated/pi/agents/devrites-slice-wright.md +3 -1
  135. package/pack/generated/pi/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
  136. package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
  137. package/pack/generated/pi/skills/devrites-lib/reference/standards/README.md +1 -1
  138. package/pack/generated/pi/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
  139. package/pack/generated/pi/skills/devrites-lib/reference/standards/code-review.md +3 -0
  140. package/pack/generated/pi/skills/devrites-lib/reference/standards/glossary.md +1 -1
  141. package/pack/generated/pi/skills/devrites-lib/reference/standards/security.md +1 -0
  142. package/pack/generated/pi/skills/devrites-lib/reference/standards/windows.md +11 -3
  143. package/pack/generated/pi/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
  144. package/pack/generated/pi/skills/rite-build/SKILL.md +1 -1
  145. package/pack/generated/pi/skills/rite-build/reference/one-slice-cycle.md +3 -2
  146. package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +5 -3
  147. package/pack/generated/pi/skills/rite-build/reference/phase-contract.md +5 -3
  148. package/pack/generated/pi/skills/rite-build/reference/spec-drift-guard.md +3 -1
  149. package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +23 -9
  150. package/pack/generated/pi/skills/rite-define/SKILL.md +2 -1
  151. package/pack/generated/pi/skills/rite-plan/SKILL.md +2 -1
  152. package/pack/generated/pi/skills/rite-pressure-test/SKILL.md +6 -3
  153. package/pack/generated/pi/skills/rite-vet/SKILL.md +9 -1
  154. package/pack/generated/pi/skills/rite-vet/reference/review-axes.md +6 -2
  155. package/package.json +1 -1
  156. package/scripts/install-lib.sh +0 -16
@@ -16,7 +16,6 @@ import (
16
16
  "net/netip"
17
17
  "net/url"
18
18
  "os"
19
- "os/exec"
20
19
  "path"
21
20
  "path/filepath"
22
21
  "regexp"
@@ -98,7 +97,7 @@ func Acquire(ctx context.Context, repository, tag string) (Candidate, func(), er
98
97
  bundleName := "devrites-" + tag + ".tar.gz"
99
98
  bundleURL := base + "/" + bundleName
100
99
  bundlePath := filepath.Join(tmp, bundleName)
101
- if err := downloadVerified(ctx, bundleURL, bundlePath, bundleName, 0o644, repository); err != nil {
100
+ if err := downloadVerified(ctx, bundleURL, bundlePath, bundleName, 0o644); err != nil {
102
101
  return fail(fmt.Errorf("download release bundle: %w", err))
103
102
  }
104
103
  source, err := extractBundle(bundlePath, tmp, tag)
@@ -108,7 +107,7 @@ func Acquire(ctx context.Context, repository, tag string) (Candidate, func(), er
108
107
 
109
108
  engineURL := base + "/" + binaryName
110
109
  enginePath := filepath.Join(tmp, binaryName)
111
- if err := downloadVerified(ctx, engineURL, enginePath, binaryName, 0o755, repository); err != nil {
110
+ if err := downloadVerified(ctx, engineURL, enginePath, binaryName, 0o755); err != nil {
112
111
  return fail(fmt.Errorf("download engine binary: %w", err))
113
112
  }
114
113
  return Candidate{
@@ -185,7 +184,7 @@ func fetchJSON(ctx context.Context, rawURL string, out any) error {
185
184
  return nil
186
185
  }
187
186
 
188
- func downloadVerified(ctx context.Context, rawURL, destination, filename string, mode fs.FileMode, repository string) error {
187
+ func downloadVerified(ctx context.Context, rawURL, destination, filename string, mode fs.FileMode) error {
189
188
  if err := download(ctx, rawURL, destination, maxAssetBytes, mode); err != nil {
190
189
  return err
191
190
  }
@@ -223,15 +222,6 @@ func downloadVerified(ctx context.Context, rawURL, destination, filename string,
223
222
  if !strings.EqualFold(hex.EncodeToString(hash.Sum(nil)), hex.EncodeToString(want)) {
224
223
  return fmt.Errorf("checksum mismatch for %s", filename)
225
224
  }
226
- signer := repository + "/.github/workflows/ci.yml@refs/heads/main"
227
- // #nosec G204 -- fixed gh argv; repository is matched by repositoryPattern (leading alphanumeric, so never a flag) in Acquire and destination is an absolute path under the engine-created temp dir
228
- cmd := exec.CommandContext(ctx, "gh", "attestation", "verify", destination, "--repo", repository, "--signer-workflow", signer)
229
- if _, err := cmd.CombinedOutput(); err != nil {
230
- if errors.Is(err, exec.ErrNotFound) {
231
- return errors.New("gh (GitHub CLI) is required and must be authenticated for attestation verification")
232
- }
233
- return fmt.Errorf("attestation verification failed for %s: %w", filename, err)
234
- }
235
225
  return nil
236
226
  }
237
227
 
@@ -33,7 +33,9 @@ func TestLatestAndAcquireVerifiedRelease(t *testing.T) {
33
33
  bundleName: bundle,
34
34
  binaryName: engine,
35
35
  }
36
- installAttestationStandIn(t, bundle, engine)
36
+ if runtime.GOOS == "windows" {
37
+ t.Skip("positive-control engine is a shell script")
38
+ }
37
39
  server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
38
40
  if r.URL.Path == "/repos/owner/repo/releases/latest" {
39
41
  _, _ = fmt.Fprintf(w, `{"tag_name":%q}`, tag)
@@ -81,12 +83,12 @@ func TestLatestAndAcquireVerifiedRelease(t *testing.T) {
81
83
  cmd := exec.Command(candidate.EnginePath)
82
84
  cmd.Env = append(os.Environ(), "MARKER="+marker)
83
85
  if err := cmd.Run(); err == nil {
84
- t.Fatal("attested engine exited successfully; want positive-control exit 23")
86
+ t.Fatal("acquired engine exited successfully; want positive-control exit 23")
85
87
  } else if exit, ok := err.(*exec.ExitError); !ok || exit.ExitCode() != 23 {
86
- t.Fatalf("attested engine exit = %v, want 23", err)
88
+ t.Fatalf("acquired engine exit = %v, want 23", err)
87
89
  }
88
90
  if got, err := os.ReadFile(marker); err != nil || string(got) != "success" {
89
- t.Fatalf("attested engine marker = %q, %v", got, err)
91
+ t.Fatalf("acquired engine marker = %q, %v", got, err)
90
92
  }
91
93
  cleanup()
92
94
  if _, err := os.Stat(root); !os.IsNotExist(err) {
@@ -111,199 +113,6 @@ func TestValidTag(t *testing.T) {
111
113
  }
112
114
  }
113
115
 
114
- func TestAcquireRejectsOriginSubstitution(t *testing.T) {
115
- const tag = "v4.1.0"
116
- trusted := testBundle(t, tag, map[string]string{
117
- "package.json": `{"version":"4.1.0"}`,
118
- "pack/generated/claude/skills/rite/SKILL.md": "rite\n",
119
- })
120
- attacker := testBundle(t, tag, map[string]string{
121
- "package.json": `{"version":"4.1.0"}`,
122
- "pack/generated/claude/skills/rite/SKILL.md": "attacker\n",
123
- })
124
- binaryName, err := platformBinaryName()
125
- if err != nil {
126
- t.Skip(err)
127
- }
128
- installAttestationStandIn(t, trusted, []byte("trusted engine"))
129
- assets := map[string][]byte{"devrites-" + tag + ".tar.gz": attacker, binaryName: []byte("attacker engine")}
130
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
131
- name := filepath.Base(r.URL.Path)
132
- assetName := strings.TrimSuffix(name, ".sha256")
133
- asset, ok := assets[assetName]
134
- if !ok {
135
- http.NotFound(w, r)
136
- return
137
- }
138
- if strings.HasSuffix(name, ".sha256") {
139
- _, _ = fmt.Fprintf(w, "%x %s\n", sha256.Sum256(asset), assetName)
140
- return
141
- }
142
- _, _ = w.Write(asset)
143
- }))
144
- defer server.Close()
145
- oldWeb := webBaseURL
146
- webBaseURL = server.URL
147
- t.Cleanup(func() { webBaseURL = oldWeb })
148
-
149
- _, _, err = Acquire(context.Background(), "owner/repo", tag)
150
- if err == nil || !strings.Contains(err.Error(), "attestation verification failed") {
151
- t.Fatalf("Acquire error = %v, want attestation refusal despite a matching origin checksum", err)
152
- }
153
- }
154
-
155
- func TestAcquireRejectsSingleAssetSubstitution(t *testing.T) {
156
- const tag = "v4.1.0"
157
- files := map[string]string{
158
- "package.json": `{"version":"4.1.0"}`,
159
- "pack/generated/claude/skills/rite/SKILL.md": "rite\n",
160
- }
161
- trustedBundle := testBundle(t, tag, files)
162
- files["pack/generated/claude/skills/rite/SKILL.md"] = "attacker\n"
163
- attackerBundle := testBundle(t, tag, files)
164
- trustedEngine, attackerEngine := []byte("trusted engine"), []byte("attacker engine")
165
- binaryName, err := platformBinaryName()
166
- if err != nil {
167
- t.Skip(err)
168
- }
169
- for _, tc := range []struct {
170
- name, refusedAsset string
171
- bundle, engine []byte
172
- }{
173
- {"engine only", binaryName, trustedBundle, attackerEngine},
174
- {"bundle only", "devrites-" + tag + ".tar.gz", attackerBundle, trustedEngine},
175
- } {
176
- t.Run(tc.name, func(t *testing.T) {
177
- installAttestationStandIn(t, trustedBundle, trustedEngine)
178
- assets := map[string][]byte{"devrites-" + tag + ".tar.gz": tc.bundle, binaryName: tc.engine}
179
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
180
- name := filepath.Base(r.URL.Path)
181
- assetName := strings.TrimSuffix(name, ".sha256")
182
- asset, ok := assets[assetName]
183
- if !ok {
184
- http.NotFound(w, r)
185
- return
186
- }
187
- if strings.HasSuffix(name, ".sha256") {
188
- _, _ = fmt.Fprintf(w, "%x %s\n", sha256.Sum256(asset), assetName)
189
- return
190
- }
191
- _, _ = w.Write(asset)
192
- }))
193
- defer server.Close()
194
- oldWeb := webBaseURL
195
- webBaseURL = server.URL
196
- t.Cleanup(func() { webBaseURL = oldWeb })
197
-
198
- _, cleanup, err := Acquire(context.Background(), "owner/repo", tag)
199
- if err == nil {
200
- cleanup()
201
- t.Fatal("Acquire accepted a release with one unattested asset")
202
- }
203
- want := "attestation verification failed for " + tc.refusedAsset
204
- if !strings.Contains(err.Error(), want) {
205
- t.Fatalf("Acquire error = %v, want %q", err, want)
206
- }
207
- })
208
- }
209
- }
210
-
211
- func installAttestationStandIn(t *testing.T, trusted ...[]byte) {
212
- t.Helper()
213
- if runtime.GOOS == "windows" {
214
- t.Skip("attestation stand-in and positive-control engine are shell scripts")
215
- }
216
- dir := t.TempDir()
217
- var hashes strings.Builder
218
- for _, data := range trusted {
219
- fmt.Fprintf(&hashes, "%x\n", sha256.Sum256(data))
220
- }
221
- allowed := filepath.Join(dir, "allowed-digests")
222
- if err := os.WriteFile(allowed, []byte(hashes.String()), 0o600); err != nil {
223
- t.Fatal(err)
224
- }
225
- gh := filepath.Join(dir, "gh")
226
- script := `#!/bin/sh
227
- [ "$1" = attestation ] && [ "$2" = verify ] && [ "$4" = --repo ] && [ "$5" = owner/repo ] && [ "$6" = --signer-workflow ] && [ "$7" = owner/repo/.github/workflows/ci.yml@refs/heads/main ] || exit 64
228
- got=$(shasum -a 256 "$3" | awk '{print $1}') || exit 65
229
- grep -Fxq "$got" "$ATTESTED_DIGESTS"
230
- `
231
- if err := os.WriteFile(gh, []byte(script), 0o700); err != nil {
232
- t.Fatal(err)
233
- }
234
- t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
235
- t.Setenv("ATTESTED_DIGESTS", allowed)
236
- }
237
-
238
- func TestAttestationStandInIsStrict(t *testing.T) {
239
- trusted := []byte("trusted engine")
240
- installAttestationStandIn(t, trusted, []byte("other"))
241
- dir := t.TempDir()
242
- good := filepath.Join(dir, "good")
243
- bad := filepath.Join(dir, "bad")
244
- if err := os.WriteFile(good, trusted, 0o600); err != nil {
245
- t.Fatal(err)
246
- }
247
- if err := os.WriteFile(bad, []byte("attacker engine"), 0o600); err != nil {
248
- t.Fatal(err)
249
- }
250
- const signer = "owner/repo/.github/workflows/ci.yml@refs/heads/main"
251
- run := func(file, repo, workflow string) error {
252
- return exec.Command("gh", "attestation", "verify", file, "--repo", repo, "--signer-workflow", workflow).Run()
253
- }
254
- if err := run(good, "owner/repo", signer); err != nil {
255
- t.Fatalf("stand-in rejected the pinned request: %v", err)
256
- }
257
- if run(good, "other/repo", signer) == nil {
258
- t.Error("stand-in accepted a wrong repo")
259
- }
260
- if run(good, "owner/repo", "owner/repo/.github/workflows/other.yml@refs/heads/main") == nil {
261
- t.Error("stand-in accepted a wrong signer workflow")
262
- }
263
- if run(bad, "owner/repo", signer) == nil {
264
- t.Error("stand-in accepted an unattested digest")
265
- }
266
- }
267
-
268
- func TestAcquireRejectsMissingAttestationVerifier(t *testing.T) {
269
- const tag = "v4.1.0"
270
- binaryName, err := platformBinaryName()
271
- if err != nil {
272
- t.Skip(err)
273
- }
274
- assets := map[string][]byte{
275
- "devrites-" + tag + ".tar.gz": testBundle(t, tag, map[string]string{
276
- "package.json": `{"version":"4.1.0"}`,
277
- "pack/generated/claude/skills/rite/SKILL.md": "rite\n",
278
- }),
279
- binaryName: []byte("engine"),
280
- }
281
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
282
- name := filepath.Base(r.URL.Path)
283
- assetName := strings.TrimSuffix(name, ".sha256")
284
- asset, ok := assets[assetName]
285
- if !ok {
286
- http.NotFound(w, r)
287
- return
288
- }
289
- if strings.HasSuffix(name, ".sha256") {
290
- _, _ = fmt.Fprintf(w, "%x %s\n", sha256.Sum256(asset), assetName)
291
- return
292
- }
293
- _, _ = w.Write(asset)
294
- }))
295
- defer server.Close()
296
- oldWeb := webBaseURL
297
- webBaseURL = server.URL
298
- t.Cleanup(func() { webBaseURL = oldWeb })
299
- t.Setenv("PATH", t.TempDir())
300
-
301
- _, _, err = Acquire(context.Background(), "owner/repo", tag)
302
- if err == nil || !strings.Contains(err.Error(), "gh (GitHub CLI) is required") {
303
- t.Fatalf("Acquire error = %v, want refusal when gh is not installed", err)
304
- }
305
- }
306
-
307
116
  func TestAcquireRejectsChecksumMismatch(t *testing.T) {
308
117
  const tag = "v4.1.0"
309
118
  bundleName := "devrites-" + tag + ".tar.gz"
package/engine/main.go CHANGED
@@ -48,11 +48,11 @@ Usage:
48
48
  devrites-engine orient <slug> Alias for observe summary
49
49
  devrites-engine next [slug] Print the minimal remaining lifecycle path with advisory skips
50
50
  devrites-engine handoff [slug] Emit the deterministic resume record (cursor, blocking gates, ledger, dead ends)
51
- devrites-engine context [slug] (--phase <p> | --skill <name>) [--role <r>] [--trigger a,b]
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
54
  devrites-engine metrics summary [slug] Summarize the per-feature metrics.jsonl event ledger
55
- devrites-engine dispatch <slug> <sub> Launch-wave barrier for parallel dispatch: open/start/seal/return/status/abandon
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)
58
58
  devrites-engine observe slice <slug> <SLICE-ID> Print one SLICE-### section of tasks.md
package/install.sh CHANGED
@@ -13,8 +13,6 @@ DEVRITES_REPO="${DEVRITES_REPO:-ViktorsBaikers/DevRites}"
13
13
  DEVRITES_REF="${DEVRITES_REF:-}"
14
14
 
15
15
  BOOTSTRAP_DIR=""
16
- RUN_JOB=""
17
- RUN_WATCH=""
18
16
  BOOTSTRAP_MAX_METADATA=1048576
19
17
  BOOTSTRAP_MAX_SIDECAR=4096
20
18
  BOOTSTRAP_MAX_ARCHIVE=67108864
@@ -140,114 +138,6 @@ verify_sha256() {
140
138
  [ -n "$want" ] && [ -n "$got" ] && [ "$got" = "$want" ]
141
139
  }
142
140
 
143
- # Runs a command for at most $1 seconds with output discarded. Past the limit it
144
- # is sent SIGTERM and, if it is still alive two seconds later, SIGKILL. A command
145
- # stopped this way sets RUN_TIMED_OUT=1 and returns 124; any other failure keeps
146
- # the command's own status, so a crash is never mistaken for a timeout. Same
147
- # watchdog idea as scripts/install-lib.sh dr_run_bounded, which is not available
148
- # until the bundle is verified and extracted. The timeout marker lives in
149
- # $BOOTSTRAP_DIR, which the EXIT trap removes, so a run without that directory
150
- # fails closed; stop_bounded ends a command and watchdog still in flight when the
151
- # bootstrap is interrupted.
152
- run_bounded() {
153
- limit="$1"; shift
154
- RUN_TIMED_OUT=0
155
- [ -n "$BOOTSTRAP_DIR" ] || return 125
156
- fired="$(mktemp "$BOOTSTRAP_DIR/bound.XXXXXX" 2>/dev/null)" || return 125
157
- "$@" >/dev/null 2>&1 </dev/null &
158
- job=$!
159
- RUN_JOB=$job
160
- {
161
- trap 'kill "$nap"; exit 0' TERM
162
- sleep "$limit" & nap=$!; wait "$nap"
163
- kill -0 "$job" || exit 0
164
- echo 1 >"$fired"
165
- kill "$job"
166
- sleep 2 & nap=$!; wait "$nap"
167
- kill -9 "$job"
168
- } >/dev/null 2>&1 </dev/null &
169
- watch=$!
170
- RUN_WATCH=$watch
171
- wait "$job" 2>/dev/null
172
- rc=$?
173
- kill "$watch" >/dev/null 2>&1
174
- wait "$watch" 2>/dev/null
175
- RUN_JOB=""; RUN_WATCH=""
176
- if [ "$rc" -ne 0 ] && [ -s "$fired" ]; then
177
- RUN_TIMED_OUT=1
178
- rc=124
179
- fi
180
- rm -f "$fired"
181
- return "$rc"
182
- }
183
-
184
- stop_bounded() {
185
- # A signal can land after a job is forked but before its PID is recorded, so
186
- # every background job is ended, not only the recorded ones. run_bounded is the
187
- # only place this script backgrounds anything.
188
- for pid in $(jobs -p); do
189
- [ "$pid" = "$RUN_WATCH" ] || kill -9 "$pid" >/dev/null 2>&1
190
- done
191
- [ -z "$RUN_WATCH" ] || kill "$RUN_WATCH" >/dev/null 2>&1
192
- RUN_JOB=""; RUN_WATCH=""
193
- }
194
-
195
- # The checksum sidecar above travels with the tarball it vouches for: it is
196
- # fetched from "$url.sha256", one suffix away from the artifact's own origin. An
197
- # attacker who substitutes the release origin therefore supplies both the
198
- # payload and a digest that matches it, and the digest check passes. The
199
- # release job already mints the proof that does not travel with the artifact --
200
- # a build-provenance attestation signed by that job's OIDC identity and bound to
201
- # the artifact digest (.github/workflows/ci.yml, 'Attest release artifacts').
202
- # Verify it here, pinned to the repository and to the signer workflow, so an
203
- # attestation minted by any other workflow in this repository is rejected too.
204
- #
205
- # There is deliberately no bypass for a missing verifier: without gh there is no
206
- # proof of origin, and the sidecar is not a substitute for one.
207
- #
208
- # The lookup is bounded: DEVRITES_ATTEST_TIMEOUT is a whole number of seconds
209
- # (1 to 99999, default 120). Anything else is ignored with a warning and the
210
- # default applies, so a bad value can never remove the bound. The follow-up
211
- # `gh auth status` is bounded by 30 seconds or that limit, whichever is shorter.
212
- # A timeout, a crash and a plain rejection each fail closed and are reported as
213
- # what they are.
214
- verify_attestation() {
215
- file="$1"
216
- signer="$DEVRITES_REPO/.github/workflows/ci.yml@refs/heads/main"
217
- ATTESTATION_FAILURE=""
218
- if ! command -v gh >/dev/null 2>&1; then
219
- ATTESTATION_FAILURE="attestation verification failed: gh (GitHub CLI) is required and was not found"
220
- return 1
221
- fi
222
- attest_limit=120
223
- case "${DEVRITES_ATTEST_TIMEOUT:-}" in
224
- "") ;;
225
- *[!0-9]* | 0* | [0-9][0-9][0-9][0-9][0-9][0-9]*)
226
- echo "warning: ignoring invalid DEVRITES_ATTEST_TIMEOUT '$DEVRITES_ATTEST_TIMEOUT' (expected whole seconds, 1 to 99999); using $attest_limit" >&2
227
- ;;
228
- *) attest_limit="$DEVRITES_ATTEST_TIMEOUT" ;;
229
- esac
230
- auth_limit=30
231
- [ "$attest_limit" -lt "$auth_limit" ] && auth_limit="$attest_limit"
232
- verify_rc=0
233
- run_bounded "$attest_limit" gh attestation verify "$file" --repo "$DEVRITES_REPO" \
234
- --signer-workflow "$signer" || verify_rc=$?
235
- if [ "$verify_rc" -ne 0 ]; then
236
- if [ "$RUN_TIMED_OUT" -eq 1 ]; then
237
- ATTESTATION_FAILURE="attestation verification timed out after ${attest_limit}s; check connectivity or raise DEVRITES_ATTEST_TIMEOUT and retry"
238
- elif [ "$verify_rc" -ge 128 ]; then
239
- ATTESTATION_FAILURE="attestation verification failed: gh exited with status $verify_rc"
240
- elif run_bounded "$auth_limit" gh auth status; then
241
- ATTESTATION_FAILURE="attestation verification failed: no build provenance from $signer"
242
- elif [ "$RUN_TIMED_OUT" -eq 1 ]; then
243
- ATTESTATION_FAILURE="attestation verification timed out: gh auth status did not answer within ${auth_limit}s; check connectivity or raise DEVRITES_ATTEST_TIMEOUT and retry"
244
- else
245
- ATTESTATION_FAILURE="attestation verification failed: gh is not authenticated (attestation lookup requires a GitHub login); run 'gh auth login' and retry"
246
- fi
247
- return 1
248
- fi
249
- }
250
-
251
141
  preflight_archive() {
252
142
  archive_path="$1"
253
143
  tag="$2"
@@ -324,7 +214,7 @@ bootstrap_bundle() {
324
214
  exit 1
325
215
  }
326
216
  umask "$old_umask"
327
- trap 'stop_bounded; exit 1' HUP INT TERM
217
+ trap 'exit 1' HUP INT TERM
328
218
  trap 'rm -rf "$BOOTSTRAP_DIR"' EXIT
329
219
  if [ -n "$DEVRITES_REF" ]; then
330
220
  tag="$(normalize_release_tag "$DEVRITES_REF")" || {
@@ -361,11 +251,6 @@ bootstrap_bundle() {
361
251
  echo "error: release $tag asset $asset: checksum failed." >&2
362
252
  exit 1
363
253
  }
364
- verify_attestation "$archive" || {
365
- rm -f "$archive" "$sidecar"
366
- echo "error: release $tag asset $asset: ${ATTESTATION_FAILURE:-attestation verification failed}." >&2
367
- exit 1
368
- }
369
254
  uncompressed="$BOOTSTRAP_DIR/devrites-$tag.tar"
370
255
  bounded_decompress "$archive" "$uncompressed" "$BOOTSTRAP_MAX_UNCOMPRESSED" || {
371
256
  rm -f "$archive" "$sidecar"
@@ -138,7 +138,9 @@ Stay inside the exact project-relative paths listed in the dispatch task.
138
138
  return an `Escalation`** when:
139
139
  - the slice is **underspecified**, the **plan looks wrong**, or requirements, code,
140
140
  and tests conflict;
141
- - the slice needs a **new dependency** or a **second design system**;
141
+ - the slice needs a **new dependency** or a **second design system**, or a planned
142
+ package fails to install or resolve (report the failure; never retry under a
143
+ similar or alternative package name);
142
144
  - the work requires an unapproved product/security-policy choice, public API break,
143
145
  external-service contract change, destructive live migration, or filesystem
144
146
  destruction outside the workspace. A path allowlist alone does not approve those
@@ -3,6 +3,12 @@
3
3
  Generate **3-5 ranked hypotheses** *before* testing any of them.
4
4
  Single-hypothesis generation anchors on the first plausible idea.
5
5
 
6
+ **Check past root causes first.** Search `.devrites/archive/*/evidence.md` with
7
+ native file search for the failure's error text, symbol, and file names. A hit
8
+ whose recorded correct hypothesis matches enters the ranked list with its
9
+ recurrence noted; it is still tested, never assumed. No archive or no hit →
10
+ skip silently.
11
+
6
12
  Each hypothesis must be **falsifiable**: state the prediction it makes.
7
13
 
8
14
  > Format: *"If <X> is the cause, then <changing Y> will make the failure
@@ -36,6 +36,7 @@ If the candidate diff has no matching surfaces, record
36
36
  2. Build each reviewer's read-set once with
37
37
  `devrites-engine context <slug> --phase <p> --role <role> --trigger <set>` and
38
38
  hand the bundle path as the packet's source list instead of a file roster.
39
+ Build roles add `--slice <SLICE-ID>` (slice-scoped, slice-named bundle).
39
40
  Evaluate the skill's trigger conditions and pass each that fires — a role's
40
41
  Trigger column maps to manifest names: `security-auditor` → `security`,
41
42
  `frontend-reviewer` → `ui`/`frontend`, `performance-reviewer` → `performance`.
@@ -59,7 +59,7 @@ topic's owner.
59
59
  | `architecture-health.md` | An audit needs a whole-codebase structural score from a code index — coupling, cohesion, cycles, god files, orphans, depth. |
60
60
  | `assumption-checkpoints.md` | Scope generalizes an existing boundary: a second case, a newly optional field, or a constant turned parameter. |
61
61
  | `debug-recovery.md` | Waiting on async readiness (server start, job completion, browser signal) needs a bounded condition-based poll. |
62
- | `windows.md` | A deferral marker (TODO/FIXME/skipped test/stub) is introduced or waived — the `check windows` ledger and grammar. |
62
+ | `windows.md` | A deferral or bar-lowering marker (TODO/FIXME/skipped test/stub/suppression/removed test) is introduced or waived — the `check windows` ledger and grammar. |
63
63
  | `glossary.md` | A DevRites term is unclear; resolve it once here instead of inferring. |
64
64
 
65
65
  The table is a load trigger, not an exemption. [`core.md` § Rule summary](core.md#rule-summary-load-the-full-file-when-in-scope)
@@ -357,6 +357,8 @@ access, safety and irreversible-action boundaries independently stop work.
357
357
  renaming/splitting an unchanged cause does not. Suggestion/Nit/FYI cannot extend
358
358
  recovery. Initial discovery and expected test-first RED are not corrections;
359
359
  total rounds, re-batches and distinct-finding counts never exhaust recovery.
360
+ Every 5 Build wright rounds on one slice, the next first needs a reslice review
361
+ ([`wright-dispatch.md`](../../../rite-build/reference/wright-dispatch.md#run)).
360
362
  - **Rechecks are scoped; late findings never reopen the round.** *Late* applies only
361
363
  from a phase's **second** review pass (Vet 1b, Build's recheck after the first repair,
362
364
  Review step 7); an initial pass has no late findings and every supported finding is
@@ -30,6 +30,9 @@ If it does not, do not merge it.
30
30
  no justification), stripped assertions, deleted or skipped tests, and new
31
31
  unimplemented stubs or `throw`-only placeholders. Each names what it lowers and
32
32
  needs a recorded reason in the diff or `decisions.md`; unexplained is a finding.
33
+ `devrites-engine check windows` flags the mechanical subset (suppressions,
34
+ skip/focus, stubs, empty catch, deleted tests, stripped assertions) against
35
+ `windows.md` waivers; loosened thresholds remain this sweep's judgment.
33
36
  3. **Correctness:** logic, edge cases, error paths, race conditions, wrong assumptions. For branching or boundary changes, run the [`edge-case trace`](edge-case-trace.md): relevant probe classes, fixed-set siblings, real wiring, negative intent, and deletion contracts with an evidence disposition.
34
37
  4. **Readability:** names, function size, control flow, intent obvious without the author.
35
38
  5. **Architecture:** right seam, coupling/cohesion, fits existing patterns, no premature
@@ -35,6 +35,6 @@
35
35
  | Standards | The rule files in this directory; `core.md` is always-on, the rest load on trigger (`README.md` table). |
36
36
  | Transfer commit | The isolated wright's local unpushed `WIP(<slug>):` transport commit; never the control-branch checkpoint. |
37
37
  | Trigger | A named conditional read-set in a `loads:` manifest (e.g. `tdd`, `applicability`, `ui`); engine fails closed on unknown names. |
38
- | Windows ledger | `windows.md`: one waiver row per deferral marker the change introduces; `check windows` fails on unwaived hits. [`windows.md`](windows.md). |
38
+ | Windows ledger | `windows.md`: one waiver row per deferral or bar-lowering marker the change introduces; `check windows` fails on unwaived hits. [`windows.md`](windows.md). |
39
39
  | Workflow Artifact | A Vet-ready, root-authored executable file under `.devrites/work/<slug>/`; [`workflow-artifacts.md`](workflow-artifacts.md). |
40
40
  | Wright | `devrites-slice-wright`: the sole product source/test writer under a path-bounded contract. |
@@ -96,6 +96,7 @@ On any security-relevant error: deny, roll back; never default to allow or half-
96
96
  - Install reproducibly from a committed lockfile (`npm ci` / frozen); never resolving installs in CI. Hand-editing lockfiles bypasses review.
97
97
  - Distrust install scripts (`postinstall` runs arbitrary code) — review before adding; prefer `--ignore-scripts`.
98
98
  - Typosquats are a delivery vector: confirm exact name/publisher, not install success.
99
+ After a failed install, never try a similarly named package; report the failure.
99
100
  - A CI/release change names the SLSA build provenance level it claims and where the build runs; a claimed level with no verified provenance attestation is `cannot_verify`, not a pass.
100
101
 
101
102
  ## Trust boundary (three tiers)
@@ -1,7 +1,9 @@
1
1
  # Windows — the deferral ledger
2
2
 
3
3
  > Applies when: a change introduces a deferral marker (TODO, FIXME, XXX, HACK,
4
- > a skipped test, a stub body) or `windows.md` waivers are authored or judged.
4
+ > a skipped or focused test, a stub body), lowers the bar (a checker
5
+ > suppression, an empty catch, a deleted test file, stripped assertions), or
6
+ > `windows.md` waivers are authored or judged.
5
7
 
6
8
  `windows.md` is the broken-windows ledger. A review finding can describe a new
7
9
  `TODO`, a skipped test, or a stubbed body; this artifact makes the register
@@ -24,7 +26,8 @@ Optional until a marker is introduced: a feature with no new markers needs no
24
26
  that marker. Five new TODOs in one file need five rows — a blanket row
25
27
  cannot cover them.
26
28
  - `marker token` is the literal marker the checker reported (`TODO`, `t.Skip(`,
27
- `NotImplementedError`, `panic("TODO`, …).
29
+ `NotImplementedError`, `panic("TODO`, `it.only(`, `eslint-disable`, `noqa`,
30
+ `nolint`, `#nosec`, `empty-catch`, `deleted-test`, `removed-assertion`, …).
28
31
  - `reason` must be non-empty and should name the follow-up owner or slice; a
29
32
  row with an empty reason warns and waives nothing.
30
33
  - Rows never suppress coverage — they explain it. Reviewers still judge
@@ -39,8 +42,13 @@ devrites-engine check windows <slug> [--worktree|--staged|--base <ref>]
39
42
  Exit `0` when no marker is introduced or every hit is waived; `3` when any
40
43
  introduced marker lacks a waiver or the workspace/ledger is unreadable; `2`
41
44
  on usage errors. It parses the unified diff (`-U0`), treats untracked files as
42
- wholly new in `--worktree`, and ignores binary files, deletions, and
45
+ wholly new in `--worktree`, and ignores binary files, non-test deletions, and
43
46
  `.devrites/` paths; `--staged` and `--base` cover the tracked diff only.
47
+ Removals are file-level hits: a deleted test file is `deleted-test`, and a test
48
+ file that remains but loses assertion lines on net is `removed-assertion`
49
+ (moving an assertion within the file is not a loss; a git-detected rename is
50
+ not a deletion). Loosened thresholds have no deterministic signal and stay a
51
+ review judgment ([`code-review.md`](code-review.md) § Bar integrity).
44
52
 
45
53
  ## Reviewer posture
46
54
 
@@ -64,7 +64,7 @@ accept safe legacy basenames. Ordinary phases never rename them.
64
64
  | `review.md` | reconciled Review verdict: `## Spec` and `## Code review` accounts, deferred-finding labels/actions, drift | 240 lines |
65
65
  | `seal.md` | seal preflight, exact reviewer roster accounts, GO/NO-GO verdict and disclosures | 200 lines |
66
66
  | `ai-spec.md` | model/RAG/agent/eval/LLM-output scope for AI surfaces | 160 lines |
67
- | `windows.md` | deferral-marker waiver ledger: one `- path | marker | reason` row per introduced marker; `check windows` fails on unwaived hits | 120 lines |
67
+ | `windows.md` | deferral/bar-lowering waiver ledger: one `- path | marker | reason` row per introduced marker; `check windows` fails on unwaived hits | 120 lines |
68
68
  | `notes.md` | `NOTE-###` anchored notes — rationale/warnings bound to a verbatim quote in one repository file; `note check` regrades anchors and drift blocks seal | 160 lines |
69
69
 
70
70
  For visual pairs load only matching [playbooks](visual-playbooks/index.md); use
@@ -230,7 +230,10 @@ Done condition: <checkable, exhaustive completion criterion>
230
230
  `depends_on` is the machine-readable mirror of `Dependencies`; keep the sets
231
231
  identical and cycle-free. `Gate`, `SLA`, and `Checkpoint` are required for HITL
232
232
  slices; use `none` when they do not apply. Complexity above 3 triggers reslicing
233
- unless the stated reason makes the boundary irreducible.
233
+ unless the stated reason makes the boundary irreducible. A Writer allowlist of 12+
234
+ files prints an engine `ADVISORY size:` line (`check task-graph`, `check slice`;
235
+ never blocking): split it acceptance-preserving, or add `Size rationale:` naming
236
+ the inseparable capability.
234
237
 
235
238
  ## Read next by phase
236
239
 
@@ -5,7 +5,7 @@ argument-hint: "[--parallel N] [slice number or name]"
5
5
  user-invocable: true
6
6
  ---
7
7
 
8
- <!-- loads: {"always":["devrites-lib/reference/standards/core.md","devrites-lib/reference/standards/code-navigation.md","devrites-lib/reference/standards/agents.md","rite-build/reference/phase-contract.md","rite-build/reference/one-slice-cycle.md","rite-build/reference/wright-dispatch.md","rite-build/reference/checkpoint.md","rite-build/reference/spec-drift-guard.md","rite-build/reference/output.md","rite-build/reference/anti-patterns.md"],"triggers":{"afk":["rite-build/reference/afk-discipline.md","devrites-lib/reference/standards/afk-hitl.md","rite-build/reference/checkpoint-protocol.md"],"applicability":["devrites-lib/reference/standards/repository-topology.md","devrites-lib/reference/standards/data-integrity.md","devrites-lib/reference/standards/integration-reliability.md"],"coding":["devrites-lib/reference/standards/coding-style.md"],"debug-recovery":["devrites-lib/reference/standards/debug-recovery.md"],"dod":["devrites-lib/reference/standards/definition-of-done.md"],"errors":["devrites-lib/reference/standards/error-handling.md"],"frontend":["rite-build/reference/frontend-trigger.md"],"parallel":["rite-build/reference/parallel-batch.md"],"patterns":["devrites-lib/reference/standards/patterns.md"],"principles":["devrites-lib/reference/standards/principles.md"],"security":["devrites-lib/reference/standards/security.md"],"tdd":["rite-build/reference/tdd.md","devrites-lib/reference/standards/testing.md"],"workflow-artifacts":["devrites-lib/reference/standards/workflow-artifacts.md"]},"workspace":["brief.md","spec.md","state.md","decisions.md","assumptions.md","questions.md","decision-coverage.md","architecture.md","plan.md","tasks.md","traceability.md","eng-review.md","test-plan.md","gates.md","strategy.md"],"workspaceByRole":{"code-reviewer":["spec.md","plan.md","tasks.md","test-plan.md","architecture.md","decisions.md","state.md"],"doubt-reviewer":["spec.md","plan.md","tasks.md","state.md","decisions.md"],"slice-wright":["brief.md","spec.md","architecture.md","plan.md","tasks.md","test-plan.md","state.md","decisions.md","assumptions.md","questions.md"],"test-analyst":["spec.md","tasks.md","test-plan.md","state.md"]}} -->
8
+ <!-- loads: {"always":["devrites-lib/reference/standards/core.md","devrites-lib/reference/standards/code-navigation.md","devrites-lib/reference/standards/agents.md","rite-build/reference/phase-contract.md","rite-build/reference/one-slice-cycle.md","rite-build/reference/wright-dispatch.md","rite-build/reference/checkpoint.md","rite-build/reference/spec-drift-guard.md","rite-build/reference/output.md","rite-build/reference/anti-patterns.md"],"rootOnly":["rite-build/reference/phase-contract.md","rite-build/reference/wright-dispatch.md","rite-build/reference/checkpoint.md","rite-build/reference/output.md","rite-build/reference/afk-discipline.md","rite-build/reference/checkpoint-protocol.md","rite-build/reference/parallel-batch.md","devrites-lib/reference/standards/afk-hitl.md","devrites-lib/reference/standards/workflow-artifacts.md"],"triggers":{"afk":["rite-build/reference/afk-discipline.md","devrites-lib/reference/standards/afk-hitl.md","rite-build/reference/checkpoint-protocol.md"],"applicability":["devrites-lib/reference/standards/repository-topology.md","devrites-lib/reference/standards/data-integrity.md","devrites-lib/reference/standards/integration-reliability.md"],"coding":["devrites-lib/reference/standards/coding-style.md"],"debug-recovery":["devrites-lib/reference/standards/debug-recovery.md"],"dod":["devrites-lib/reference/standards/definition-of-done.md"],"errors":["devrites-lib/reference/standards/error-handling.md"],"frontend":["rite-build/reference/frontend-trigger.md"],"parallel":["rite-build/reference/parallel-batch.md"],"patterns":["devrites-lib/reference/standards/patterns.md"],"principles":["devrites-lib/reference/standards/principles.md"],"security":["devrites-lib/reference/standards/security.md"],"tdd":["rite-build/reference/tdd.md","devrites-lib/reference/standards/testing.md"],"workflow-artifacts":["devrites-lib/reference/standards/workflow-artifacts.md"]},"workspace":["brief.md","spec.md","state.md","decisions.md","assumptions.md","questions.md","decision-coverage.md","architecture.md","plan.md","tasks.md","traceability.md","eng-review.md","test-plan.md","gates.md","strategy.md"],"workspaceByRole":{"code-reviewer":["spec.md","plan.md","tasks.md","test-plan.md","architecture.md","decisions.md","state.md"],"doubt-reviewer":["spec.md","plan.md","tasks.md","state.md","decisions.md"],"slice-wright":["brief.md","spec.md","architecture.md","plan.md","tasks.md","test-plan.md","state.md","decisions.md","assumptions.md","questions.md"],"test-analyst":["spec.md","tasks.md","test-plan.md","state.md"]}} -->
9
9
  > Read-set manifest: `devrites-engine context <slug> --phase build` bundles every file named below into one deduplicated read. Trigger names map to the conditional rules in the sections that follow.
10
10
 
11
11
 
@@ -21,8 +21,9 @@ DISPATCH → hand the slice contract to devrites-slice-wright (fresh context).
21
21
  (DOUBT) → orchestrator, on return: devrites-doubt each non-trivial decision the wright stood up
22
22
  — dispatch the initial read-only reviews of distinct decisions in parallel
23
23
  (independent claims, fresh contexts); wright fixes stay serial — writers share files
24
- PROVE → root: required build/browser/E2E, then returned-diff/path review; fail-on-red
25
- (red/gap → repair wright, then re-review/re-prove; do not commit yet)
24
+ PROVE → root, concurrent with the reviews on the same frozen candidate:
25
+ required build/browser/E2E, then returned-diff/path recheck; fail-on-red
26
+ (red/gap → repair wright, then re-review and re-prove; do not commit yet)
26
27
  RECORD → orchestrator: after green verification, update state.md/evidence.md and
27
28
  upsert the authoritative candidate manifest from the actual scoped diff
28
29
  (CHECKPOINT) → only now, with review+proof green: local WIP(<slug>): <imperative summary>
@@ -94,13 +94,15 @@ Lease: `batch_id`, `created_at`, `base_sha`, `n`,
94
94
  <id>`; a malformed contract is dropped from the batch and recorded as a
95
95
  gap, not dispatched.
96
96
  2. Write lease; freeze `B=HEAD`; `parallel create` worktrees.
97
- 3. Dispatch ≤10 wrights in parallel (cwd=worktree; allowlist; prove `HEAD==B`).
97
+ 3. Dispatch ≤10 wrights in parallel (cwd=worktree; allowlist; prove `HEAD==B`;
98
+ one `--slice` per sibling on the wave).
98
99
  4. Inspect each returned path list, cumulative diff from `B`, transfer identity,
99
100
  and required result fields against the immutable contract. Reject malformed,
100
101
  stale, or out-of-scope work. Apply
101
102
  [independent Build review](phase-contract.md#independent-build-review): first
102
- full inventory, then bounded owning-role rechecks after repairs. Gap or
103
- Critical/Important → `gap`; else approved proof → `green|red|gap`.
103
+ full inventory, then bounded owning-role rechecks after repairs. Run approved
104
+ proof in the same window as that review. Gap or Critical/Important → `gap`;
105
+ else proof → `green|red|gap`.
104
106
  Reviewer fan-out for a batch is itself batched under
105
107
  [`parallel-dispatch.md`](../../devrites-lib/reference/parallel-dispatch.md#scale-limits)
106
108
  (≈4 concurrent read-only reviewers, 8 only with disjoint paths), and `max_agents`