devrites 3.2.15 → 3.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +1 -1
  3. package/engine/hooks.go +20 -4
  4. package/engine/hooks_agent_dispatch.go +297 -48
  5. package/engine/hooks_events_test.go +40 -0
  6. package/engine/hooks_workspace.go +26 -6
  7. package/engine/internal/devritespaths/paths.go +80 -0
  8. package/engine/internal/lib/adoption.go +22 -7
  9. package/engine/internal/lib/adoption_test.go +30 -0
  10. package/engine/internal/lib/closeout.go +34 -11
  11. package/engine/internal/lib/closeout_test.go +99 -0
  12. package/engine/internal/lib/dispatchwaive.go +31 -0
  13. package/engine/internal/lib/dispatchwaive_test.go +23 -0
  14. package/engine/internal/lib/mutationgate.go +6 -2
  15. package/engine/internal/lib/reconcile.go +201 -64
  16. package/engine/internal/lib/reconcile_test.go +159 -12
  17. package/engine/internal/lib/testintegrity.go +4 -4
  18. package/engine/internal/lib/testintegrity_test.go +15 -0
  19. package/engine/internal/lib/util.go +128 -23
  20. package/engine/main.go +4 -1
  21. package/engine/testdata/golden/TestParityReconcile/close-window.golden +1 -0
  22. package/engine/testdata/golden/TestParityReconcile/not-git.golden +1 -1
  23. package/engine/testdata/golden/TestParityTestIntegrity/not-git.golden +1 -1
  24. package/engine/tests/hook_test.go +388 -17
  25. package/engine/tests/parity_reconcile_test.go +4 -1
  26. package/engine/tests/parity_testintegrity_test.go +1 -1
  27. package/pack/.claude/agents/devrites-code-reviewer.md +3 -0
  28. package/pack/.claude/agents/devrites-devex-reviewer.md +12 -8
  29. package/pack/.claude/agents/devrites-doubt-reviewer.md +3 -0
  30. package/pack/.claude/agents/devrites-forge-judge.md +4 -1
  31. package/pack/.claude/agents/devrites-frontend-reviewer.md +3 -0
  32. package/pack/.claude/agents/devrites-performance-reviewer.md +10 -9
  33. package/pack/.claude/agents/devrites-plan-reviewer.md +3 -0
  34. package/pack/.claude/agents/devrites-proof-runner.md +14 -14
  35. package/pack/.claude/agents/devrites-retrospector.md +3 -0
  36. package/pack/.claude/agents/devrites-security-auditor.md +3 -0
  37. package/pack/.claude/agents/devrites-simplifier-reviewer.md +3 -0
  38. package/pack/.claude/agents/devrites-slice-wright.md +6 -5
  39. package/pack/.claude/agents/devrites-spec-reviewer.md +3 -0
  40. package/pack/.claude/agents/devrites-strategy-reviewer.md +3 -0
  41. package/pack/.claude/agents/devrites-test-analyst.md +3 -0
  42. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +8 -0
  43. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +10 -0
  44. package/pack/.claude/skills/devrites-source-driven/SKILL.md +1 -1
  45. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +2 -2
  46. package/pack/.claude/skills/rite-build/reference/phase-contract.md +28 -4
  47. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +7 -5
  48. package/pack/.claude/skills/rite-prove/SKILL.md +25 -14
  49. package/pack/.claude/skills/rite-temper/SKILL.md +1 -1
  50. package/pack/.claude/skills/rite-upgrade/SKILL.md +1 -1
  51. package/pack/.claude/skills/rite-vet/SKILL.md +18 -15
  52. package/pack/generated/claude/agents/devrites-code-reviewer.md +3 -0
  53. package/pack/generated/claude/agents/devrites-devex-reviewer.md +12 -8
  54. package/pack/generated/claude/agents/devrites-doubt-reviewer.md +3 -0
  55. package/pack/generated/claude/agents/devrites-forge-judge.md +4 -1
  56. package/pack/generated/claude/agents/devrites-frontend-reviewer.md +3 -0
  57. package/pack/generated/claude/agents/devrites-performance-reviewer.md +10 -9
  58. package/pack/generated/claude/agents/devrites-plan-reviewer.md +3 -0
  59. package/pack/generated/claude/agents/devrites-proof-runner.md +14 -14
  60. package/pack/generated/claude/agents/devrites-retrospector.md +3 -0
  61. package/pack/generated/claude/agents/devrites-security-auditor.md +3 -0
  62. package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +3 -0
  63. package/pack/generated/claude/agents/devrites-slice-wright.md +6 -5
  64. package/pack/generated/claude/agents/devrites-spec-reviewer.md +3 -0
  65. package/pack/generated/claude/agents/devrites-strategy-reviewer.md +3 -0
  66. package/pack/generated/claude/agents/devrites-test-analyst.md +3 -0
  67. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +8 -0
  68. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +10 -0
  69. package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +1 -1
  70. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +2 -2
  71. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +28 -4
  72. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +7 -5
  73. package/pack/generated/claude/skills/rite-prove/SKILL.md +25 -14
  74. package/pack/generated/claude/skills/rite-temper/SKILL.md +1 -1
  75. package/pack/generated/claude/skills/rite-upgrade/SKILL.md +1 -1
  76. package/pack/generated/claude/skills/rite-vet/SKILL.md +18 -15
  77. package/pack/generated/codex/agents/devrites-code-reviewer.toml +3 -0
  78. package/pack/generated/codex/agents/devrites-devex-reviewer.toml +11 -7
  79. package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +3 -0
  80. package/pack/generated/codex/agents/devrites-forge-judge.toml +4 -1
  81. package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +3 -0
  82. package/pack/generated/codex/agents/devrites-performance-reviewer.toml +9 -8
  83. package/pack/generated/codex/agents/devrites-plan-reviewer.toml +3 -0
  84. package/pack/generated/codex/agents/devrites-proof-runner.toml +13 -13
  85. package/pack/generated/codex/agents/devrites-retrospector.toml +3 -0
  86. package/pack/generated/codex/agents/devrites-security-auditor.toml +3 -0
  87. package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +3 -0
  88. package/pack/generated/codex/agents/devrites-slice-wright.toml +6 -5
  89. package/pack/generated/codex/agents/devrites-spec-reviewer.toml +3 -0
  90. package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +3 -0
  91. package/pack/generated/codex/agents/devrites-test-analyst.toml +3 -0
  92. package/pack/generated/codex/hooks.json +1 -1
  93. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +8 -0
  94. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +10 -0
  95. package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +1 -1
  96. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +2 -2
  97. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +28 -4
  98. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +7 -5
  99. package/pack/generated/codex/skills/rite-prove/SKILL.md +25 -14
  100. package/pack/generated/codex/skills/rite-temper/SKILL.md +1 -1
  101. package/pack/generated/codex/skills/rite-upgrade/SKILL.md +1 -1
  102. package/pack/generated/codex/skills/rite-vet/SKILL.md +18 -15
  103. package/package.json +1 -1
  104. package/scripts/codex-generate.sh +1 -1
