devrites 3.2.11 → 3.2.13
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/engine/hooks_agent_dispatch.go +67 -2
- package/engine/tests/hook_test.go +169 -0
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +3 -2
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +3 -2
- package/pack/generated/codex/hooks.json +10 -0
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +3 -2
- package/package.json +1 -1
- package/scripts/codex-generate.sh +10 -0
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
|
+
## [3.2.13](https://github.com/ViktorsBaikers/DevRites/compare/v3.2.12...v3.2.13) (2026-07-26)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
* **agents:** retain canonical boundary before spawn ([b290aa9](https://github.com/ViktorsBaikers/DevRites/commit/b290aa97851915afcddd85ff43855fc8e07b6860))
|
|
10
|
+
|
|
11
|
+
## [3.2.12](https://github.com/ViktorsBaikers/DevRites/compare/v3.2.11...v3.2.12) (2026-07-26)
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
* **agents:** capture wright boundary before spawn ([4eb95f3](https://github.com/ViktorsBaikers/DevRites/commit/4eb95f3f75c150b9c535972db6d84fadf97f1a87))
|
|
16
|
+
|
|
5
17
|
## [3.2.11](https://github.com/ViktorsBaikers/DevRites/compare/v3.2.10...v3.2.11) (2026-07-26)
|
|
6
18
|
|
|
7
19
|
### 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.13`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v3.2.13): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
|
|
29
29
|
|
|
30
30
|
## Quick start
|
|
31
31
|
|
|
@@ -17,6 +17,7 @@ import (
|
|
|
17
17
|
"time"
|
|
18
18
|
|
|
19
19
|
"github.com/devrites/devrites/internal/harness"
|
|
20
|
+
"github.com/devrites/devrites/internal/lib"
|
|
20
21
|
"github.com/devrites/devrites/internal/orient"
|
|
21
22
|
"github.com/devrites/devrites/internal/safepath"
|
|
22
23
|
)
|
|
@@ -400,8 +401,9 @@ func currentReconcileWindowID() string {
|
|
|
400
401
|
if !ok {
|
|
401
402
|
return ""
|
|
402
403
|
}
|
|
403
|
-
// The wright-start canonical fingerprint is captured
|
|
404
|
-
//
|
|
404
|
+
// The wright-start canonical fingerprint is captured by the spawn hook
|
|
405
|
+
// before the child starts. It narrows the delta but does not change source
|
|
406
|
+
// authorization.
|
|
405
407
|
names := []string{".reconcile-base", ".reconcile-allowlist", ".reconcile-devrites"}
|
|
406
408
|
h := sha256.New()
|
|
407
409
|
for _, name := range names {
|
|
@@ -895,6 +897,41 @@ func stopDispatchFailure(reason string, stdout, stderr io.Writer) int {
|
|
|
895
897
|
return exitOK
|
|
896
898
|
}
|
|
897
899
|
|
|
900
|
+
func refreshPendingWrightBoundary(
|
|
901
|
+
root string,
|
|
902
|
+
in agentDispatchHookInput,
|
|
903
|
+
armed map[string]struct{},
|
|
904
|
+
attempts []*agentDispatchAttempt,
|
|
905
|
+
) error {
|
|
906
|
+
if in.AgentID != "" || in.AgentType != "" || currentReconcileWindowID() == "" {
|
|
907
|
+
return nil
|
|
908
|
+
}
|
|
909
|
+
_, wrightRequired := armed["devrites-slice-wright"]
|
|
910
|
+
_, conditional := armed[agentDispatchSkillGuard]
|
|
911
|
+
if !wrightRequired && !conditional {
|
|
912
|
+
return nil
|
|
913
|
+
}
|
|
914
|
+
for _, attempt := range attempts {
|
|
915
|
+
if attempt.Role == "devrites-slice-wright" {
|
|
916
|
+
return nil
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
durable, err := durableCodexV2DispatchAttempts(root, in.SessionID, in.TurnID, armed)
|
|
920
|
+
if err != nil {
|
|
921
|
+
return err
|
|
922
|
+
}
|
|
923
|
+
for _, attempt := range durable {
|
|
924
|
+
if attempt.Role == "devrites-slice-wright" {
|
|
925
|
+
return nil
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
workspaceRoot, slug, _, ok := resolveWorkspace()
|
|
929
|
+
if !ok || !sameResolvedPath(workspaceRoot, root) {
|
|
930
|
+
return fmt.Errorf("active reconcile window is unavailable")
|
|
931
|
+
}
|
|
932
|
+
return lib.CaptureReconcileWrightBoundary(root, slug)
|
|
933
|
+
}
|
|
934
|
+
|
|
898
935
|
func hookAgentDispatch(h harness.Harness, stdin io.Reader, stdout, stderr io.Writer) int {
|
|
899
936
|
if h != harness.Codex {
|
|
900
937
|
_, _ = io.Copy(io.Discard, stdin)
|
|
@@ -940,6 +977,13 @@ func hookAgentDispatch(h harness.Harness, stdin io.Reader, stdout, stderr io.Wri
|
|
|
940
977
|
return stopDispatchBlock(h, "DevRites could not arm the required agent dispatch: "+err.Error(), stdout, stderr)
|
|
941
978
|
}
|
|
942
979
|
}
|
|
980
|
+
events, stateErr := readAgentDispatchEvents(root, in.SessionID)
|
|
981
|
+
if stateErr == nil {
|
|
982
|
+
armed, attempts := dispatchTurnState(events, in.TurnID)
|
|
983
|
+
if err := refreshPendingWrightBoundary(root, in, armed, attempts); err != nil {
|
|
984
|
+
debugf(stderr, "agent-dispatch pending wright boundary: %v", err)
|
|
985
|
+
}
|
|
986
|
+
}
|
|
943
987
|
if len(roles) > 0 {
|
|
944
988
|
instructions := make([]string, 0, len(roles))
|
|
945
989
|
for _, role := range roles {
|
|
@@ -962,6 +1006,18 @@ func hookAgentDispatch(h harness.Harness, stdin io.Reader, stdout, stderr io.Wri
|
|
|
962
1006
|
case "PreToolUse":
|
|
963
1007
|
return hookAgentDispatchPreTool(h, root, in, stdout, stderr)
|
|
964
1008
|
|
|
1009
|
+
case "PostToolUse":
|
|
1010
|
+
events, err := readAgentDispatchEvents(root, in.SessionID)
|
|
1011
|
+
if err != nil {
|
|
1012
|
+
debugf(stderr, "agent-dispatch post-tool state: %v", err)
|
|
1013
|
+
return exitOK
|
|
1014
|
+
}
|
|
1015
|
+
armed, attempts := dispatchTurnState(events, in.TurnID)
|
|
1016
|
+
if err := refreshPendingWrightBoundary(root, in, armed, attempts); err != nil {
|
|
1017
|
+
debugf(stderr, "agent-dispatch post-tool wright boundary: %v", err)
|
|
1018
|
+
}
|
|
1019
|
+
return exitOK
|
|
1020
|
+
|
|
965
1021
|
case "SubagentStop":
|
|
966
1022
|
if in.AgentID == "" {
|
|
967
1023
|
return exitOK
|
|
@@ -1088,6 +1144,15 @@ func hookAgentDispatchPreTool(h harness.Harness, root string, in agentDispatchHo
|
|
|
1088
1144
|
if role == "devrites-slice-wright" && windowID == "" {
|
|
1089
1145
|
return preToolDeny(h, "devrites-slice-wright requires an active reconcile snapshot before spawn_agent.", stdout, stderr)
|
|
1090
1146
|
}
|
|
1147
|
+
if role == "devrites-slice-wright" {
|
|
1148
|
+
workspaceRoot, slug, _, ok := resolveWorkspace()
|
|
1149
|
+
if !ok || !sameResolvedPath(workspaceRoot, root) {
|
|
1150
|
+
return preToolDeny(h, "DevRites could not resolve the active wright reconcile window before spawn_agent.", stdout, stderr)
|
|
1151
|
+
}
|
|
1152
|
+
if err := lib.CaptureReconcileWrightBoundary(root, slug); err != nil {
|
|
1153
|
+
return preToolDeny(h, "DevRites could not capture the wright canonical boundary before spawn_agent: "+err.Error(), stdout, stderr)
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1091
1156
|
if err := appendAgentDispatchEvent(root, in.SessionID, agentDispatchEvent{
|
|
1092
1157
|
Event: "pending",
|
|
1093
1158
|
TurnID: in.TurnID,
|
|
@@ -5,6 +5,7 @@ package main_test
|
|
|
5
5
|
// check stdout and exit status. parity_test.go owns the parity oracle.
|
|
6
6
|
|
|
7
7
|
import (
|
|
8
|
+
"bytes"
|
|
8
9
|
"encoding/json"
|
|
9
10
|
"os"
|
|
10
11
|
"path/filepath"
|
|
@@ -1900,6 +1901,174 @@ func TestCodexAgentDispatchGatesReconcileCloseOnBoundWrightResult(t *testing.T)
|
|
|
1900
1901
|
}
|
|
1901
1902
|
}
|
|
1902
1903
|
|
|
1904
|
+
func TestCodexAgentDispatchCapturesWrightBoundaryBeforeSpawn(t *testing.T) {
|
|
1905
|
+
root := newWorkspace(t)
|
|
1906
|
+
writeActive(t, root, "auth-tokens")
|
|
1907
|
+
workspace := filepath.Join(root, "features", "auth-tokens")
|
|
1908
|
+
for name, body := range map[string]string{
|
|
1909
|
+
".reconcile-base": ".reconcile-base\n",
|
|
1910
|
+
".reconcile-allowlist": ".reconcile-allowlist\n",
|
|
1911
|
+
".reconcile-devrites": "[]\n",
|
|
1912
|
+
"action.log": "Prior root action\n",
|
|
1913
|
+
"browser-evidence.md": "Prior root evidence\n",
|
|
1914
|
+
"decisions.md": "Prior root decision\n",
|
|
1915
|
+
"evidence.md": "Prior root evidence\n",
|
|
1916
|
+
"footprint.log": "Prior root footprint\n",
|
|
1917
|
+
"state.md": "Phase: build\n",
|
|
1918
|
+
"touched-files.md": "Prior root manifest\n",
|
|
1919
|
+
} {
|
|
1920
|
+
if err := os.WriteFile(filepath.Join(workspace, name), []byte(body), 0o600); err != nil {
|
|
1921
|
+
t.Fatal(err)
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
if err := os.MkdirAll(filepath.Join(workspace, ".reconcile-objects"), 0o700); err != nil {
|
|
1925
|
+
t.Fatal(err)
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
sessionID, turnID := "session-wright-spawn", "turn-wright-spawn"
|
|
1929
|
+
writeCodexAgentContract(t, root, "devrites-slice-wright")
|
|
1930
|
+
writeCodexSkillContract(t, root, "rite-build", "devrites-slice-wright")
|
|
1931
|
+
runDevritesIO(t, root, hookPayload(t, map[string]any{
|
|
1932
|
+
"hook_event_name": "UserPromptSubmit",
|
|
1933
|
+
"session_id": sessionID,
|
|
1934
|
+
"turn_id": turnID,
|
|
1935
|
+
"prompt": "$rite-build",
|
|
1936
|
+
}), nil, "hook", "agent-dispatch", "--harness=codex")
|
|
1937
|
+
|
|
1938
|
+
spawn := hookPayload(t, map[string]any{
|
|
1939
|
+
"hook_event_name": "PreToolUse",
|
|
1940
|
+
"session_id": sessionID,
|
|
1941
|
+
"turn_id": turnID,
|
|
1942
|
+
"tool_name": "spawn_agent",
|
|
1943
|
+
"tool_use_id": "spawn-wright",
|
|
1944
|
+
"tool_input": map[string]any{
|
|
1945
|
+
"agent_type": "devrites-slice-wright",
|
|
1946
|
+
"fork_turns": "none",
|
|
1947
|
+
"message": "Read .codex/agents/devrites-slice-wright.toml.",
|
|
1948
|
+
},
|
|
1949
|
+
})
|
|
1950
|
+
if out, stderr, code := runDevritesIO(t, root, spawn, nil,
|
|
1951
|
+
"hook", "agent-dispatch", "--harness=codex"); code != 0 || strings.TrimSpace(out) != "" {
|
|
1952
|
+
t.Fatalf("spawn exit=%d out=%s stderr=%s", code, out, stderr)
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
raw, err := os.ReadFile(filepath.Join(workspace, ".reconcile-wright-devrites"))
|
|
1956
|
+
if err != nil {
|
|
1957
|
+
t.Fatalf("read wright boundary: %v", err)
|
|
1958
|
+
}
|
|
1959
|
+
for _, path := range []string{
|
|
1960
|
+
"features/auth-tokens/action.log",
|
|
1961
|
+
"features/auth-tokens/browser-evidence.md",
|
|
1962
|
+
"features/auth-tokens/decisions.md",
|
|
1963
|
+
"features/auth-tokens/evidence.md",
|
|
1964
|
+
"features/auth-tokens/footprint.log",
|
|
1965
|
+
"features/auth-tokens/state.md",
|
|
1966
|
+
"features/auth-tokens/touched-files.md",
|
|
1967
|
+
} {
|
|
1968
|
+
if !strings.Contains(string(raw), path) {
|
|
1969
|
+
t.Errorf("wright boundary omitted %s: %s", path, raw)
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
func TestCodexAgentDispatchRefreshesWrightBoundaryUntilDurableSpawn(t *testing.T) {
|
|
1975
|
+
root := newWorkspace(t)
|
|
1976
|
+
codeHome := t.TempDir()
|
|
1977
|
+
writeActive(t, root, "auth-tokens")
|
|
1978
|
+
workspace := filepath.Join(root, "features", "auth-tokens")
|
|
1979
|
+
for name, body := range map[string]string{
|
|
1980
|
+
".reconcile-base": ".reconcile-base\n",
|
|
1981
|
+
".reconcile-allowlist": ".reconcile-allowlist\n",
|
|
1982
|
+
".reconcile-devrites": "[]\n",
|
|
1983
|
+
"action.log": "Prior root action\n",
|
|
1984
|
+
"browser-evidence.md": "Prior root evidence\n",
|
|
1985
|
+
"decisions.md": "Prior root decision\n",
|
|
1986
|
+
"evidence.md": "Prior root evidence\n",
|
|
1987
|
+
"footprint.log": "Prior root footprint\n",
|
|
1988
|
+
"state.md": "Phase: ready\n",
|
|
1989
|
+
"touched-files.md": "Prior root manifest\n",
|
|
1990
|
+
} {
|
|
1991
|
+
if err := os.WriteFile(filepath.Join(workspace, name), []byte(body), 0o600); err != nil {
|
|
1992
|
+
t.Fatal(err)
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
if err := os.MkdirAll(filepath.Join(workspace, ".reconcile-objects"), 0o700); err != nil {
|
|
1996
|
+
t.Fatal(err)
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
sessionID, turnID := "session-wright-durable", "turn-wright-durable"
|
|
2000
|
+
role := "devrites-slice-wright"
|
|
2001
|
+
writeCodexAgentContract(t, root, role)
|
|
2002
|
+
writeCodexSkillContract(t, root, "rite-build", role)
|
|
2003
|
+
env := []string{"CODEX_HOME=" + codeHome}
|
|
2004
|
+
runDevritesIO(t, root, hookPayload(t, map[string]any{
|
|
2005
|
+
"hook_event_name": "UserPromptSubmit",
|
|
2006
|
+
"session_id": sessionID,
|
|
2007
|
+
"turn_id": turnID,
|
|
2008
|
+
"prompt": "$rite-build",
|
|
2009
|
+
}), env, "hook", "agent-dispatch", "--harness=codex")
|
|
2010
|
+
|
|
2011
|
+
wrightState := filepath.Join(workspace, ".reconcile-wright-devrites")
|
|
2012
|
+
initial, err := os.ReadFile(wrightState)
|
|
2013
|
+
if err != nil {
|
|
2014
|
+
t.Fatalf("read initial wright boundary: %v", err)
|
|
2015
|
+
}
|
|
2016
|
+
for _, path := range []string{
|
|
2017
|
+
"features/auth-tokens/action.log",
|
|
2018
|
+
"features/auth-tokens/browser-evidence.md",
|
|
2019
|
+
"features/auth-tokens/decisions.md",
|
|
2020
|
+
"features/auth-tokens/evidence.md",
|
|
2021
|
+
"features/auth-tokens/footprint.log",
|
|
2022
|
+
"features/auth-tokens/state.md",
|
|
2023
|
+
"features/auth-tokens/touched-files.md",
|
|
2024
|
+
} {
|
|
2025
|
+
if !strings.Contains(string(initial), path) {
|
|
2026
|
+
t.Errorf("pending wright boundary omitted %s: %s", path, initial)
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
if err := os.WriteFile(filepath.Join(workspace, "state.md"), []byte("Phase: build\n"), 0o600); err != nil {
|
|
2030
|
+
t.Fatal(err)
|
|
2031
|
+
}
|
|
2032
|
+
postTool := hookPayload(t, map[string]any{
|
|
2033
|
+
"hook_event_name": "PostToolUse",
|
|
2034
|
+
"session_id": sessionID,
|
|
2035
|
+
"turn_id": turnID,
|
|
2036
|
+
"tool_name": "apply_patch",
|
|
2037
|
+
"tool_use_id": "root-state-edit",
|
|
2038
|
+
"tool_input": map[string]any{"file_path": "state.md"},
|
|
2039
|
+
})
|
|
2040
|
+
if out, stderr, code := runDevritesIO(t, root, postTool, env,
|
|
2041
|
+
"hook", "agent-dispatch", "--harness=codex"); code != 0 || strings.TrimSpace(out) != "" {
|
|
2042
|
+
t.Fatalf("root post-tool refresh exit=%d out=%s stderr=%s", code, out, stderr)
|
|
2043
|
+
}
|
|
2044
|
+
beforeSpawn, err := os.ReadFile(wrightState)
|
|
2045
|
+
if err != nil {
|
|
2046
|
+
t.Fatalf("read refreshed wright boundary: %v", err)
|
|
2047
|
+
}
|
|
2048
|
+
if bytes.Equal(initial, beforeSpawn) {
|
|
2049
|
+
t.Fatal("root PostToolUse did not refresh the pending wright boundary")
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
writeCodexV2Rollouts(
|
|
2053
|
+
t, codeHome, filepath.Dir(root), sessionID, turnID, role,
|
|
2054
|
+
"devrites_slice_wright_unhooked", true, true, true,
|
|
2055
|
+
)
|
|
2056
|
+
if err := os.WriteFile(filepath.Join(workspace, "state.md"), []byte("Writer mutation\n"), 0o600); err != nil {
|
|
2057
|
+
t.Fatal(err)
|
|
2058
|
+
}
|
|
2059
|
+
if out, stderr, code := runDevritesIO(t, root, postTool, env,
|
|
2060
|
+
"hook", "agent-dispatch", "--harness=codex"); code != 0 || strings.TrimSpace(out) != "" {
|
|
2061
|
+
t.Fatalf("post-spawn PostToolUse exit=%d out=%s stderr=%s", code, out, stderr)
|
|
2062
|
+
}
|
|
2063
|
+
afterSpawn, err := os.ReadFile(wrightState)
|
|
2064
|
+
if err != nil {
|
|
2065
|
+
t.Fatalf("read frozen wright boundary: %v", err)
|
|
2066
|
+
}
|
|
2067
|
+
if !bytes.Equal(beforeSpawn, afterSpawn) {
|
|
2068
|
+
t.Fatal("durable V2 spawn did not freeze the pre-writer canonical boundary")
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
|
|
1903
2072
|
func TestCodexAgentDispatchBindsDurableV2WrightToReconcileWindow(t *testing.T) {
|
|
1904
2073
|
for _, tc := range []struct {
|
|
1905
2074
|
name string
|
|
@@ -123,8 +123,9 @@ resume the same slice. Close it before abandoning the slice for a scope/plan tra
|
|
|
123
123
|
Use named wright → enforced V1 generic worker from `standards/agents.md`. Codex V2
|
|
124
124
|
dispatches the exact named `devrites-slice-wright` with a unique `task_name` and
|
|
125
125
|
`fork_turns="none"`; its durable rollout proves the native role, wait, and non-empty
|
|
126
|
-
result in the current window.
|
|
127
|
-
|
|
126
|
+
result in the current window. Root lifecycle hooks keep the pending canonical
|
|
127
|
+
boundary current until the accepted or durable spawn, then freeze it apart from the
|
|
128
|
+
retained source baseline: pre-start root recovery records are excluded, while
|
|
128
129
|
post-start canonical changes fail.
|
|
129
130
|
Any generic worker still needs the exact allowlist or an isolated checkout;
|
|
130
131
|
reconciliation alone is insufficient. If no safe fresh-agent rung is available, stop
|
|
@@ -123,8 +123,9 @@ resume the same slice. Close it before abandoning the slice for a scope/plan tra
|
|
|
123
123
|
Use named wright → enforced V1 generic worker from `standards/agents.md`. Codex V2
|
|
124
124
|
dispatches the exact named `devrites-slice-wright` with a unique `task_name` and
|
|
125
125
|
`fork_turns="none"`; its durable rollout proves the native role, wait, and non-empty
|
|
126
|
-
result in the current window.
|
|
127
|
-
|
|
126
|
+
result in the current window. Root lifecycle hooks keep the pending canonical
|
|
127
|
+
boundary current until the accepted or durable spawn, then freeze it apart from the
|
|
128
|
+
retained source baseline: pre-start root recovery records are excluded, while
|
|
128
129
|
post-start canonical changes fail.
|
|
129
130
|
Any generic worker still needs the exact allowlist or an isolated checkout;
|
|
130
131
|
reconciliation alone is insufficient. If no safe fresh-agent rung is available, stop
|
|
@@ -57,6 +57,16 @@
|
|
|
57
57
|
"statusMessage": "DevRites: checking test/build result"
|
|
58
58
|
}
|
|
59
59
|
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"matcher": "Bash|Shell|sh|exec_command|run_command|Edit|Write|MultiEdit|NotebookEdit|apply_patch|exec|js|python|computer|computer_use|write_stdin|run_code|Agent|Task|spawn_agent|delegate|dispatch_agent|create_agent|wait|wait_agent",
|
|
63
|
+
"hooks": [
|
|
64
|
+
{
|
|
65
|
+
"type": "command",
|
|
66
|
+
"command": "command -v devrites-engine >/dev/null 2>&1 || exit 0; cd \"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\" 2>/dev/null || exit 0; exec devrites-engine hook agent-dispatch --harness=codex",
|
|
67
|
+
"statusMessage": "DevRites: retaining the pre-writer canonical boundary"
|
|
68
|
+
}
|
|
69
|
+
]
|
|
60
70
|
}
|
|
61
71
|
],
|
|
62
72
|
"PreCompact": [
|
|
@@ -123,8 +123,9 @@ resume the same slice. Close it before abandoning the slice for a scope/plan tra
|
|
|
123
123
|
Use named wright → enforced V1 generic worker from `standards/agents.md`. Codex V2
|
|
124
124
|
dispatches the exact named `devrites-slice-wright` with a unique `task_name` and
|
|
125
125
|
`fork_turns="none"`; its durable rollout proves the native role, wait, and non-empty
|
|
126
|
-
result in the current window.
|
|
127
|
-
|
|
126
|
+
result in the current window. Root lifecycle hooks keep the pending canonical
|
|
127
|
+
boundary current until the accepted or durable spawn, then freeze it apart from the
|
|
128
|
+
retained source baseline: pre-start root recovery records are excluded, while
|
|
128
129
|
post-start canonical changes fail.
|
|
129
130
|
Any generic worker still needs the exact allowlist or an isolated checkout;
|
|
130
131
|
reconciliation alone is insufficient. If no safe fresh-agent rung is available, stop
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devrites",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.13",
|
|
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",
|
|
@@ -246,6 +246,16 @@ gen_codex_hooks_json() {
|
|
|
246
246
|
"statusMessage": "DevRites: checking test/build result"
|
|
247
247
|
}
|
|
248
248
|
]
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"matcher": "Bash|Shell|sh|exec_command|run_command|Edit|Write|MultiEdit|NotebookEdit|apply_patch|exec|js|python|computer|computer_use|write_stdin|run_code|Agent|Task|spawn_agent|delegate|dispatch_agent|create_agent|wait|wait_agent",
|
|
252
|
+
"hooks": [
|
|
253
|
+
{
|
|
254
|
+
"type": "command",
|
|
255
|
+
"command": "command -v devrites-engine >/dev/null 2>&1 || exit 0; cd \"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\" 2>/dev/null || exit 0; exec devrites-engine hook agent-dispatch --harness=codex",
|
|
256
|
+
"statusMessage": "DevRites: retaining the pre-writer canonical boundary"
|
|
257
|
+
}
|
|
258
|
+
]
|
|
249
259
|
}
|
|
250
260
|
],
|
|
251
261
|
"PreCompact": [
|