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.
- package/CHANGELOG.md +16 -0
- package/README.md +15 -64
- package/SECURITY.md +7 -11
- package/bin/devrites.mjs +0 -8
- package/docs/adr/0034-workflow-artifact-byte-corrections-get-code-review.md +60 -0
- package/docs/adr/README.md +2 -1
- package/docs/architecture.md +2 -5
- package/docs/cli.md +4 -11
- package/docs/command-map.md +1 -2
- package/docs/engine/commands.md +6 -6
- package/docs/release.md +5 -6
- package/engine/help.go +12 -7
- package/engine/help_test.go +1 -0
- package/engine/internal/gate/readiness_binding.go +19 -0
- package/engine/internal/lib/cli_observe.go +16 -0
- package/engine/internal/lib/context.go +191 -19
- package/engine/internal/lib/context_next_test.go +186 -0
- package/engine/internal/lib/dispatch.go +146 -11
- package/engine/internal/lib/drift.go +10 -2
- package/engine/internal/lib/drift_test.go +29 -0
- package/engine/internal/lib/slicecheck.go +19 -0
- package/engine/internal/lib/slicecheck_test.go +41 -0
- package/engine/internal/lib/windows.go +109 -13
- package/engine/internal/lib/windows_test.go +85 -0
- package/engine/internal/release/release.go +3 -13
- package/engine/internal/release/release_test.go +6 -197
- package/engine/main.go +2 -2
- package/install.sh +1 -116
- package/pack/.claude/agents/devrites-slice-wright.md +3 -1
- package/pack/.claude/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/README.md +1 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +3 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/glossary.md +1 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/security.md +1 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/windows.md +11 -3
- package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
- package/pack/.claude/skills/rite-build/SKILL.md +1 -1
- package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/.claude/skills/rite-build/reference/parallel-batch.md +5 -3
- package/pack/.claude/skills/rite-build/reference/phase-contract.md +5 -3
- package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +3 -1
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +23 -9
- package/pack/.claude/skills/rite-define/SKILL.md +2 -1
- package/pack/.claude/skills/rite-plan/SKILL.md +2 -1
- package/pack/.claude/skills/rite-pressure-test/SKILL.md +6 -3
- package/pack/.claude/skills/rite-vet/SKILL.md +9 -1
- package/pack/.claude/skills/rite-vet/reference/review-axes.md +6 -2
- package/pack/generated/claude/agents/devrites-slice-wright.md +3 -1
- package/pack/generated/claude/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
- package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +1 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
- package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +3 -0
- package/pack/generated/claude/skills/devrites-lib/reference/standards/glossary.md +1 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +1 -0
- package/pack/generated/claude/skills/devrites-lib/reference/standards/windows.md +11 -3
- package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
- package/pack/generated/claude/skills/rite-build/SKILL.md +1 -1
- package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +5 -3
- package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +5 -3
- package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +3 -1
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +23 -9
- package/pack/generated/claude/skills/rite-define/SKILL.md +2 -1
- package/pack/generated/claude/skills/rite-plan/SKILL.md +2 -1
- package/pack/generated/claude/skills/rite-pressure-test/SKILL.md +6 -3
- package/pack/generated/claude/skills/rite-vet/SKILL.md +9 -1
- package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +6 -2
- package/pack/generated/codex/agents/devrites-slice-wright.toml +3 -1
- package/pack/generated/codex/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
- package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +1 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
- package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +3 -0
- package/pack/generated/codex/skills/devrites-lib/reference/standards/glossary.md +1 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +1 -0
- package/pack/generated/codex/skills/devrites-lib/reference/standards/windows.md +11 -3
- package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
- package/pack/generated/codex/skills/rite-build/SKILL.md +1 -1
- package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +5 -3
- package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +5 -3
- package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +3 -1
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +23 -9
- package/pack/generated/codex/skills/rite-define/SKILL.md +2 -1
- package/pack/generated/codex/skills/rite-plan/SKILL.md +2 -1
- package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +6 -3
- package/pack/generated/codex/skills/rite-vet/SKILL.md +9 -1
- package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +6 -2
- package/pack/generated/devin/agents/devrites-slice-wright.md +3 -1
- package/pack/generated/devin/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
- package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
- package/pack/generated/devin/skills/devrites-lib/reference/standards/README.md +1 -1
- package/pack/generated/devin/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
- package/pack/generated/devin/skills/devrites-lib/reference/standards/code-review.md +3 -0
- package/pack/generated/devin/skills/devrites-lib/reference/standards/glossary.md +1 -1
- package/pack/generated/devin/skills/devrites-lib/reference/standards/security.md +1 -0
- package/pack/generated/devin/skills/devrites-lib/reference/standards/windows.md +11 -3
- package/pack/generated/devin/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
- package/pack/generated/devin/skills/rite-build/SKILL.md +1 -1
- package/pack/generated/devin/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +5 -3
- package/pack/generated/devin/skills/rite-build/reference/phase-contract.md +5 -3
- package/pack/generated/devin/skills/rite-build/reference/spec-drift-guard.md +3 -1
- package/pack/generated/devin/skills/rite-build/reference/wright-dispatch.md +23 -9
- package/pack/generated/devin/skills/rite-define/SKILL.md +2 -1
- package/pack/generated/devin/skills/rite-plan/SKILL.md +2 -1
- package/pack/generated/devin/skills/rite-pressure-test/SKILL.md +6 -3
- package/pack/generated/devin/skills/rite-vet/SKILL.md +9 -1
- package/pack/generated/devin/skills/rite-vet/reference/review-axes.md +6 -2
- package/pack/generated/omp/agents/devrites-slice-wright.md +3 -1
- package/pack/generated/omp/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
- package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
- package/pack/generated/omp/skills/devrites-lib/reference/standards/README.md +1 -1
- package/pack/generated/omp/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
- package/pack/generated/omp/skills/devrites-lib/reference/standards/code-review.md +3 -0
- package/pack/generated/omp/skills/devrites-lib/reference/standards/glossary.md +1 -1
- package/pack/generated/omp/skills/devrites-lib/reference/standards/security.md +1 -0
- package/pack/generated/omp/skills/devrites-lib/reference/standards/windows.md +11 -3
- package/pack/generated/omp/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
- package/pack/generated/omp/skills/rite-build/SKILL.md +1 -1
- package/pack/generated/omp/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +5 -3
- package/pack/generated/omp/skills/rite-build/reference/phase-contract.md +5 -3
- package/pack/generated/omp/skills/rite-build/reference/spec-drift-guard.md +3 -1
- package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +23 -9
- package/pack/generated/omp/skills/rite-define/SKILL.md +2 -1
- package/pack/generated/omp/skills/rite-plan/SKILL.md +2 -1
- package/pack/generated/omp/skills/rite-pressure-test/SKILL.md +6 -3
- package/pack/generated/omp/skills/rite-vet/SKILL.md +9 -1
- package/pack/generated/omp/skills/rite-vet/reference/review-axes.md +6 -2
- package/pack/generated/pi/agents/devrites-slice-wright.md +3 -1
- package/pack/generated/pi/skills/devrites-debug-recovery/reference/hypotheses.md +6 -0
- package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +1 -0
- package/pack/generated/pi/skills/devrites-lib/reference/standards/README.md +1 -1
- package/pack/generated/pi/skills/devrites-lib/reference/standards/afk-hitl.md +2 -0
- package/pack/generated/pi/skills/devrites-lib/reference/standards/code-review.md +3 -0
- package/pack/generated/pi/skills/devrites-lib/reference/standards/glossary.md +1 -1
- package/pack/generated/pi/skills/devrites-lib/reference/standards/security.md +1 -0
- package/pack/generated/pi/skills/devrites-lib/reference/standards/windows.md +11 -3
- package/pack/generated/pi/skills/devrites-lib/reference/workspace-artifact-schema.md +5 -2
- package/pack/generated/pi/skills/rite-build/SKILL.md +1 -1
- package/pack/generated/pi/skills/rite-build/reference/one-slice-cycle.md +3 -2
- package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +5 -3
- package/pack/generated/pi/skills/rite-build/reference/phase-contract.md +5 -3
- package/pack/generated/pi/skills/rite-build/reference/spec-drift-guard.md +3 -1
- package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +23 -9
- package/pack/generated/pi/skills/rite-define/SKILL.md +2 -1
- package/pack/generated/pi/skills/rite-plan/SKILL.md +2 -1
- package/pack/generated/pi/skills/rite-pressure-test/SKILL.md +6 -3
- package/pack/generated/pi/skills/rite-vet/SKILL.md +9 -1
- package/pack/generated/pi/skills/rite-vet/reference/review-axes.md +6 -2
- package/package.json +1 -1
- 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
|
|
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
|
|
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
|
|
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
|
-
|
|
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("
|
|
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("
|
|
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("
|
|
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 '
|
|
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)
|
|
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-
|
|
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
|
|
25
|
-
|
|
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.
|
|
103
|
-
|
|
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`
|