@@ -1,42 +1,147 @@
1
1
  package lib
2
2
 
3
3
  import (
4
+ "bytes"
5
+ "context"
6
+ "errors"
7
+ "fmt"
8
+ "os"
4
9
  "os/exec"
5
10
  "path/filepath"
6
11
  "strings"
12
+ "time"
7
13
  )
8
14
 
9
- // gitToplevel returns the absolute path of the git working tree containing dir,
10
- // or "" when dir is not inside a repository. The git-backed gates
11
- // (test-integrity and reconcile) skip rather than block when it is empty.
12
- func gitToplevel(dir string) string {
13
- out, err := exec.Command("git", "-C", dir, "rev-parse", "--show-toplevel").Output()
15
+ var errNotGitRepository = errors.New("not a git repository")
16
+
17
+ const (
18
+ gitCommandTimeout = 30 * time.Second
19
+ gitOutputLimit = 16 << 20
20
+ )
21
+
22
+ type cappedGitOutput struct {
23
+ bytes.Buffer
24
+ truncated bool
25
+ }
26
+
27
+ func (w *cappedGitOutput) Write(p []byte) (int, error) {
28
+ remaining := gitOutputLimit - w.Len()
29
+ if remaining > 0 {
30
+ if len(p) > remaining {
31
+ _, _ = w.Buffer.Write(p[:remaining])
32
+ w.truncated = true
33
+ } else {
34
+ _, _ = w.Buffer.Write(p)
35
+ }
36
+ } else {
37
+ w.truncated = true
38
+ }
39
+ return len(p), nil
40
+ }
41
+
42
+ type gitCommandError struct {
43
+ args []string
44
+ output string
45
+ exitCode int
46
+ err error
47
+ }
48
+
49
+ func (e *gitCommandError) Error() string {
50
+ detail := strings.TrimSpace(e.output)
51
+ if detail == "" {
52
+ return fmt.Sprintf("git %s: %v", strings.Join(e.args, " "), e.err)
53
+ }
54
+ return fmt.Sprintf("git %s: %v: %s", strings.Join(e.args, " "), e.err, detail)
55
+ }
56
+
57
+ func (e *gitCommandError) Unwrap() error { return e.err }
58
+
59
+ func runGitCommand(dir string, env []string, args ...string) ([]byte, error) {
60
+ ctx, cancel := context.WithTimeout(context.Background(), gitCommandTimeout)
61
+ defer cancel()
62
+ commandArgs := append([]string{"-C", dir}, args...)
63
+ cmd := exec.CommandContext(ctx, "git", commandArgs...)
64
+ cmd.WaitDelay = 2 * time.Second
65
+ if env == nil {
66
+ env = os.Environ()
67
+ } else {
68
+ env = append([]string{}, env...)
69
+ }
70
+ cmd.Env = append(
71
+ env,
72
+ "GIT_TERMINAL_PROMPT=0",
73
+ "GCM_INTERACTIVE=never",
74
+ "GIT_PAGER=cat",
75
+ "PAGER=cat",
76
+ "LC_ALL=C",
77
+ )
78
+ var output cappedGitOutput
79
+ cmd.Stdout = &output
80
+ cmd.Stderr = &output
81
+ err := cmd.Run()
82
+ if err == nil {
83
+ return output.Bytes(), nil
84
+ }
85
+ code := -1
86
+ var exitErr *exec.ExitError
87
+ if errors.As(err, &exitErr) {
88
+ code = exitErr.ExitCode()
89
+ }
90
+ if ctx.Err() != nil {
91
+ err = ctx.Err()
92
+ }
93
+ detail := output.String()
94
+ if output.truncated {
95
+ detail += "\n[git output truncated]"
96
+ }
97
+ return nil, &gitCommandError{
98
+ args: args,
99
+ output: detail,
100
+ exitCode: code,
101
+ err: err,
102
+ }
103
+ }
104
+
105
+ func gitErrorExitCode(err error) (int, bool) {
106
+ var commandErr *gitCommandError
107
+ if !errors.As(err, &commandErr) {
108
+ return 0, false
109
+ }
110
+ return commandErr.exitCode, commandErr.exitCode >= 0
111
+ }
112
+
113
+ // gitToplevel returns the absolute path of the Git working tree containing dir.
114
+ // Repository absence is distinct from Git execution failure so integrity gates
115
+ // can fail closed on a missing binary, corrupt repository, or poisoned runtime.
116
+ func gitToplevel(dir string) (string, error) {
117
+ out, err := runGitCommand(dir, nil, "rev-parse", "--show-toplevel")
14
118
  if err != nil {
15
- return ""
119
+ var commandErr *gitCommandError
120
+ if errors.As(err, &commandErr) {
121
+ detail := strings.ToLower(commandErr.output)
122
+ if strings.Contains(detail, "not a git repository") ||
123
+ strings.Contains(detail, "not a git work tree") {
124
+ return "", errNotGitRepository
125
+ }
126
+ }
127
+ return "", err
128
+ }
129
+ root := strings.TrimSpace(string(out))
130
+ if root == "" {
131
+ return "", fmt.Errorf("git rev-parse returned an empty top-level path")
16
132
  }
17
- return filepath.Clean(strings.TrimSpace(string(out)))
133
+ return filepath.Clean(root), nil
18
134
  }
19
135
 
20
136
  // gitDiffNames lists the paths that differ, relative to gitRoot. With one ref it
21
137
  // diffs the working tree against that ref; with two it diffs the two tree objects.
22
- func gitDiffNames(gitRoot string, refs ...string) []string {
23
- args := append([]string{"-C", gitRoot, "diff", "--name-only"}, refs...)
24
- out, err := exec.Command("git", args...).Output()
138
+ func gitDiffNames(gitRoot string, refs ...string) ([]string, error) {
139
+ args := append([]string{"diff", "--name-only"}, refs...)
140
+ out, err := runGitCommand(gitRoot, nil, args...)
25
141
  if err != nil {
26
- return nil
142
+ return nil, err
27
143
  }
28
- return splitLinesNoTrailing(out)
29
- }
30
-
31
- // stripASCIISpace removes every whitespace character from s (used to normalise an
32
- // ACTIVE pointer before comparing it to a slug).
33
- func stripASCIISpace(s string) string {
34
- return strings.Map(func(r rune) rune {
35
- if strings.ContainsRune(spaceChars, r) {
36
- return -1
37
- }
38
- return r
39
- }, s)
144
+ return splitLinesNoTrailing(out), nil
40
145
  }
41
146
 
42
147
  // isAllDigits reports whether s is a non-empty run of ASCII digits.
package/engine/main.go CHANGED
@@ -56,7 +56,8 @@ Usage:
56
56
  devrites-engine mutation-gate [slug] Advisory: detect the mutation runner and scope it
57
57
  devrites-engine test-integrity [slug] Gate: no test deleted, skipped, or de-asserted
58
58
  devrites-engine review-integrity [slug] Gate: no adversarial review axis is silent (zero findings, no justification)
59
- devrites-engine reconcile <sub> [slug] A1 gate: snapshot|check|close the wright's source writes
59
+ devrites-engine dispatch-waive <reason> Record a deterministic pre-dispatch STOP reason
60
+ devrites-engine reconcile <sub> [slug] A1 gate: snapshot|check|restore-check|close the wright's source writes
60
61
  devrites-engine resolve <qid> "<ans>" Resolve an open question; keep state.md consistent
61
62
  devrites-engine close-out <slug> Archive a shipped feature and clear ACTIVE
62
63
  devrites-engine archive-search "<nouns>" Find prior shipped specs that overlap the query
@@ -238,6 +239,8 @@ func run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
238
239
  return lib.TestIntegrity(root, args[1:], stdout, stderr)
239
240
  case "review-integrity":
240
241
  return lib.ReviewIntegrity(root, args[1:], stdout, stderr)
242
+ case "dispatch-waive":
243
+ return lib.DispatchWaive(args[1:], stdout, stderr)
241
244
  case "reconcile":
242
245
  return lib.Reconcile(root, args[1:], stdout, stderr)
243
246
  case "resolve":
@@ -1,2 +1,3 @@
1
1
  exit 0
2
+ reconcile: OK: every slice change matches the orchestrator allowlist; baseline retained for post-slice gates (run "reconcile close" afterward).
2
3
  reconcile: closed slice window for feat.
@@ -1 +1 @@
1
- exit 0
1
+ exit 6
@@ -1 +1 @@
1
- exit 0
1
+ exit 2