devrites 6.7.3 → 6.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/docs/engine/commands.md +3 -3
- package/engine/internal/acceptance/evidence.go +28 -5
- package/engine/internal/acceptance/inputs.go +93 -0
- package/engine/internal/acceptance/ledger.go +5 -2
- package/engine/internal/acceptance/run.go +18 -5
- package/engine/internal/lib/candidate_coverage.go +147 -0
- package/engine/internal/lib/dispatch.go +5 -0
- package/engine/main.go +4 -0
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +2 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/gates.md +16 -7
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +3 -1
- package/pack/.claude/skills/rite-review/SKILL.md +6 -1
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +2 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/gates.md +16 -7
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +3 -1
- package/pack/generated/claude/skills/rite-review/SKILL.md +6 -1
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +2 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/gates.md +16 -7
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +3 -1
- package/pack/generated/codex/skills/rite-review/SKILL.md +6 -1
- package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +2 -1
- package/pack/generated/devin/skills/devrites-lib/reference/standards/gates.md +16 -7
- package/pack/generated/devin/skills/rite-autocomplete/SKILL.md +3 -1
- package/pack/generated/devin/skills/rite-review/SKILL.md +6 -1
- package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +2 -1
- package/pack/generated/omp/skills/devrites-lib/reference/standards/gates.md +16 -7
- package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +3 -1
- package/pack/generated/omp/skills/rite-review/SKILL.md +6 -1
- package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +2 -1
- package/pack/generated/pi/skills/devrites-lib/reference/standards/gates.md +16 -7
- package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +3 -1
- package/pack/generated/pi/skills/rite-review/SKILL.md +6 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to DevRites are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and DevRites adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Releases are generated automatically by [semantic-release](https://semantic-release.gitbook.io/) from Conventional Commits on `main`.
|
|
4
4
|
|
|
5
|
+
## [6.8.0](https://github.com/ViktorsBaikers/DevRites/compare/v6.7.4...v6.8.0) (2026-10-18)
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
* **devrites:** reuse unchanged gates and refuse voided review waves ([#129](https://github.com/ViktorsBaikers/DevRites/issues/129)) ([51349fe](https://github.com/ViktorsBaikers/DevRites/commit/51349fe0ce8f96ad890461c0e796bf3184e49079))
|
|
10
|
+
|
|
11
|
+
## [6.7.4](https://github.com/ViktorsBaikers/DevRites/compare/v6.7.3...v6.7.4) (2026-10-17)
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
* **devrites:** a host reminder is not a turn end ([#128](https://github.com/ViktorsBaikers/DevRites/issues/128)) ([45f4922](https://github.com/ViktorsBaikers/DevRites/commit/45f492269f568449572f27d355641d181d6dd21c))
|
|
16
|
+
|
|
5
17
|
## [6.7.3](https://github.com/ViktorsBaikers/DevRites/compare/v6.7.2...v6.7.3) (2026-10-17)
|
|
6
18
|
|
|
7
19
|
### Fixed
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ history can't lose it. After you clear the conversation, switch hosts, or hand
|
|
|
31
31
|
the feature to another engineer, the next session reads the same plan,
|
|
32
32
|
decisions, and evidence before it continues.
|
|
33
33
|
|
|
34
|
-
**Status:** [`v6.
|
|
34
|
+
**Status:** [`v6.8.0`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v6.8.0): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
|
|
35
35
|
|
|
36
36
|
This is the latest published release; `main` may contain unreleased work.
|
|
37
37
|
|
package/docs/engine/commands.md
CHANGED
|
@@ -11,7 +11,7 @@ workflow strategy.
|
|
|
11
11
|
| `install [flags]` | Install manifest-owned host artifacts and the optional shared binary. |
|
|
12
12
|
| `update [flags]` | Refresh an existing managed installation. |
|
|
13
13
|
| `uninstall [flags]` | Remove managed artifacts while preserving runtime workspace state. |
|
|
14
|
-
| `check candidate <slug>` | Validate the strict manifest and compute the content-bound project-candidate identity. |
|
|
14
|
+
| `check candidate <slug>` | Validate the strict manifest and compute the content-bound project-candidate identity. Exits `3` when the manifest omits a path the feature's `WIP(<slug>):` checkpoint commits changed (net from the feature base to HEAD). |
|
|
15
15
|
| `check readiness <slug>` | Check target-Phase files, open human gates from Clarify onward, the `tasks.md` slice graph when that artifact is required, canonical `AC-###` presence in `tasks.md`/`test-plan.md` when those artifacts are required, and the current stable Build-input binding when applicable. |
|
|
16
16
|
| `check readiness --emit-binding <slug>` | Render the exact stable Build-input binding for Vet to record after review. |
|
|
17
17
|
| `check seal <slug>` | Check files required by target Phase `seal`, open human gates, the `tasks.md` slice graph, canonical `AC-###` presence when `tasks.md`/`test-plan.md` are required, the stable Build-input binding, and exact candidate bindings. |
|
|
@@ -34,12 +34,12 @@ workflow strategy.
|
|
|
34
34
|
| `handoff [slug]` | Emit the deterministic resume record: cursor fields, `awaiting_human`, open `questions.md` gate kinds, the `gates.md` reduction with unmet/stale/abandoned ids, `decisions.md` dead ends, missing required files, and the canonical read-next order. Read-only; the `/rite-handoff` spine and the compaction-fallback entry point. |
|
|
35
35
|
| `recall --paths <p1,p2,...> [--limit n]` | Read-time query over `.devrites/archive/`: list archived features whose `touched-files.md` candidate manifest covers a queried project-relative path (exact or directory prefix, case-folded), ranked by overlap count, then newest archive-directory mtime, then slug (default 10 features). Each feature prints up to 8 `archive/<slug>/<file>:<line>` pointers, then `+N more`: `decisions.md` `DEC-###` entries and `## Carry-forward constraints` bullets whose `Files:` overlap (or a constraint naming no files), plus `review.md`/`seal.md`/`drift.md` lines that mention a queried path; fenced examples are ignored. Never writes, caches, or indexes (no metric either); symlinked archive entries are refused. An unreadable archive file prints `unreadable:` and the scan continues. Exit `0` (including `recall: no archive`), `1` invalid archive directory, `2` usage. |
|
|
36
36
|
| `context [slug] (--phase <p>\|--skill <name>) [--role <r>] [--slice <SLICE-ID>] [--trigger a,b]` | Emit one deduplicated read-set bundle per phase/role/skill from each skill's `loads:` manifest; `--skill` works without a workspace when `--out` is given. A bundle without `--role` (the root's own read) gives over-budget artifacts as by-reference indexes; a `--role` bundle leads with the role's agent contract and names the manifest's `rootOnly` paths instead of inlining them. `--slice` inlines that slice's `tasks.md` block and turns each over-budget artifact, and each artifact over 16 KiB except `spec.md`, into a by-reference index (path, SHA-256, headings, line hits for every ID the block cites); repeat `--slice` for several slices; an unknown slice exits `3`. |
|
|
37
|
-
| `dispatch <slug> <open\|start\|seal\|return\|status\|abandon>` | Launch-wave barrier for parallel dispatch: seal needs a handle per role, return needs seal; start/return auto-record metrics. A build wave with a wright role needs `--slice <SLICE-ID>` per slice, and a slice-bound vet wave takes it too; a reviewer, auditor, or test-analyst wave needs `--packet <file>` and `open` exits `3` on a packet that seeds a conclusion (gate `*-PASS` token, `no-findings`, an `Outcome:`/`Counts:` line, an "already passed" claim, or zero Critical/Important counts); every 5 rounds of either kind on one slice, `open` exits `3` until `--reslice-review <DRIFT-ID>` names an unused `drift.md` `## <DRIFT-ID>` heading for that slice. `open` also warns when an earlier wave for the same slice never returned. |
|
|
37
|
+
| `dispatch <slug> <open\|start\|seal\|return\|status\|abandon>` | Launch-wave barrier for parallel dispatch: seal needs a handle per role, return needs seal; start/return auto-record metrics. A build wave with a wright role needs `--slice <SLICE-ID>` per slice, and a slice-bound vet wave takes it too; a reviewer, auditor, or test-analyst wave needs `--packet <file>` and `open` exits `3` on a packet that seeds a conclusion (gate `*-PASS` token, `no-findings`, an `Outcome:`/`Counts:` line, an "already passed" claim, or zero Critical/Important counts); a Review or Seal reviewer wave also exits `3` unless the packet names an existing non-empty `.diff`/`.patch` file and the manifest covers every checkpointed path; every 5 rounds of either kind on one slice, `open` exits `3` until `--reslice-review <DRIFT-ID>` names an unused `drift.md` `## <DRIFT-ID>` heading for that slice. `open` also warns when an earlier wave for the same slice never returned. |
|
|
38
38
|
| `metrics record <slug> --phase <p> --event <e> [--role r] [--bytes n] [--note s]` | Append an event to the workspace's `metrics.jsonl` ledger. |
|
|
39
39
|
| `metrics summary [slug] [--by-slice]` | Reduce `metrics.jsonl` into a per-phase roll-up. `--by-slice` reads `dispatch.json` instead: per slice, build wright rounds, slice-bound Vet rechecks, and agent-minutes (role start to `returned_at`; abandoned waves excluded). |
|
|
40
40
|
| `claim <add\|release\|list\|check>` | Advisory session-scoped file claims in `.devrites/claims.jsonl` (append-only). `add` records intent to write a path set with a TTL (1–240 min, default 30); a live claim by another session exits `3` and names the holder. `release` appends a release event owned by the claiming session. `list` shows live claims (`--all` includes expired/released); a claim whose holding session has provably exited on this host shows `dead` and no longer conflicts. `check` is the read-only preflight for a path set. Coordination aid only — never a substitute for one-writer-per-worktree. |
|
|
41
41
|
| `note <add\|list\|check\|rm> <slug> ...` | Anchored workspace notes in `notes.md`: `add <slug> <subject> <quote> <title> [body]` binds a note to a verbatim quote in one repository file and assigns `NOTE-###`; `list` and `check` regrade each anchor `exact`/`moved`/`stale`/`ambiguous`/`lost` by searching the repo for the quote (dependency and workspace trees excluded); `check --repair` rewrites a `moved` subject to its new file; `rm` deletes by ID. `check seal` refuses a malformed `notes.md` and any non-`exact` anchor. |
|
|
42
|
-
| `gates <scaffold\|status\|run\|reverify\|lint\|attest\|abandon> <slug>` | Operate the `gates.md` acceptance ledger: seed per-AC gates, execute runnable gates bound to `test-plan.md` preflight rows, attest manual gates, and reduce to `all-met`/`not-met`/`handoff`/`malformed`. |
|
|
42
|
+
| `gates <scaffold\|status\|run\|reverify\|lint\|attest\|abandon> <slug>` | Operate the `gates.md` acceptance ledger: seed per-AC gates, execute runnable gates bound to `test-plan.md` preflight rows, attest manual gates, and reduce to `all-met`/`not-met`/`handoff`/`malformed`. A gate with `INPUTS:` records its files' content digest and is reused by `run` and `reverify` while they are unchanged. |
|
|
43
43
|
| `state resolve <qid> "<answer>"` | Resolve an open question and update `questions.md` plus `state.md` atomically. |
|
|
44
44
|
| `state merge-manifest <slug> [pred...]` | Fold the recorded predecessor chain's manifests into the release candidate manifest. |
|
|
45
45
|
| `state close <slug>` | Archive a shipped workspace and clear matching `ACTIVE`. |
|
|
@@ -25,14 +25,19 @@ const (
|
|
|
25
25
|
// manual gate keeps human attestation.
|
|
26
26
|
const EvidenceMarker = "automatic-evidence=v1"
|
|
27
27
|
|
|
28
|
-
var automaticEvidenceRE = regexp.MustCompile(`^automatic-evidence=v1; def=([0-9a-f]{64}); exit=0; expect=matched; output-sha256=([0-9a-f]{64}); output-bytes=([0-9]+)
|
|
28
|
+
var automaticEvidenceRE = regexp.MustCompile(`^automatic-evidence=v1; def=([0-9a-f]{64}); exit=0; expect=matched; output-sha256=([0-9a-f]{64}); output-bytes=([0-9]+)(?:; inputs=([0-9a-f]{64}))?; at=(\S+)$`)
|
|
29
29
|
|
|
30
30
|
// DefinitionDigest is the environment-independent SHA-256 binding of the
|
|
31
|
-
// parsed CHECK, EXPECT,
|
|
32
|
-
// previously recorded evidence stale. The digest is unkeyed
|
|
33
|
-
// not tamper proof: a ledger editor can write a syntactically
|
|
31
|
+
// parsed CHECK, EXPECT, raw CWD and (when declared) INPUTS fields. Editing any
|
|
32
|
+
// bound input renders previously recorded evidence stale. The digest is unkeyed
|
|
33
|
+
// drift detection, not tamper proof: a ledger editor can write a syntactically
|
|
34
|
+
// valid line.
|
|
34
35
|
func DefinitionDigest(gate *Gate) string {
|
|
35
|
-
|
|
36
|
+
definition := "v1\x00" + gate.Check + "\x00" + gate.Expect + "\x00" + gate.Cwd
|
|
37
|
+
if gate.Inputs != "" {
|
|
38
|
+
definition += "\x00inputs\x00" + gate.Inputs
|
|
39
|
+
}
|
|
40
|
+
sum := sha256.Sum256([]byte(definition))
|
|
36
41
|
return hex.EncodeToString(sum[:])
|
|
37
42
|
}
|
|
38
43
|
|
|
@@ -42,6 +47,24 @@ func FormatEvidence(gate *Gate, outputDigest string, outputBytes int, at string)
|
|
|
42
47
|
EvidenceMarker, DefinitionDigest(gate), outputDigest, outputBytes, at)
|
|
43
48
|
}
|
|
44
49
|
|
|
50
|
+
// withInputs adds the INPUTS content digest to an evidence payload, so a later
|
|
51
|
+
// run can tell whether the files the result depends on are unchanged.
|
|
52
|
+
func withInputs(evidence, inputsDigest string) string {
|
|
53
|
+
if inputsDigest == "" {
|
|
54
|
+
return evidence
|
|
55
|
+
}
|
|
56
|
+
at := strings.LastIndex(evidence, "; at=")
|
|
57
|
+
return evidence[:at] + "; inputs=" + inputsDigest + evidence[at:]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// recordedInputs is the INPUTS digest a gate's automatic evidence carries, or "".
|
|
61
|
+
func recordedInputs(gate *Gate) string {
|
|
62
|
+
if match := automaticEvidenceRE.FindStringSubmatch(strings.TrimSpace(gate.Evidence)); match != nil {
|
|
63
|
+
return match[4]
|
|
64
|
+
}
|
|
65
|
+
return ""
|
|
66
|
+
}
|
|
67
|
+
|
|
45
68
|
// OutputDigest is the recorded fingerprint of the canonical successful output
|
|
46
69
|
// string. Raw output is never persisted.
|
|
47
70
|
func OutputDigest(output string) string {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
package acceptance
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"crypto/sha256"
|
|
5
|
+
"encoding/hex"
|
|
6
|
+
"errors"
|
|
7
|
+
"fmt"
|
|
8
|
+
"io"
|
|
9
|
+
"io/fs"
|
|
10
|
+
"os"
|
|
11
|
+
"path"
|
|
12
|
+
"path/filepath"
|
|
13
|
+
"sort"
|
|
14
|
+
"strings"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
// maxInputFiles bounds one INPUTS expansion so a broad glob cannot turn a
|
|
18
|
+
// gate's reuse check into a repository-wide hash.
|
|
19
|
+
const maxInputFiles = 20000
|
|
20
|
+
|
|
21
|
+
// InputsDigest hashes every regular file an INPUTS spec names under project:
|
|
22
|
+
// whitespace-separated project-relative paths, directories (walked
|
|
23
|
+
// recursively, .git skipped) or globs. Each pattern must match at least one
|
|
24
|
+
// file, so a typo fails closed (the gate re-executes) instead of hashing
|
|
25
|
+
// nothing. The digest binds each sorted relative path to its content.
|
|
26
|
+
func InputsDigest(project, spec string) (string, error) {
|
|
27
|
+
files := map[string]bool{}
|
|
28
|
+
for _, pattern := range strings.Fields(spec) {
|
|
29
|
+
clean := path.Clean(pattern)
|
|
30
|
+
if path.IsAbs(clean) || clean == ".." || strings.HasPrefix(clean, "../") || strings.Contains(pattern, `\`) {
|
|
31
|
+
return "", fmt.Errorf("INPUTS %q must be a project-relative slash path", pattern)
|
|
32
|
+
}
|
|
33
|
+
matches, err := filepath.Glob(filepath.Join(project, filepath.FromSlash(clean)))
|
|
34
|
+
if err != nil {
|
|
35
|
+
return "", fmt.Errorf("INPUTS %q: %w", pattern, err)
|
|
36
|
+
}
|
|
37
|
+
before := len(files)
|
|
38
|
+
for _, match := range matches {
|
|
39
|
+
if err := addInputFiles(project, match, files); err != nil {
|
|
40
|
+
return "", err
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if len(files) == before {
|
|
44
|
+
return "", fmt.Errorf("INPUTS %q matches no file", pattern)
|
|
45
|
+
}
|
|
46
|
+
if len(files) > maxInputFiles {
|
|
47
|
+
return "", fmt.Errorf("INPUTS names more than %d files", maxInputFiles)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
names := make([]string, 0, len(files))
|
|
51
|
+
for name := range files {
|
|
52
|
+
names = append(names, name)
|
|
53
|
+
}
|
|
54
|
+
sort.Strings(names)
|
|
55
|
+
digest := sha256.New()
|
|
56
|
+
for _, name := range names {
|
|
57
|
+
file, err := os.Open(filepath.Join(project, filepath.FromSlash(name))) // #nosec G304 -- path enumerated under the project root
|
|
58
|
+
if err != nil {
|
|
59
|
+
return "", err
|
|
60
|
+
}
|
|
61
|
+
content := sha256.New()
|
|
62
|
+
_, err = io.Copy(content, file)
|
|
63
|
+
_ = file.Close()
|
|
64
|
+
if err != nil {
|
|
65
|
+
return "", err
|
|
66
|
+
}
|
|
67
|
+
fmt.Fprintf(digest, "%d:%s\x00%x\n", len(name), name, content.Sum(nil))
|
|
68
|
+
}
|
|
69
|
+
return hex.EncodeToString(digest.Sum(nil)), nil
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
func addInputFiles(project, start string, files map[string]bool) error {
|
|
73
|
+
return filepath.WalkDir(start, func(name string, entry fs.DirEntry, err error) error {
|
|
74
|
+
if err != nil {
|
|
75
|
+
return err
|
|
76
|
+
}
|
|
77
|
+
if entry.IsDir() {
|
|
78
|
+
if entry.Name() == ".git" {
|
|
79
|
+
return filepath.SkipDir
|
|
80
|
+
}
|
|
81
|
+
return nil
|
|
82
|
+
}
|
|
83
|
+
if !entry.Type().IsRegular() {
|
|
84
|
+
return nil
|
|
85
|
+
}
|
|
86
|
+
rel, err := filepath.Rel(project, name)
|
|
87
|
+
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
|
88
|
+
return errors.New("INPUTS path escapes the project")
|
|
89
|
+
}
|
|
90
|
+
files[filepath.ToSlash(rel)] = true
|
|
91
|
+
return nil
|
|
92
|
+
})
|
|
93
|
+
}
|
|
@@ -30,6 +30,7 @@ type Gate struct {
|
|
|
30
30
|
Check string
|
|
31
31
|
Expect string
|
|
32
32
|
Cwd string
|
|
33
|
+
Inputs string // optional whitespace-separated paths/globs the result depends on
|
|
33
34
|
Evidence string
|
|
34
35
|
Line int // 1-based line of the "- [ ]" row in the original text
|
|
35
36
|
}
|
|
@@ -46,8 +47,8 @@ type Document struct {
|
|
|
46
47
|
|
|
47
48
|
var (
|
|
48
49
|
gateLineRE = regexp.MustCompile(`^\s*- \[([ xX])\] ([^\s:]+): (.*)$`)
|
|
49
|
-
attrLineRE = regexp.MustCompile(`^(\s+)(CHECK|EXPECT|CWD|EVIDENCE):(.*)$`)
|
|
50
|
-
unindentedAttr = regexp.MustCompile(`^(CHECK|EXPECT|CWD|EVIDENCE):`)
|
|
50
|
+
attrLineRE = regexp.MustCompile(`^(\s+)(CHECK|EXPECT|CWD|INPUTS|EVIDENCE):(.*)$`)
|
|
51
|
+
unindentedAttr = regexp.MustCompile(`^(CHECK|EXPECT|CWD|INPUTS|EVIDENCE):`)
|
|
51
52
|
abandonLineRE = regexp.MustCompile(`^ABANDON: (\S+)\s*(.*)$`)
|
|
52
53
|
indentedAbandon = regexp.MustCompile(`^\s+ABANDON:`)
|
|
53
54
|
expectSlashRE = regexp.MustCompile(`^/(.*)/([ims]*)$`)
|
|
@@ -114,6 +115,8 @@ func ParseLedger(text string) *Document {
|
|
|
114
115
|
current.Expect = value
|
|
115
116
|
case "CWD":
|
|
116
117
|
current.Cwd = value
|
|
118
|
+
case "INPUTS":
|
|
119
|
+
current.Inputs = value
|
|
117
120
|
case "EVIDENCE":
|
|
118
121
|
current.Evidence = value
|
|
119
122
|
}
|
|
@@ -87,9 +87,22 @@ func RunGates(ctx context.Context, root, ledgerPath string, doc *Document, appro
|
|
|
87
87
|
results = append(results, GateResult{ID: gate.ID, Detail: "manual gate; evidence is judged, not executed"})
|
|
88
88
|
continue
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
inputs := ""
|
|
91
|
+
if gate.Inputs != "" {
|
|
92
|
+
// An unreadable or unmatched spec yields "", which never matches, so the gate re-executes.
|
|
93
|
+
inputs, _ = InputsDigest(root, gate.Inputs)
|
|
94
|
+
}
|
|
95
|
+
if GateState(gate, doc) == StateMet {
|
|
96
|
+
if gate.Inputs == "" && !reverify {
|
|
97
|
+
results = append(results, GateResult{ID: gate.ID, Passed: true, Detail: "already met"})
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
// A gate that declares INPUTS is reused, under run and reverify alike,
|
|
101
|
+
// exactly while the files it depends on are byte-identical; any change re-executes it.
|
|
102
|
+
if inputs != "" && inputs == recordedInputs(gate) {
|
|
103
|
+
results = append(results, GateResult{ID: gate.ID, Passed: true, Detail: "reused: inputs unchanged"})
|
|
104
|
+
continue
|
|
105
|
+
}
|
|
93
106
|
}
|
|
94
107
|
if !Approved(gate.Check, gate.Cwd, approved) {
|
|
95
108
|
results = append(results, GateResult{ID: gate.ID, Detail: "CHECK/CWD is not an approved test-plan.md preflight row; not executed"})
|
|
@@ -111,7 +124,7 @@ func RunGates(ctx context.Context, root, ledgerPath string, doc *Document, appro
|
|
|
111
124
|
outcomes[key] = outcome
|
|
112
125
|
}
|
|
113
126
|
if outcome.Err == nil && outcome.ExitCode == 0 && expectation.Matches(outcome.Output) {
|
|
114
|
-
evidence := FormatEvidence(gate, OutputDigest(outcome.Output), outcome.OutputBytes, time.Now().UTC().Format(time.RFC3339))
|
|
127
|
+
evidence := withInputs(FormatEvidence(gate, OutputDigest(outcome.Output), outcome.OutputBytes, time.Now().UTC().Format(time.RFC3339)), inputs)
|
|
115
128
|
writeErr := write(gate, evidence, true)
|
|
116
129
|
results = append(results, GateResult{
|
|
117
130
|
ID: gate.ID, Ran: true, Passed: true,
|
|
@@ -170,7 +183,7 @@ func writeResult(ledgerPath string, ran *Gate, evidence string, checked bool) er
|
|
|
170
183
|
if current == nil {
|
|
171
184
|
return fmt.Errorf("%w: gate %s disappeared", ErrOracleMoved, ran.ID)
|
|
172
185
|
}
|
|
173
|
-
if current.Check != ran.Check || current.Expect != ran.Expect || current.Cwd != ran.Cwd {
|
|
186
|
+
if current.Check != ran.Check || current.Expect != ran.Expect || current.Cwd != ran.Cwd || current.Inputs != ran.Inputs {
|
|
174
187
|
return fmt.Errorf("%w: gate %s definition edited", ErrOracleMoved, ran.ID)
|
|
175
188
|
}
|
|
176
189
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
package lib
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"errors"
|
|
5
|
+
"fmt"
|
|
6
|
+
"os"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"regexp"
|
|
9
|
+
"sort"
|
|
10
|
+
"strings"
|
|
11
|
+
|
|
12
|
+
"github.com/devrites/devrites/internal/devritespaths"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
// CandidateOmissions lists the project paths the feature's own
|
|
16
|
+
// `WIP(<slug>):` checkpoint commits changed (net, feature base to HEAD) that
|
|
17
|
+
// touched-files.md lists neither present nor deleted. The base is the parent
|
|
18
|
+
// of the oldest such commit, or the empty tree for a root commit. A project
|
|
19
|
+
// outside Git, or with no checkpoint yet, has nothing to compare.
|
|
20
|
+
func CandidateOmissions(root, slug string) ([]string, error) {
|
|
21
|
+
workspace, err := devritespaths.ExistingFeatureDirChecked(root, slug)
|
|
22
|
+
if err != nil {
|
|
23
|
+
return nil, err
|
|
24
|
+
}
|
|
25
|
+
raw, err := readBoundedRegularFile(filepath.Join(workspace, "touched-files.md"), maxCandidateManifestBytes)
|
|
26
|
+
if err != nil {
|
|
27
|
+
return nil, fmt.Errorf("candidate manifest: %w", err)
|
|
28
|
+
}
|
|
29
|
+
rows, err := parseCandidateManifest(raw)
|
|
30
|
+
if err != nil {
|
|
31
|
+
return nil, fmt.Errorf("candidate manifest: %w", err)
|
|
32
|
+
}
|
|
33
|
+
project := projectDir(root)
|
|
34
|
+
if _, err := runGitCommand(project, nil, "rev-parse", "--verify", "--quiet", "HEAD"); err != nil {
|
|
35
|
+
return nil, nil
|
|
36
|
+
}
|
|
37
|
+
log, err := runGitCommand(project, nil, "log", "--fixed-strings", "--grep=WIP("+slug+"):", "--format=%H%x1f%P%x1f%s", "HEAD")
|
|
38
|
+
if err != nil {
|
|
39
|
+
return nil, err
|
|
40
|
+
}
|
|
41
|
+
var commits []string
|
|
42
|
+
base := ""
|
|
43
|
+
for _, line := range strings.Split(strings.TrimSpace(string(log)), "\n") {
|
|
44
|
+
fields := strings.SplitN(line, "\x1f", 3)
|
|
45
|
+
if len(fields) != 3 || !strings.HasPrefix(fields[2], "WIP("+slug+"):") {
|
|
46
|
+
continue
|
|
47
|
+
}
|
|
48
|
+
commits = append(commits, fields[0])
|
|
49
|
+
base = "" // log lists newest first, so the oldest commit's parent wins
|
|
50
|
+
if parents := strings.Fields(fields[1]); len(parents) > 0 {
|
|
51
|
+
base = parents[0]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if len(commits) == 0 {
|
|
55
|
+
return nil, nil
|
|
56
|
+
}
|
|
57
|
+
if base == "" {
|
|
58
|
+
tree, err := runGitCommandIO(project, nil, []byte{}, nil, "hash-object", "-t", "tree", "--stdin")
|
|
59
|
+
if err != nil {
|
|
60
|
+
return nil, err
|
|
61
|
+
}
|
|
62
|
+
base = strings.TrimSpace(string(tree))
|
|
63
|
+
}
|
|
64
|
+
touched := map[string]bool{}
|
|
65
|
+
for _, commit := range commits {
|
|
66
|
+
names, err := runGitCommand(project, nil, "diff-tree", "-r", "--root", "--no-commit-id", "--name-only", "--no-renames", "--relative", "-z", commit)
|
|
67
|
+
if err != nil {
|
|
68
|
+
return nil, err
|
|
69
|
+
}
|
|
70
|
+
for _, name := range strings.Split(string(names), "\x00") {
|
|
71
|
+
if name != "" {
|
|
72
|
+
touched[name] = true
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
net, err := runGitCommand(project, nil, "diff", "--name-only", "--no-renames", "--relative", "-z", base, "HEAD")
|
|
77
|
+
if err != nil {
|
|
78
|
+
return nil, err
|
|
79
|
+
}
|
|
80
|
+
listed := make(map[string]bool, len(rows))
|
|
81
|
+
for _, row := range rows {
|
|
82
|
+
listed[row.path] = true
|
|
83
|
+
}
|
|
84
|
+
var omitted []string
|
|
85
|
+
for _, name := range strings.Split(string(net), "\x00") {
|
|
86
|
+
if name != "" && touched[name] && !listed[name] && !forbiddenCandidateStatePath(name) {
|
|
87
|
+
omitted = append(omitted, name)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
sort.Strings(omitted)
|
|
91
|
+
return omitted, nil
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// CheckCandidateCoverage fails when touched-files.md omits a path the
|
|
95
|
+
// feature's checkpoints changed.
|
|
96
|
+
func CheckCandidateCoverage(root, slug string) error {
|
|
97
|
+
omitted, err := CandidateOmissions(root, slug)
|
|
98
|
+
if err != nil {
|
|
99
|
+
return err
|
|
100
|
+
}
|
|
101
|
+
if len(omitted) > 0 {
|
|
102
|
+
return omissionError(omitted)
|
|
103
|
+
}
|
|
104
|
+
return nil
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// omissionError renders a bounded BLOCKED reason for omitted candidate paths.
|
|
108
|
+
func omissionError(omitted []string) error {
|
|
109
|
+
shown := omitted
|
|
110
|
+
if len(shown) > 10 {
|
|
111
|
+
shown = shown[:10]
|
|
112
|
+
}
|
|
113
|
+
more := ""
|
|
114
|
+
if len(omitted) > len(shown) {
|
|
115
|
+
more = ", …"
|
|
116
|
+
}
|
|
117
|
+
return fmt.Errorf("candidate manifest omits %d path(s) the feature's WIP checkpoints changed: %s%s; add each to touched-files.md (present or deleted) before freezing the candidate",
|
|
118
|
+
len(omitted), strings.Join(shown, ", "), more)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
var diffPathRE = regexp.MustCompile(`[A-Za-z0-9_./@+-]+\.(?:diff|patch)\b`)
|
|
122
|
+
|
|
123
|
+
// lintReviewCandidate gates a Review or Seal reviewer wave: the candidate
|
|
124
|
+
// manifest must cover every path the feature's checkpoints changed, and the
|
|
125
|
+
// packet must name the frozen candidate diff the reviewers judge. A digest
|
|
126
|
+
// alone, or a manifest that drops changed files, voids every reviewer at once.
|
|
127
|
+
func lintReviewCandidate(root, slug, packet string) error {
|
|
128
|
+
if err := CheckCandidateCoverage(root, slug); err != nil {
|
|
129
|
+
return err
|
|
130
|
+
}
|
|
131
|
+
raw, err := readBoundedRegularFile(packet, maxContractBytes)
|
|
132
|
+
if err != nil {
|
|
133
|
+
return fmt.Errorf("reviewer packet %s: %w", packet, err)
|
|
134
|
+
}
|
|
135
|
+
for _, name := range diffPathRE.FindAllString(string(raw), -1) {
|
|
136
|
+
for _, dir := range []string{projectDir(root), featureDir(root, slug), ""} {
|
|
137
|
+
candidate := name
|
|
138
|
+
if dir != "" {
|
|
139
|
+
candidate = filepath.Join(dir, filepath.FromSlash(name))
|
|
140
|
+
}
|
|
141
|
+
if info, err := os.Stat(candidate); err == nil && info.Mode().IsRegular() && info.Size() > 0 {
|
|
142
|
+
return nil
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return errors.New("a Review or Seal reviewer packet must name the frozen candidate diff (an existing non-empty .diff or .patch file); reviewers cannot judge a digest alone")
|
|
147
|
+
}
|
|
@@ -223,6 +223,11 @@ func dispatchLocked(root, slug, sub, phase, wave, role, handle, reason string, r
|
|
|
223
223
|
if err := lintReviewerPacket(packet); err != nil {
|
|
224
224
|
return fail("BLOCKED: %v", err)
|
|
225
225
|
}
|
|
226
|
+
if strings.EqualFold(phase, "review") || strings.EqualFold(phase, "seal") {
|
|
227
|
+
if err := lintReviewCandidate(root, slug, packet); err != nil {
|
|
228
|
+
return fail("BLOCKED: %v", err)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
226
231
|
break
|
|
227
232
|
}
|
|
228
233
|
}
|
package/engine/main.go
CHANGED
|
@@ -225,6 +225,10 @@ func cmdCandidate(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
225
225
|
fmt.Fprintf(stderr, "candidate: BLOCKED: %v\n", err)
|
|
226
226
|
return exitBlocked
|
|
227
227
|
}
|
|
228
|
+
if err := lib.CheckCandidateCoverage(root, args[0]); err != nil {
|
|
229
|
+
fmt.Fprintf(stderr, "candidate: BLOCKED: %v\n", err)
|
|
230
|
+
return exitBlocked
|
|
231
|
+
}
|
|
228
232
|
fmt.Fprintf(stdout, "candidate-sha256: %s\ncandidate-files: %d\n", digest, files)
|
|
229
233
|
return exitOK
|
|
230
234
|
}
|
|
@@ -50,7 +50,8 @@ If the candidate diff has no matching surfaces, record
|
|
|
50
50
|
(`devrites-engine dispatch <slug> open --phase <p> --wave <w> --role <r>...`;
|
|
51
51
|
a reviewer, auditor, or test-analyst wave adds `--packet <file>`, the exact packet
|
|
52
52
|
those agents receive, and `open` refuses one that seeds a gate result, outcome,
|
|
53
|
-
count, or "already passed" claim
|
|
53
|
+
count, or "already passed" claim; at Review and Seal it also needs the packet to
|
|
54
|
+
name the frozen candidate diff and the manifest to cover every checkpointed path), launch every native agent nonblocking, record each returned host handle
|
|
54
55
|
(`dispatch ... start --wave <w> --role <r> --handle <id>`), then `seal` —
|
|
55
56
|
seal fails until every declared role has a distinct handle, which makes a
|
|
56
57
|
serial launch-then-wait pattern impossible to record. Record each completion
|
|
@@ -19,6 +19,7 @@ every later phase requires its presence; proof phases require every gate met.
|
|
|
19
19
|
CHECK: <exact command from test-plan.md Build-entry preflight>
|
|
20
20
|
EXPECT: <substring | /regex/flags>
|
|
21
21
|
CWD: <repository-relative path; omit for repo root>
|
|
22
|
+
INPUTS: <optional project-relative paths, directories or globs>
|
|
22
23
|
EVIDENCE: pending
|
|
23
24
|
|
|
24
25
|
- [x] AC-002: <human-judged outcome>
|
|
@@ -33,6 +34,10 @@ ABANDON: AC-003 <why the outcome can never be met>
|
|
|
33
34
|
- Attributes are indented beneath their gate. Fenced code examples never parse.
|
|
34
35
|
- `EXPECT` is a substring, or `/pattern/flags` with `i`, `m`, `s` flags.
|
|
35
36
|
- `CWD` is repository-relative; absolute paths and `..` segments are rejected.
|
|
37
|
+
- `INPUTS` (optional) lists every file the gate's result depends on: sources,
|
|
38
|
+
tests, fixtures, build and lock files, and the gate's own scripts. Each pattern
|
|
39
|
+
must match a file. A gate whose verdict depends on state that no file
|
|
40
|
+
captures (network, wall clock, live host load) takes no `INPUTS`.
|
|
36
41
|
- `ABANDON:` starts at column 1, names a known gate, and requires a reason.
|
|
37
42
|
An abandoned gate blocks completion and demands a handoff — it can never
|
|
38
43
|
read as a pass.
|
|
@@ -53,8 +58,8 @@ directory with a bounded timeout; pass requires exit 0 **and** output matching
|
|
|
53
58
|
EXPECT. Output is captured but never persisted — only its digest.
|
|
54
59
|
|
|
55
60
|
After Vet READY the runnable set is fixed: converting a runnable gate to manual,
|
|
56
|
-
loosening EXPECT, changing CHECK or CWD, or
|
|
57
|
-
re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
61
|
+
loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
|
|
62
|
+
a row is Plan repair plus re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
58
63
|
with `unavailable: <reason>`. Prove attests only gates that were manual at Vet,
|
|
59
64
|
checked by diffing `gates.md` against the Vet record. **Failing case:** AC-003's
|
|
60
65
|
runnable gate fails at Prove, the root deletes CHECK/EXPECT and attests "verified
|
|
@@ -63,9 +68,13 @@ manually", and `gates status` reads `all-met`.
|
|
|
63
68
|
## Evidence binding
|
|
64
69
|
|
|
65
70
|
A successful run writes `EVIDENCE: automatic-evidence=v1; def=<sha256>; …`
|
|
66
|
-
where `def` binds the exact CHECK/EXPECT/CWD
|
|
67
|
-
|
|
68
|
-
behind an edited oracle.
|
|
71
|
+
where `def` binds the exact CHECK/EXPECT/CWD (and INPUTS, when declared) the run
|
|
72
|
+
executed. Editing any of them renders prior evidence `stale-unmet` — a stale pass
|
|
73
|
+
cannot hide behind an edited oracle. A gate with `INPUTS` also records
|
|
74
|
+
`inputs=<sha256>` of those files' content. `gates run` and `gates reverify`
|
|
75
|
+
both reuse such a gate while the files are byte-identical, and re-execute it
|
|
76
|
+
when any of them changes. A gate without `INPUTS` keeps the plain rule:
|
|
77
|
+
`run` skips it once met, and `reverify` re-executes it. Prose evidence on a runnable gate is also stale:
|
|
69
78
|
only automatic evidence bound to the current definition counts.
|
|
70
79
|
|
|
71
80
|
Manual gates are met when checked with non-placeholder human evidence;
|
|
@@ -85,8 +94,8 @@ self-attests.
|
|
|
85
94
|
```text
|
|
86
95
|
devrites-engine gates scaffold <slug> create gates.md from spec.md AC ids
|
|
87
96
|
devrites-engine gates status <slug> reduce without executing; all-met/handoff/not-met/malformed
|
|
88
|
-
devrites-engine gates run <slug> execute unmet runnable gates
|
|
89
|
-
devrites-engine gates reverify <slug> re-execute every runnable gate
|
|
97
|
+
devrites-engine gates run <slug> execute unmet runnable gates (and INPUTS gates whose files changed)
|
|
98
|
+
devrites-engine gates reverify <slug> re-execute every runnable gate except INPUTS gates with unchanged files
|
|
90
99
|
devrites-engine gates lint <slug> audit oracle quality without executing
|
|
91
100
|
devrites-engine gates attest <slug> <id> <note> record human evidence on a manual gate
|
|
92
101
|
devrites-engine gates abandon <slug> <id> <why> record a terminal ABANDON handoff
|
|
@@ -63,7 +63,9 @@ executable controller/harness/bundle bytes or a missing writer, read
|
|
|
63
63
|
- Never end the turn with a progress note while a wave runs or a next step
|
|
64
64
|
exists: on a headless host a text-only reply ends the session and kills
|
|
65
65
|
running children. Wait with the host's wait tool and keep working; end only
|
|
66
|
-
at a recorded stop.
|
|
66
|
+
at a recorded stop. A host reminder or rule notice (style rule, advisory) is
|
|
67
|
+
not a stop either: follow it in your next tool call, never in a text-only
|
|
68
|
+
acknowledgement.
|
|
67
69
|
- Parse flags only from this invocation. `--ship`, `--yolo`, `--max-slices`,
|
|
68
70
|
`--parallel`, `--full`, and `--cross-model` activate only as exact standalone
|
|
69
71
|
tokens in `$ARGUMENTS`; examples or earlier messages can never arm them.
|
|
@@ -89,7 +89,12 @@ Pull these via `Read` when the diff demands them:
|
|
|
89
89
|
what the spec asked but breaks project conventions (Spec pass, Code-review fail).
|
|
90
90
|
Separate contexts prevent one axis from masking the other. This is the initial
|
|
91
91
|
full pass; a correction uses step 7's bounded recheck:
|
|
92
|
-
- Freeze that closed digest
|
|
92
|
+
- Freeze that closed digest: write the candidate diff (feature base to HEAD, plus any
|
|
93
|
+
uncommitted candidate change) to `packets/<wave>.diff` and name that file in the
|
|
94
|
+
packet. `dispatch open` refuses a Review or Seal reviewer wave whose packet names
|
|
95
|
+
no existing diff, or whose manifest omits a path the feature's `WIP(<slug>):`
|
|
96
|
+
checkpoints changed.
|
|
97
|
+
Give the same digest and diff to the **two** axis reviewers (three when
|
|
93
98
|
[engineering lanes](../devrites-lib/reference/parallel-dispatch.md) split the code axis) in
|
|
94
99
|
parallel, each with its own narrow brief and no
|
|
95
100
|
cross-pollination — and evaluate the frontend (step 4), security (step 5),
|
|
@@ -50,7 +50,8 @@ If the candidate diff has no matching surfaces, record
|
|
|
50
50
|
(`devrites-engine dispatch <slug> open --phase <p> --wave <w> --role <r>...`;
|
|
51
51
|
a reviewer, auditor, or test-analyst wave adds `--packet <file>`, the exact packet
|
|
52
52
|
those agents receive, and `open` refuses one that seeds a gate result, outcome,
|
|
53
|
-
count, or "already passed" claim
|
|
53
|
+
count, or "already passed" claim; at Review and Seal it also needs the packet to
|
|
54
|
+
name the frozen candidate diff and the manifest to cover every checkpointed path), launch every native agent nonblocking, record each returned host handle
|
|
54
55
|
(`dispatch ... start --wave <w> --role <r> --handle <id>`), then `seal` —
|
|
55
56
|
seal fails until every declared role has a distinct handle, which makes a
|
|
56
57
|
serial launch-then-wait pattern impossible to record. Record each completion
|
|
@@ -19,6 +19,7 @@ every later phase requires its presence; proof phases require every gate met.
|
|
|
19
19
|
CHECK: <exact command from test-plan.md Build-entry preflight>
|
|
20
20
|
EXPECT: <substring | /regex/flags>
|
|
21
21
|
CWD: <repository-relative path; omit for repo root>
|
|
22
|
+
INPUTS: <optional project-relative paths, directories or globs>
|
|
22
23
|
EVIDENCE: pending
|
|
23
24
|
|
|
24
25
|
- [x] AC-002: <human-judged outcome>
|
|
@@ -33,6 +34,10 @@ ABANDON: AC-003 <why the outcome can never be met>
|
|
|
33
34
|
- Attributes are indented beneath their gate. Fenced code examples never parse.
|
|
34
35
|
- `EXPECT` is a substring, or `/pattern/flags` with `i`, `m`, `s` flags.
|
|
35
36
|
- `CWD` is repository-relative; absolute paths and `..` segments are rejected.
|
|
37
|
+
- `INPUTS` (optional) lists every file the gate's result depends on: sources,
|
|
38
|
+
tests, fixtures, build and lock files, and the gate's own scripts. Each pattern
|
|
39
|
+
must match a file. A gate whose verdict depends on state that no file
|
|
40
|
+
captures (network, wall clock, live host load) takes no `INPUTS`.
|
|
36
41
|
- `ABANDON:` starts at column 1, names a known gate, and requires a reason.
|
|
37
42
|
An abandoned gate blocks completion and demands a handoff — it can never
|
|
38
43
|
read as a pass.
|
|
@@ -53,8 +58,8 @@ directory with a bounded timeout; pass requires exit 0 **and** output matching
|
|
|
53
58
|
EXPECT. Output is captured but never persisted — only its digest.
|
|
54
59
|
|
|
55
60
|
After Vet READY the runnable set is fixed: converting a runnable gate to manual,
|
|
56
|
-
loosening EXPECT, changing CHECK or CWD, or
|
|
57
|
-
re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
61
|
+
loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
|
|
62
|
+
a row is Plan repair plus re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
58
63
|
with `unavailable: <reason>`. Prove attests only gates that were manual at Vet,
|
|
59
64
|
checked by diffing `gates.md` against the Vet record. **Failing case:** AC-003's
|
|
60
65
|
runnable gate fails at Prove, the root deletes CHECK/EXPECT and attests "verified
|
|
@@ -63,9 +68,13 @@ manually", and `gates status` reads `all-met`.
|
|
|
63
68
|
## Evidence binding
|
|
64
69
|
|
|
65
70
|
A successful run writes `EVIDENCE: automatic-evidence=v1; def=<sha256>; …`
|
|
66
|
-
where `def` binds the exact CHECK/EXPECT/CWD
|
|
67
|
-
|
|
68
|
-
behind an edited oracle.
|
|
71
|
+
where `def` binds the exact CHECK/EXPECT/CWD (and INPUTS, when declared) the run
|
|
72
|
+
executed. Editing any of them renders prior evidence `stale-unmet` — a stale pass
|
|
73
|
+
cannot hide behind an edited oracle. A gate with `INPUTS` also records
|
|
74
|
+
`inputs=<sha256>` of those files' content. `gates run` and `gates reverify`
|
|
75
|
+
both reuse such a gate while the files are byte-identical, and re-execute it
|
|
76
|
+
when any of them changes. A gate without `INPUTS` keeps the plain rule:
|
|
77
|
+
`run` skips it once met, and `reverify` re-executes it. Prose evidence on a runnable gate is also stale:
|
|
69
78
|
only automatic evidence bound to the current definition counts.
|
|
70
79
|
|
|
71
80
|
Manual gates are met when checked with non-placeholder human evidence;
|
|
@@ -85,8 +94,8 @@ self-attests.
|
|
|
85
94
|
```text
|
|
86
95
|
devrites-engine gates scaffold <slug> create gates.md from spec.md AC ids
|
|
87
96
|
devrites-engine gates status <slug> reduce without executing; all-met/handoff/not-met/malformed
|
|
88
|
-
devrites-engine gates run <slug> execute unmet runnable gates
|
|
89
|
-
devrites-engine gates reverify <slug> re-execute every runnable gate
|
|
97
|
+
devrites-engine gates run <slug> execute unmet runnable gates (and INPUTS gates whose files changed)
|
|
98
|
+
devrites-engine gates reverify <slug> re-execute every runnable gate except INPUTS gates with unchanged files
|
|
90
99
|
devrites-engine gates lint <slug> audit oracle quality without executing
|
|
91
100
|
devrites-engine gates attest <slug> <id> <note> record human evidence on a manual gate
|
|
92
101
|
devrites-engine gates abandon <slug> <id> <why> record a terminal ABANDON handoff
|
|
@@ -65,7 +65,9 @@ executable controller/harness/bundle bytes or a missing writer, read
|
|
|
65
65
|
- Never end the turn with a progress note while a wave runs or a next step
|
|
66
66
|
exists: on a headless host a text-only reply ends the session and kills
|
|
67
67
|
running children. Wait with the host's wait tool and keep working; end only
|
|
68
|
-
at a recorded stop.
|
|
68
|
+
at a recorded stop. A host reminder or rule notice (style rule, advisory) is
|
|
69
|
+
not a stop either: follow it in your next tool call, never in a text-only
|
|
70
|
+
acknowledgement.
|
|
69
71
|
- Parse flags only from this invocation. `--ship`, `--yolo`, `--max-slices`,
|
|
70
72
|
`--parallel`, `--full`, and `--cross-model` activate only as exact standalone
|
|
71
73
|
tokens in `$ARGUMENTS`; examples or earlier messages can never arm them.
|
|
@@ -89,7 +89,12 @@ Pull these via `Read` when the diff demands them:
|
|
|
89
89
|
what the spec asked but breaks project conventions (Spec pass, Code-review fail).
|
|
90
90
|
Separate contexts prevent one axis from masking the other. This is the initial
|
|
91
91
|
full pass; a correction uses step 7's bounded recheck:
|
|
92
|
-
- Freeze that closed digest
|
|
92
|
+
- Freeze that closed digest: write the candidate diff (feature base to HEAD, plus any
|
|
93
|
+
uncommitted candidate change) to `packets/<wave>.diff` and name that file in the
|
|
94
|
+
packet. `dispatch open` refuses a Review or Seal reviewer wave whose packet names
|
|
95
|
+
no existing diff, or whose manifest omits a path the feature's `WIP(<slug>):`
|
|
96
|
+
checkpoints changed.
|
|
97
|
+
Give the same digest and diff to the **two** axis reviewers (three when
|
|
93
98
|
[engineering lanes](../devrites-lib/reference/parallel-dispatch.md) split the code axis) in
|
|
94
99
|
parallel, each with its own narrow brief and no
|
|
95
100
|
cross-pollination — and evaluate the frontend (step 4), security (step 5),
|
|
@@ -50,7 +50,8 @@ If the candidate diff has no matching surfaces, record
|
|
|
50
50
|
(`devrites-engine dispatch <slug> open --phase <p> --wave <w> --role <r>...`;
|
|
51
51
|
a reviewer, auditor, or test-analyst wave adds `--packet <file>`, the exact packet
|
|
52
52
|
those agents receive, and `open` refuses one that seeds a gate result, outcome,
|
|
53
|
-
count, or "already passed" claim
|
|
53
|
+
count, or "already passed" claim; at Review and Seal it also needs the packet to
|
|
54
|
+
name the frozen candidate diff and the manifest to cover every checkpointed path), launch every native agent nonblocking, record each returned host handle
|
|
54
55
|
(`dispatch ... start --wave <w> --role <r> --handle <id>`), then `seal` —
|
|
55
56
|
seal fails until every declared role has a distinct handle, which makes a
|
|
56
57
|
serial launch-then-wait pattern impossible to record. Record each completion
|
|
@@ -19,6 +19,7 @@ every later phase requires its presence; proof phases require every gate met.
|
|
|
19
19
|
CHECK: <exact command from test-plan.md Build-entry preflight>
|
|
20
20
|
EXPECT: <substring | /regex/flags>
|
|
21
21
|
CWD: <repository-relative path; omit for repo root>
|
|
22
|
+
INPUTS: <optional project-relative paths, directories or globs>
|
|
22
23
|
EVIDENCE: pending
|
|
23
24
|
|
|
24
25
|
- [x] AC-002: <human-judged outcome>
|
|
@@ -33,6 +34,10 @@ ABANDON: AC-003 <why the outcome can never be met>
|
|
|
33
34
|
- Attributes are indented beneath their gate. Fenced code examples never parse.
|
|
34
35
|
- `EXPECT` is a substring, or `/pattern/flags` with `i`, `m`, `s` flags.
|
|
35
36
|
- `CWD` is repository-relative; absolute paths and `..` segments are rejected.
|
|
37
|
+
- `INPUTS` (optional) lists every file the gate's result depends on: sources,
|
|
38
|
+
tests, fixtures, build and lock files, and the gate's own scripts. Each pattern
|
|
39
|
+
must match a file. A gate whose verdict depends on state that no file
|
|
40
|
+
captures (network, wall clock, live host load) takes no `INPUTS`.
|
|
36
41
|
- `ABANDON:` starts at column 1, names a known gate, and requires a reason.
|
|
37
42
|
An abandoned gate blocks completion and demands a handoff — it can never
|
|
38
43
|
read as a pass.
|
|
@@ -53,8 +58,8 @@ directory with a bounded timeout; pass requires exit 0 **and** output matching
|
|
|
53
58
|
EXPECT. Output is captured but never persisted — only its digest.
|
|
54
59
|
|
|
55
60
|
After Vet READY the runnable set is fixed: converting a runnable gate to manual,
|
|
56
|
-
loosening EXPECT, changing CHECK or CWD, or
|
|
57
|
-
re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
61
|
+
loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
|
|
62
|
+
a row is Plan repair plus re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
58
63
|
with `unavailable: <reason>`. Prove attests only gates that were manual at Vet,
|
|
59
64
|
checked by diffing `gates.md` against the Vet record. **Failing case:** AC-003's
|
|
60
65
|
runnable gate fails at Prove, the root deletes CHECK/EXPECT and attests "verified
|
|
@@ -63,9 +68,13 @@ manually", and `gates status` reads `all-met`.
|
|
|
63
68
|
## Evidence binding
|
|
64
69
|
|
|
65
70
|
A successful run writes `EVIDENCE: automatic-evidence=v1; def=<sha256>; …`
|
|
66
|
-
where `def` binds the exact CHECK/EXPECT/CWD
|
|
67
|
-
|
|
68
|
-
behind an edited oracle.
|
|
71
|
+
where `def` binds the exact CHECK/EXPECT/CWD (and INPUTS, when declared) the run
|
|
72
|
+
executed. Editing any of them renders prior evidence `stale-unmet` — a stale pass
|
|
73
|
+
cannot hide behind an edited oracle. A gate with `INPUTS` also records
|
|
74
|
+
`inputs=<sha256>` of those files' content. `gates run` and `gates reverify`
|
|
75
|
+
both reuse such a gate while the files are byte-identical, and re-execute it
|
|
76
|
+
when any of them changes. A gate without `INPUTS` keeps the plain rule:
|
|
77
|
+
`run` skips it once met, and `reverify` re-executes it. Prose evidence on a runnable gate is also stale:
|
|
69
78
|
only automatic evidence bound to the current definition counts.
|
|
70
79
|
|
|
71
80
|
Manual gates are met when checked with non-placeholder human evidence;
|
|
@@ -85,8 +94,8 @@ self-attests.
|
|
|
85
94
|
```text
|
|
86
95
|
devrites-engine gates scaffold <slug> create gates.md from spec.md AC ids
|
|
87
96
|
devrites-engine gates status <slug> reduce without executing; all-met/handoff/not-met/malformed
|
|
88
|
-
devrites-engine gates run <slug> execute unmet runnable gates
|
|
89
|
-
devrites-engine gates reverify <slug> re-execute every runnable gate
|
|
97
|
+
devrites-engine gates run <slug> execute unmet runnable gates (and INPUTS gates whose files changed)
|
|
98
|
+
devrites-engine gates reverify <slug> re-execute every runnable gate except INPUTS gates with unchanged files
|
|
90
99
|
devrites-engine gates lint <slug> audit oracle quality without executing
|
|
91
100
|
devrites-engine gates attest <slug> <id> <note> record human evidence on a manual gate
|
|
92
101
|
devrites-engine gates abandon <slug> <id> <why> record a terminal ABANDON handoff
|
|
@@ -65,7 +65,9 @@ executable controller/harness/bundle bytes or a missing writer, read
|
|
|
65
65
|
- Never end the turn with a progress note while a wave runs or a next step
|
|
66
66
|
exists: on a headless host a text-only reply ends the session and kills
|
|
67
67
|
running children. Wait with the host's wait tool and keep working; end only
|
|
68
|
-
at a recorded stop.
|
|
68
|
+
at a recorded stop. A host reminder or rule notice (style rule, advisory) is
|
|
69
|
+
not a stop either: follow it in your next tool call, never in a text-only
|
|
70
|
+
acknowledgement.
|
|
69
71
|
- Parse flags only from this invocation. `--ship`, `--yolo`, `--max-slices`,
|
|
70
72
|
`--parallel`, `--full`, and `--cross-model` activate only as exact standalone
|
|
71
73
|
tokens in `$ARGUMENTS`; examples or earlier messages can never arm them.
|
|
@@ -89,7 +89,12 @@ Pull these via `Read` when the diff demands them:
|
|
|
89
89
|
what the spec asked but breaks project conventions (Spec pass, Code-review fail).
|
|
90
90
|
Separate contexts prevent one axis from masking the other. This is the initial
|
|
91
91
|
full pass; a correction uses step 7's bounded recheck:
|
|
92
|
-
- Freeze that closed digest
|
|
92
|
+
- Freeze that closed digest: write the candidate diff (feature base to HEAD, plus any
|
|
93
|
+
uncommitted candidate change) to `packets/<wave>.diff` and name that file in the
|
|
94
|
+
packet. `dispatch open` refuses a Review or Seal reviewer wave whose packet names
|
|
95
|
+
no existing diff, or whose manifest omits a path the feature's `WIP(<slug>):`
|
|
96
|
+
checkpoints changed.
|
|
97
|
+
Give the same digest and diff to the **two** axis reviewers (three when
|
|
93
98
|
[engineering lanes](../devrites-lib/reference/parallel-dispatch.md) split the code axis) in
|
|
94
99
|
parallel, each with its own narrow brief and no
|
|
95
100
|
cross-pollination — and evaluate the frontend (step 4), security (step 5),
|
|
@@ -50,7 +50,8 @@ If the candidate diff has no matching surfaces, record
|
|
|
50
50
|
(`devrites-engine dispatch <slug> open --phase <p> --wave <w> --role <r>...`;
|
|
51
51
|
a reviewer, auditor, or test-analyst wave adds `--packet <file>`, the exact packet
|
|
52
52
|
those agents receive, and `open` refuses one that seeds a gate result, outcome,
|
|
53
|
-
count, or "already passed" claim
|
|
53
|
+
count, or "already passed" claim; at Review and Seal it also needs the packet to
|
|
54
|
+
name the frozen candidate diff and the manifest to cover every checkpointed path), launch every native agent nonblocking, record each returned host handle
|
|
54
55
|
(`dispatch ... start --wave <w> --role <r> --handle <id>`), then `seal` —
|
|
55
56
|
seal fails until every declared role has a distinct handle, which makes a
|
|
56
57
|
serial launch-then-wait pattern impossible to record. Record each completion
|
|
@@ -19,6 +19,7 @@ every later phase requires its presence; proof phases require every gate met.
|
|
|
19
19
|
CHECK: <exact command from test-plan.md Build-entry preflight>
|
|
20
20
|
EXPECT: <substring | /regex/flags>
|
|
21
21
|
CWD: <repository-relative path; omit for repo root>
|
|
22
|
+
INPUTS: <optional project-relative paths, directories or globs>
|
|
22
23
|
EVIDENCE: pending
|
|
23
24
|
|
|
24
25
|
- [x] AC-002: <human-judged outcome>
|
|
@@ -33,6 +34,10 @@ ABANDON: AC-003 <why the outcome can never be met>
|
|
|
33
34
|
- Attributes are indented beneath their gate. Fenced code examples never parse.
|
|
34
35
|
- `EXPECT` is a substring, or `/pattern/flags` with `i`, `m`, `s` flags.
|
|
35
36
|
- `CWD` is repository-relative; absolute paths and `..` segments are rejected.
|
|
37
|
+
- `INPUTS` (optional) lists every file the gate's result depends on: sources,
|
|
38
|
+
tests, fixtures, build and lock files, and the gate's own scripts. Each pattern
|
|
39
|
+
must match a file. A gate whose verdict depends on state that no file
|
|
40
|
+
captures (network, wall clock, live host load) takes no `INPUTS`.
|
|
36
41
|
- `ABANDON:` starts at column 1, names a known gate, and requires a reason.
|
|
37
42
|
An abandoned gate blocks completion and demands a handoff — it can never
|
|
38
43
|
read as a pass.
|
|
@@ -53,8 +58,8 @@ directory with a bounded timeout; pass requires exit 0 **and** output matching
|
|
|
53
58
|
EXPECT. Output is captured but never persisted — only its digest.
|
|
54
59
|
|
|
55
60
|
After Vet READY the runnable set is fixed: converting a runnable gate to manual,
|
|
56
|
-
loosening EXPECT, changing CHECK or CWD, or
|
|
57
|
-
re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
61
|
+
loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
|
|
62
|
+
a row is Plan repair plus re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
58
63
|
with `unavailable: <reason>`. Prove attests only gates that were manual at Vet,
|
|
59
64
|
checked by diffing `gates.md` against the Vet record. **Failing case:** AC-003's
|
|
60
65
|
runnable gate fails at Prove, the root deletes CHECK/EXPECT and attests "verified
|
|
@@ -63,9 +68,13 @@ manually", and `gates status` reads `all-met`.
|
|
|
63
68
|
## Evidence binding
|
|
64
69
|
|
|
65
70
|
A successful run writes `EVIDENCE: automatic-evidence=v1; def=<sha256>; …`
|
|
66
|
-
where `def` binds the exact CHECK/EXPECT/CWD
|
|
67
|
-
|
|
68
|
-
behind an edited oracle.
|
|
71
|
+
where `def` binds the exact CHECK/EXPECT/CWD (and INPUTS, when declared) the run
|
|
72
|
+
executed. Editing any of them renders prior evidence `stale-unmet` — a stale pass
|
|
73
|
+
cannot hide behind an edited oracle. A gate with `INPUTS` also records
|
|
74
|
+
`inputs=<sha256>` of those files' content. `gates run` and `gates reverify`
|
|
75
|
+
both reuse such a gate while the files are byte-identical, and re-execute it
|
|
76
|
+
when any of them changes. A gate without `INPUTS` keeps the plain rule:
|
|
77
|
+
`run` skips it once met, and `reverify` re-executes it. Prose evidence on a runnable gate is also stale:
|
|
69
78
|
only automatic evidence bound to the current definition counts.
|
|
70
79
|
|
|
71
80
|
Manual gates are met when checked with non-placeholder human evidence;
|
|
@@ -85,8 +94,8 @@ self-attests.
|
|
|
85
94
|
```text
|
|
86
95
|
devrites-engine gates scaffold <slug> create gates.md from spec.md AC ids
|
|
87
96
|
devrites-engine gates status <slug> reduce without executing; all-met/handoff/not-met/malformed
|
|
88
|
-
devrites-engine gates run <slug> execute unmet runnable gates
|
|
89
|
-
devrites-engine gates reverify <slug> re-execute every runnable gate
|
|
97
|
+
devrites-engine gates run <slug> execute unmet runnable gates (and INPUTS gates whose files changed)
|
|
98
|
+
devrites-engine gates reverify <slug> re-execute every runnable gate except INPUTS gates with unchanged files
|
|
90
99
|
devrites-engine gates lint <slug> audit oracle quality without executing
|
|
91
100
|
devrites-engine gates attest <slug> <id> <note> record human evidence on a manual gate
|
|
92
101
|
devrites-engine gates abandon <slug> <id> <why> record a terminal ABANDON handoff
|
|
@@ -64,7 +64,9 @@ executable controller/harness/bundle bytes or a missing writer, read
|
|
|
64
64
|
- Never end the turn with a progress note while a wave runs or a next step
|
|
65
65
|
exists: on a headless host a text-only reply ends the session and kills
|
|
66
66
|
running children. Wait with the host's wait tool and keep working; end only
|
|
67
|
-
at a recorded stop.
|
|
67
|
+
at a recorded stop. A host reminder or rule notice (style rule, advisory) is
|
|
68
|
+
not a stop either: follow it in your next tool call, never in a text-only
|
|
69
|
+
acknowledgement.
|
|
68
70
|
- Parse flags only from this invocation. `--ship`, `--yolo`, `--max-slices`,
|
|
69
71
|
`--parallel`, `--full`, and `--cross-model` activate only as exact standalone
|
|
70
72
|
tokens in `$ARGUMENTS`; examples or earlier messages can never arm them.
|
|
@@ -88,7 +88,12 @@ Pull these via `Read` when the diff demands them:
|
|
|
88
88
|
what the spec asked but breaks project conventions (Spec pass, Code-review fail).
|
|
89
89
|
Separate contexts prevent one axis from masking the other. This is the initial
|
|
90
90
|
full pass; a correction uses step 7's bounded recheck:
|
|
91
|
-
- Freeze that closed digest
|
|
91
|
+
- Freeze that closed digest: write the candidate diff (feature base to HEAD, plus any
|
|
92
|
+
uncommitted candidate change) to `packets/<wave>.diff` and name that file in the
|
|
93
|
+
packet. `dispatch open` refuses a Review or Seal reviewer wave whose packet names
|
|
94
|
+
no existing diff, or whose manifest omits a path the feature's `WIP(<slug>):`
|
|
95
|
+
checkpoints changed.
|
|
96
|
+
Give the same digest and diff to the **two** axis reviewers (three when
|
|
92
97
|
[engineering lanes](../devrites-lib/reference/parallel-dispatch.md) split the code axis) in
|
|
93
98
|
parallel, each with its own narrow brief and no
|
|
94
99
|
cross-pollination — and evaluate the frontend (step 4), security (step 5),
|
|
@@ -50,7 +50,8 @@ If the candidate diff has no matching surfaces, record
|
|
|
50
50
|
(`devrites-engine dispatch <slug> open --phase <p> --wave <w> --role <r>...`;
|
|
51
51
|
a reviewer, auditor, or test-analyst wave adds `--packet <file>`, the exact packet
|
|
52
52
|
those agents receive, and `open` refuses one that seeds a gate result, outcome,
|
|
53
|
-
count, or "already passed" claim
|
|
53
|
+
count, or "already passed" claim; at Review and Seal it also needs the packet to
|
|
54
|
+
name the frozen candidate diff and the manifest to cover every checkpointed path), launch every native agent nonblocking, record each returned host handle
|
|
54
55
|
(`dispatch ... start --wave <w> --role <r> --handle <id>`), then `seal` —
|
|
55
56
|
seal fails until every declared role has a distinct handle, which makes a
|
|
56
57
|
serial launch-then-wait pattern impossible to record. Record each completion
|
|
@@ -19,6 +19,7 @@ every later phase requires its presence; proof phases require every gate met.
|
|
|
19
19
|
CHECK: <exact command from test-plan.md Build-entry preflight>
|
|
20
20
|
EXPECT: <substring | /regex/flags>
|
|
21
21
|
CWD: <repository-relative path; omit for repo root>
|
|
22
|
+
INPUTS: <optional project-relative paths, directories or globs>
|
|
22
23
|
EVIDENCE: pending
|
|
23
24
|
|
|
24
25
|
- [x] AC-002: <human-judged outcome>
|
|
@@ -33,6 +34,10 @@ ABANDON: AC-003 <why the outcome can never be met>
|
|
|
33
34
|
- Attributes are indented beneath their gate. Fenced code examples never parse.
|
|
34
35
|
- `EXPECT` is a substring, or `/pattern/flags` with `i`, `m`, `s` flags.
|
|
35
36
|
- `CWD` is repository-relative; absolute paths and `..` segments are rejected.
|
|
37
|
+
- `INPUTS` (optional) lists every file the gate's result depends on: sources,
|
|
38
|
+
tests, fixtures, build and lock files, and the gate's own scripts. Each pattern
|
|
39
|
+
must match a file. A gate whose verdict depends on state that no file
|
|
40
|
+
captures (network, wall clock, live host load) takes no `INPUTS`.
|
|
36
41
|
- `ABANDON:` starts at column 1, names a known gate, and requires a reason.
|
|
37
42
|
An abandoned gate blocks completion and demands a handoff — it can never
|
|
38
43
|
read as a pass.
|
|
@@ -53,8 +58,8 @@ directory with a bounded timeout; pass requires exit 0 **and** output matching
|
|
|
53
58
|
EXPECT. Output is captured but never persisted — only its digest.
|
|
54
59
|
|
|
55
60
|
After Vet READY the runnable set is fixed: converting a runnable gate to manual,
|
|
56
|
-
loosening EXPECT, changing CHECK or CWD, or
|
|
57
|
-
re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
61
|
+
loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
|
|
62
|
+
a row is Plan repair plus re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
58
63
|
with `unavailable: <reason>`. Prove attests only gates that were manual at Vet,
|
|
59
64
|
checked by diffing `gates.md` against the Vet record. **Failing case:** AC-003's
|
|
60
65
|
runnable gate fails at Prove, the root deletes CHECK/EXPECT and attests "verified
|
|
@@ -63,9 +68,13 @@ manually", and `gates status` reads `all-met`.
|
|
|
63
68
|
## Evidence binding
|
|
64
69
|
|
|
65
70
|
A successful run writes `EVIDENCE: automatic-evidence=v1; def=<sha256>; …`
|
|
66
|
-
where `def` binds the exact CHECK/EXPECT/CWD
|
|
67
|
-
|
|
68
|
-
behind an edited oracle.
|
|
71
|
+
where `def` binds the exact CHECK/EXPECT/CWD (and INPUTS, when declared) the run
|
|
72
|
+
executed. Editing any of them renders prior evidence `stale-unmet` — a stale pass
|
|
73
|
+
cannot hide behind an edited oracle. A gate with `INPUTS` also records
|
|
74
|
+
`inputs=<sha256>` of those files' content. `gates run` and `gates reverify`
|
|
75
|
+
both reuse such a gate while the files are byte-identical, and re-execute it
|
|
76
|
+
when any of them changes. A gate without `INPUTS` keeps the plain rule:
|
|
77
|
+
`run` skips it once met, and `reverify` re-executes it. Prose evidence on a runnable gate is also stale:
|
|
69
78
|
only automatic evidence bound to the current definition counts.
|
|
70
79
|
|
|
71
80
|
Manual gates are met when checked with non-placeholder human evidence;
|
|
@@ -85,8 +94,8 @@ self-attests.
|
|
|
85
94
|
```text
|
|
86
95
|
devrites-engine gates scaffold <slug> create gates.md from spec.md AC ids
|
|
87
96
|
devrites-engine gates status <slug> reduce without executing; all-met/handoff/not-met/malformed
|
|
88
|
-
devrites-engine gates run <slug> execute unmet runnable gates
|
|
89
|
-
devrites-engine gates reverify <slug> re-execute every runnable gate
|
|
97
|
+
devrites-engine gates run <slug> execute unmet runnable gates (and INPUTS gates whose files changed)
|
|
98
|
+
devrites-engine gates reverify <slug> re-execute every runnable gate except INPUTS gates with unchanged files
|
|
90
99
|
devrites-engine gates lint <slug> audit oracle quality without executing
|
|
91
100
|
devrites-engine gates attest <slug> <id> <note> record human evidence on a manual gate
|
|
92
101
|
devrites-engine gates abandon <slug> <id> <why> record a terminal ABANDON handoff
|
|
@@ -65,7 +65,9 @@ executable controller/harness/bundle bytes or a missing writer, read
|
|
|
65
65
|
- Never end the turn with a progress note while a wave runs or a next step
|
|
66
66
|
exists: on a headless host a text-only reply ends the session and kills
|
|
67
67
|
running children. Wait with the host's wait tool and keep working; end only
|
|
68
|
-
at a recorded stop.
|
|
68
|
+
at a recorded stop. A host reminder or rule notice (style rule, advisory) is
|
|
69
|
+
not a stop either: follow it in your next tool call, never in a text-only
|
|
70
|
+
acknowledgement.
|
|
69
71
|
- Parse flags only from this invocation. `--ship`, `--yolo`, `--max-slices`,
|
|
70
72
|
`--parallel`, `--full`, and `--cross-model` activate only as exact standalone
|
|
71
73
|
tokens in `$ARGUMENTS`; examples or earlier messages can never arm them.
|
|
@@ -89,7 +89,12 @@ Pull these via `Read` when the diff demands them:
|
|
|
89
89
|
what the spec asked but breaks project conventions (Spec pass, Code-review fail).
|
|
90
90
|
Separate contexts prevent one axis from masking the other. This is the initial
|
|
91
91
|
full pass; a correction uses step 7's bounded recheck:
|
|
92
|
-
- Freeze that closed digest
|
|
92
|
+
- Freeze that closed digest: write the candidate diff (feature base to HEAD, plus any
|
|
93
|
+
uncommitted candidate change) to `packets/<wave>.diff` and name that file in the
|
|
94
|
+
packet. `dispatch open` refuses a Review or Seal reviewer wave whose packet names
|
|
95
|
+
no existing diff, or whose manifest omits a path the feature's `WIP(<slug>):`
|
|
96
|
+
checkpoints changed.
|
|
97
|
+
Give the same digest and diff to the **two** axis reviewers (three when
|
|
93
98
|
[engineering lanes](skill://devrites-lib/reference/parallel-dispatch.md) split the code axis) in
|
|
94
99
|
parallel, each with its own narrow brief and no
|
|
95
100
|
cross-pollination — and evaluate the frontend (step 4), security (step 5),
|
|
@@ -50,7 +50,8 @@ If the candidate diff has no matching surfaces, record
|
|
|
50
50
|
(`devrites-engine dispatch <slug> open --phase <p> --wave <w> --role <r>...`;
|
|
51
51
|
a reviewer, auditor, or test-analyst wave adds `--packet <file>`, the exact packet
|
|
52
52
|
those agents receive, and `open` refuses one that seeds a gate result, outcome,
|
|
53
|
-
count, or "already passed" claim
|
|
53
|
+
count, or "already passed" claim; at Review and Seal it also needs the packet to
|
|
54
|
+
name the frozen candidate diff and the manifest to cover every checkpointed path), launch every native agent nonblocking, record each returned host handle
|
|
54
55
|
(`dispatch ... start --wave <w> --role <r> --handle <id>`), then `seal` —
|
|
55
56
|
seal fails until every declared role has a distinct handle, which makes a
|
|
56
57
|
serial launch-then-wait pattern impossible to record. Record each completion
|
|
@@ -19,6 +19,7 @@ every later phase requires its presence; proof phases require every gate met.
|
|
|
19
19
|
CHECK: <exact command from test-plan.md Build-entry preflight>
|
|
20
20
|
EXPECT: <substring | /regex/flags>
|
|
21
21
|
CWD: <repository-relative path; omit for repo root>
|
|
22
|
+
INPUTS: <optional project-relative paths, directories or globs>
|
|
22
23
|
EVIDENCE: pending
|
|
23
24
|
|
|
24
25
|
- [x] AC-002: <human-judged outcome>
|
|
@@ -33,6 +34,10 @@ ABANDON: AC-003 <why the outcome can never be met>
|
|
|
33
34
|
- Attributes are indented beneath their gate. Fenced code examples never parse.
|
|
34
35
|
- `EXPECT` is a substring, or `/pattern/flags` with `i`, `m`, `s` flags.
|
|
35
36
|
- `CWD` is repository-relative; absolute paths and `..` segments are rejected.
|
|
37
|
+
- `INPUTS` (optional) lists every file the gate's result depends on: sources,
|
|
38
|
+
tests, fixtures, build and lock files, and the gate's own scripts. Each pattern
|
|
39
|
+
must match a file. A gate whose verdict depends on state that no file
|
|
40
|
+
captures (network, wall clock, live host load) takes no `INPUTS`.
|
|
36
41
|
- `ABANDON:` starts at column 1, names a known gate, and requires a reason.
|
|
37
42
|
An abandoned gate blocks completion and demands a handoff — it can never
|
|
38
43
|
read as a pass.
|
|
@@ -53,8 +58,8 @@ directory with a bounded timeout; pass requires exit 0 **and** output matching
|
|
|
53
58
|
EXPECT. Output is captured but never persisted — only its digest.
|
|
54
59
|
|
|
55
60
|
After Vet READY the runnable set is fixed: converting a runnable gate to manual,
|
|
56
|
-
loosening EXPECT, changing CHECK or CWD, or
|
|
57
|
-
re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
61
|
+
loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
|
|
62
|
+
a row is Plan repair plus re-Vet, never a Prove or Seal edit. A runnable gate that cannot execute stays unmet
|
|
58
63
|
with `unavailable: <reason>`. Prove attests only gates that were manual at Vet,
|
|
59
64
|
checked by diffing `gates.md` against the Vet record. **Failing case:** AC-003's
|
|
60
65
|
runnable gate fails at Prove, the root deletes CHECK/EXPECT and attests "verified
|
|
@@ -63,9 +68,13 @@ manually", and `gates status` reads `all-met`.
|
|
|
63
68
|
## Evidence binding
|
|
64
69
|
|
|
65
70
|
A successful run writes `EVIDENCE: automatic-evidence=v1; def=<sha256>; …`
|
|
66
|
-
where `def` binds the exact CHECK/EXPECT/CWD
|
|
67
|
-
|
|
68
|
-
behind an edited oracle.
|
|
71
|
+
where `def` binds the exact CHECK/EXPECT/CWD (and INPUTS, when declared) the run
|
|
72
|
+
executed. Editing any of them renders prior evidence `stale-unmet` — a stale pass
|
|
73
|
+
cannot hide behind an edited oracle. A gate with `INPUTS` also records
|
|
74
|
+
`inputs=<sha256>` of those files' content. `gates run` and `gates reverify`
|
|
75
|
+
both reuse such a gate while the files are byte-identical, and re-execute it
|
|
76
|
+
when any of them changes. A gate without `INPUTS` keeps the plain rule:
|
|
77
|
+
`run` skips it once met, and `reverify` re-executes it. Prose evidence on a runnable gate is also stale:
|
|
69
78
|
only automatic evidence bound to the current definition counts.
|
|
70
79
|
|
|
71
80
|
Manual gates are met when checked with non-placeholder human evidence;
|
|
@@ -85,8 +94,8 @@ self-attests.
|
|
|
85
94
|
```text
|
|
86
95
|
devrites-engine gates scaffold <slug> create gates.md from spec.md AC ids
|
|
87
96
|
devrites-engine gates status <slug> reduce without executing; all-met/handoff/not-met/malformed
|
|
88
|
-
devrites-engine gates run <slug> execute unmet runnable gates
|
|
89
|
-
devrites-engine gates reverify <slug> re-execute every runnable gate
|
|
97
|
+
devrites-engine gates run <slug> execute unmet runnable gates (and INPUTS gates whose files changed)
|
|
98
|
+
devrites-engine gates reverify <slug> re-execute every runnable gate except INPUTS gates with unchanged files
|
|
90
99
|
devrites-engine gates lint <slug> audit oracle quality without executing
|
|
91
100
|
devrites-engine gates attest <slug> <id> <note> record human evidence on a manual gate
|
|
92
101
|
devrites-engine gates abandon <slug> <id> <why> record a terminal ABANDON handoff
|
|
@@ -65,7 +65,9 @@ executable controller/harness/bundle bytes or a missing writer, read
|
|
|
65
65
|
- Never end the turn with a progress note while a wave runs or a next step
|
|
66
66
|
exists: on a headless host a text-only reply ends the session and kills
|
|
67
67
|
running children. Wait with the host's wait tool and keep working; end only
|
|
68
|
-
at a recorded stop.
|
|
68
|
+
at a recorded stop. A host reminder or rule notice (style rule, advisory) is
|
|
69
|
+
not a stop either: follow it in your next tool call, never in a text-only
|
|
70
|
+
acknowledgement.
|
|
69
71
|
- Parse flags only from this invocation. `--ship`, `--yolo`, `--max-slices`,
|
|
70
72
|
`--parallel`, `--full`, and `--cross-model` activate only as exact standalone
|
|
71
73
|
tokens in `$ARGUMENTS`; examples or earlier messages can never arm them.
|
|
@@ -89,7 +89,12 @@ Pull these via `Read` when the diff demands them:
|
|
|
89
89
|
what the spec asked but breaks project conventions (Spec pass, Code-review fail).
|
|
90
90
|
Separate contexts prevent one axis from masking the other. This is the initial
|
|
91
91
|
full pass; a correction uses step 7's bounded recheck:
|
|
92
|
-
- Freeze that closed digest
|
|
92
|
+
- Freeze that closed digest: write the candidate diff (feature base to HEAD, plus any
|
|
93
|
+
uncommitted candidate change) to `packets/<wave>.diff` and name that file in the
|
|
94
|
+
packet. `dispatch open` refuses a Review or Seal reviewer wave whose packet names
|
|
95
|
+
no existing diff, or whose manifest omits a path the feature's `WIP(<slug>):`
|
|
96
|
+
checkpoints changed.
|
|
97
|
+
Give the same digest and diff to the **two** axis reviewers (three when
|
|
93
98
|
[engineering lanes](../devrites-lib/reference/parallel-dispatch.md) split the code axis) in
|
|
94
99
|
parallel, each with its own narrow brief and no
|
|
95
100
|
cross-pollination — and evaluate the frontend (step 4), security (step 5),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devrites",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.8.0",
|
|
4
4
|
"description": "DevRites: a disciplined senior-engineer workflow pack for Claude Code, Codex, omp, pi, and Devin CLI",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://github.com/ViktorsBaikers/DevRites#readme",
|