devrites 3.2.4 → 3.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/engine/hooks_agent_dispatch.go +39 -9
- package/engine/tests/hook_test.go +42 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to DevRites are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and DevRites adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Releases are generated automatically by [semantic-release](https://semantic-release.gitbook.io/) from Conventional Commits on `main`.
|
|
4
4
|
|
|
5
|
+
## [3.2.5](https://github.com/ViktorsBaikers/DevRites/compare/v3.2.4...v3.2.5) (2026-07-25)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
* **agents:** retain v2 wright dispatch receipt ([84f0c05](https://github.com/ViktorsBaikers/DevRites/commit/84f0c05c9d91d85194dfd4c97593ca1c07183282))
|
|
10
|
+
|
|
5
11
|
## [3.2.4](https://github.com/ViktorsBaikers/DevRites/compare/v3.2.3...v3.2.4) (2026-07-25)
|
|
6
12
|
|
|
7
13
|
### Fixed
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ final commit, push, and tag, and it requires a typed `GO` confirmation.
|
|
|
25
25
|
Unattended runs may create local WIP checkpoint commits along the way, but only
|
|
26
26
|
Ship collapses and pushes them.
|
|
27
27
|
|
|
28
|
-
**Status:** [`v3.2.
|
|
28
|
+
**Status:** [`v3.2.5`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v3.2.5): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
|
|
29
29
|
|
|
30
30
|
## Quick start
|
|
31
31
|
|
|
@@ -457,6 +457,17 @@ func dispatchTurnState(events []agentDispatchEvent, turnID string) (map[string]s
|
|
|
457
457
|
attempt.Stopped = true
|
|
458
458
|
attempt.ResultSHA256 = event.ResultSHA256
|
|
459
459
|
}
|
|
460
|
+
case "verified":
|
|
461
|
+
attempts = append(attempts, &agentDispatchAttempt{
|
|
462
|
+
Role: event.Role,
|
|
463
|
+
AgentType: event.AgentType,
|
|
464
|
+
AgentID: event.AgentID,
|
|
465
|
+
WindowID: event.WindowID,
|
|
466
|
+
Started: true,
|
|
467
|
+
Stopped: true,
|
|
468
|
+
Waited: true,
|
|
469
|
+
ResultSHA256: event.ResultSHA256,
|
|
470
|
+
})
|
|
460
471
|
}
|
|
461
472
|
}
|
|
462
473
|
return armed, attempts
|
|
@@ -771,7 +782,7 @@ func reconcileWindowPredates(at time.Time) bool {
|
|
|
771
782
|
}
|
|
772
783
|
|
|
773
784
|
func dispatchAttemptComplete(attempt *agentDispatchAttempt) bool {
|
|
774
|
-
return attempt.Started && attempt.Stopped && attempt.ResultSHA256 != ""
|
|
785
|
+
return attempt.Started && attempt.Stopped && attempt.Waited && attempt.ResultSHA256 != ""
|
|
775
786
|
}
|
|
776
787
|
|
|
777
788
|
func roleSatisfied(role, windowID string, attempts []*agentDispatchAttempt) bool {
|
|
@@ -1092,15 +1103,34 @@ func hookAgentDispatchPreTool(h harness.Harness, root string, in agentDispatchHo
|
|
|
1092
1103
|
if windowID == "" {
|
|
1093
1104
|
return exitOK
|
|
1094
1105
|
}
|
|
1095
|
-
durable, durableErr := durableCodexV2DispatchAttempts(
|
|
1096
|
-
root, in.SessionID, in.TurnID, armed,
|
|
1097
|
-
)
|
|
1098
|
-
if durableErr != nil {
|
|
1099
|
-
return preToolDeny(h, "DevRites could not verify the Codex V2 wright result: "+durableErr.Error(), stdout, stderr)
|
|
1100
|
-
}
|
|
1101
|
-
attempts = append(attempts, durable...)
|
|
1102
1106
|
if !roleSatisfied("devrites-slice-wright", windowID, attempts) {
|
|
1103
|
-
|
|
1107
|
+
durable, durableErr := durableCodexV2DispatchAttempts(
|
|
1108
|
+
root, in.SessionID, in.TurnID, armed,
|
|
1109
|
+
)
|
|
1110
|
+
if durableErr != nil {
|
|
1111
|
+
return preToolDeny(h, "DevRites could not verify the Codex V2 wright result: "+durableErr.Error(), stdout, stderr)
|
|
1112
|
+
}
|
|
1113
|
+
attempts = append(attempts, durable...)
|
|
1114
|
+
if !roleSatisfied("devrites-slice-wright", windowID, attempts) {
|
|
1115
|
+
return preToolDeny(h, "DevRites reconcile check/close requires a confirmed, awaited devrites-slice-wright result bound to the active reconcile snapshot.", stdout, stderr)
|
|
1116
|
+
}
|
|
1117
|
+
for _, attempt := range durable {
|
|
1118
|
+
if attempt.Role != "devrites-slice-wright" || attempt.WindowID != windowID ||
|
|
1119
|
+
!dispatchAttemptComplete(attempt) {
|
|
1120
|
+
continue
|
|
1121
|
+
}
|
|
1122
|
+
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1123
|
+
Event: "verified",
|
|
1124
|
+
TurnID: in.TurnID,
|
|
1125
|
+
AgentID: attempt.AgentID,
|
|
1126
|
+
AgentType: attempt.AgentType,
|
|
1127
|
+
Role: attempt.Role,
|
|
1128
|
+
WindowID: attempt.WindowID,
|
|
1129
|
+
ResultSHA256: attempt.ResultSHA256,
|
|
1130
|
+
}); err != nil {
|
|
1131
|
+
return preToolDeny(h, "DevRites could not persist the verified Codex V2 wright result: "+err.Error(), stdout, stderr)
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1104
1134
|
}
|
|
1105
1135
|
}
|
|
1106
1136
|
return exitOK
|
|
@@ -1341,6 +1341,33 @@ func TestCodexAgentDispatchConfirmsGenericRoleAndResult(t *testing.T) {
|
|
|
1341
1341
|
"turn_id": turnID,
|
|
1342
1342
|
"stop_hook_active": false,
|
|
1343
1343
|
})
|
|
1344
|
+
out, stderr, code = runDevritesIO(t, root, stop, nil,
|
|
1345
|
+
"hook", "agent-dispatch", "--harness=codex")
|
|
1346
|
+
if code != 0 {
|
|
1347
|
+
t.Fatalf("unawaited stop exit=%d stderr=%s", code, stderr)
|
|
1348
|
+
}
|
|
1349
|
+
var blocked struct {
|
|
1350
|
+
Decision string `json:"decision"`
|
|
1351
|
+
}
|
|
1352
|
+
if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &blocked); err != nil {
|
|
1353
|
+
t.Fatalf("invalid unawaited Stop response: %v\n%s", err, out)
|
|
1354
|
+
}
|
|
1355
|
+
if blocked.Decision != "block" {
|
|
1356
|
+
t.Fatalf("unawaited child result passed Stop: %s", out)
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
wait := hookPayload(t, map[string]any{
|
|
1360
|
+
"hook_event_name": "PreToolUse",
|
|
1361
|
+
"session_id": sessionID,
|
|
1362
|
+
"turn_id": turnID,
|
|
1363
|
+
"tool_name": "wait",
|
|
1364
|
+
"tool_input": map[string]any{"ids": []string{"agent-1"}},
|
|
1365
|
+
})
|
|
1366
|
+
if out, stderr, code := runDevritesIO(t, root, wait, nil,
|
|
1367
|
+
"hook", "agent-dispatch", "--harness=codex"); code != 0 || strings.TrimSpace(out) != "" {
|
|
1368
|
+
t.Fatalf("wait exit=%d out=%s stderr=%s", code, out, stderr)
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1344
1371
|
if out, stderr, code := runDevritesIO(t, root, stop, nil,
|
|
1345
1372
|
"hook", "agent-dispatch", "--harness=codex"); code != 0 || strings.TrimSpace(out) != "" {
|
|
1346
1373
|
t.Fatalf("resolved stop exit=%d out=%s stderr=%s", code, out, stderr)
|
|
@@ -1579,6 +1606,21 @@ func TestCodexAgentDispatchBindsDurableV2WrightToReconcileWindow(t *testing.T) {
|
|
|
1579
1606
|
if decision, _ := parsePermissionDecision(t, out); decision != "deny" {
|
|
1580
1607
|
t.Fatalf("changed-window V2 wright was accepted: %s", out)
|
|
1581
1608
|
}
|
|
1609
|
+
return
|
|
1610
|
+
}
|
|
1611
|
+
for _, name := range []string{".reconcile-base", ".reconcile-allowlist", ".reconcile-devrites"} {
|
|
1612
|
+
if err := os.Remove(filepath.Join(workspace, name)); err != nil {
|
|
1613
|
+
t.Fatal(err)
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
stop := hookPayload(t, map[string]any{
|
|
1617
|
+
"hook_event_name": "Stop",
|
|
1618
|
+
"session_id": sessionID,
|
|
1619
|
+
"turn_id": turnID,
|
|
1620
|
+
})
|
|
1621
|
+
if out, stderr, code := runDevritesIO(t, root, stop, nil,
|
|
1622
|
+
"hook", "agent-dispatch", "--harness=codex"); code != 0 || strings.TrimSpace(out) != "" {
|
|
1623
|
+
t.Fatalf("persisted V2 wright receipt was lost after close: exit=%d out=%s stderr=%s", code, out, stderr)
|
|
1582
1624
|
}
|
|
1583
1625
|
})
|
|
1584
1626
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devrites",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.5",
|
|
4
4
|
"description": "DevRites: a disciplined senior-engineer workflow pack for Claude Code and Codex",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://github.com/ViktorsBaikers/DevRites#readme",
|