devrites 6.1.3 → 6.1.4
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/internal/lib/claims.go +4 -0
- package/engine/internal/lib/claims_holder.go +35 -5
- package/engine/internal/lib/dispatch.go +3 -0
- package/pack/.claude/skills/rite-build/SKILL.md +3 -0
- package/pack/generated/claude/skills/rite-build/SKILL.md +3 -0
- package/pack/generated/codex/skills/rite-build/SKILL.md +3 -0
- package/pack/generated/devin/skills/rite-build/SKILL.md +3 -0
- package/pack/generated/omp/skills/rite-build/SKILL.md +3 -0
- package/pack/generated/pi/skills/rite-build/SKILL.md +3 -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
|
+
## [6.1.4](https://github.com/ViktorsBaikers/DevRites/compare/v6.1.3...v6.1.4) (2026-10-16)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
* **devrites:** free dead-session claims and surface writer-safe rule ([#110](https://github.com/ViktorsBaikers/DevRites/issues/110)) ([3d431b1](https://github.com/ViktorsBaikers/DevRites/commit/3d431b12cab83d4aaff7aaa4ced2c75210416a90))
|
|
10
|
+
|
|
5
11
|
## [6.1.3](https://github.com/ViktorsBaikers/DevRites/compare/v6.1.2...v6.1.3) (2026-10-16)
|
|
6
12
|
|
|
7
13
|
### Changed
|
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.1.
|
|
34
|
+
**Status:** [`v6.1.4`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v6.1.4): 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
|
|
|
@@ -55,6 +55,9 @@ type claimRecord struct {
|
|
|
55
55
|
// so a later session can prove that holder exited (see holderProbe).
|
|
56
56
|
Host string `json:"host,omitempty"`
|
|
57
57
|
Anchors []claimProc `json:"anchors,omitempty"`
|
|
58
|
+
// Holder is the anchor that is the claiming agent session itself; its
|
|
59
|
+
// exit releases the claim even while the shell that launched it lives.
|
|
60
|
+
Holder *claimProc `json:"holder,omitempty"`
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
// RunClaim manages the advisory file-claim ledger.
|
|
@@ -318,6 +321,7 @@ func claimAddLocked(path, session, reason string, ttl int, paths []string, stdou
|
|
|
318
321
|
ExpiresAt: now.Add(time.Duration(ttl) * time.Minute).Format(time.RFC3339),
|
|
319
322
|
Host: probe.host,
|
|
320
323
|
Anchors: probe.anchors(now),
|
|
324
|
+
Holder: probe.holder(now),
|
|
321
325
|
}
|
|
322
326
|
if err := appendClaim(path, rec); err != nil {
|
|
323
327
|
fmt.Fprintf(stderr, "claim: %v\n", err)
|
|
@@ -3,6 +3,7 @@ package lib
|
|
|
3
3
|
import (
|
|
4
4
|
"os"
|
|
5
5
|
"os/exec"
|
|
6
|
+
"path/filepath"
|
|
6
7
|
"slices"
|
|
7
8
|
"strconv"
|
|
8
9
|
"strings"
|
|
@@ -23,6 +24,14 @@ type claimProc struct {
|
|
|
23
24
|
type procInfo struct {
|
|
24
25
|
ppid int
|
|
25
26
|
start string
|
|
27
|
+
comm string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// shells outlive the agent sessions they launch (a terminal pane, a
|
|
31
|
+
// multiplexer's login shell), so a shell never stands for the holder.
|
|
32
|
+
var shells = map[string]bool{
|
|
33
|
+
"sh": true, "bash": true, "zsh": true, "fish": true, "dash": true, "ksh": true,
|
|
34
|
+
"tcsh": true, "csh": true, "nu": true, "pwsh": true, "powershell": true, "cmd": true,
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
// processTable lists running processes keyed by pid, or nil when the host
|
|
@@ -30,7 +39,7 @@ type procInfo struct {
|
|
|
30
39
|
var processTable = psProcessTable
|
|
31
40
|
|
|
32
41
|
func psProcessTable() map[int]procInfo {
|
|
33
|
-
cmd := exec.Command("ps", "-A", "-o", "pid=,ppid=,lstart=")
|
|
42
|
+
cmd := exec.Command("ps", "-A", "-o", "pid=,ppid=,lstart=,comm=")
|
|
34
43
|
cmd.Env = append(os.Environ(), "LC_ALL=C")
|
|
35
44
|
out, err := cmd.Output()
|
|
36
45
|
if err != nil {
|
|
@@ -39,16 +48,16 @@ func psProcessTable() map[int]procInfo {
|
|
|
39
48
|
table := map[int]procInfo{}
|
|
40
49
|
for _, line := range strings.Split(string(out), "\n") {
|
|
41
50
|
f := strings.Fields(line)
|
|
42
|
-
if len(f)
|
|
51
|
+
if len(f) < 8 {
|
|
43
52
|
continue
|
|
44
53
|
}
|
|
45
54
|
pid, perr := strconv.Atoi(f[0])
|
|
46
55
|
ppid, pperr := strconv.Atoi(f[1])
|
|
47
|
-
start, serr := time.ParseInLocation("Mon Jan 2 15:04:05 2006", strings.Join(f[2:], " "), time.Local)
|
|
56
|
+
start, serr := time.ParseInLocation("Mon Jan 2 15:04:05 2006", strings.Join(f[2:7], " "), time.Local)
|
|
48
57
|
if perr != nil || pperr != nil || serr != nil {
|
|
49
58
|
continue
|
|
50
59
|
}
|
|
51
|
-
table[pid] = procInfo{ppid: ppid, start: start.UTC().Format(time.RFC3339)}
|
|
60
|
+
table[pid] = procInfo{ppid: ppid, start: start.UTC().Format(time.RFC3339), comm: strings.Join(f[7:], " ")}
|
|
52
61
|
}
|
|
53
62
|
if len(table) == 0 {
|
|
54
63
|
return nil
|
|
@@ -99,12 +108,33 @@ func (p holderProbe) anchors(now time.Time) []claimProc {
|
|
|
99
108
|
return out
|
|
100
109
|
}
|
|
101
110
|
|
|
111
|
+
// holder returns the nearest anchor that is not a shell: the agent session
|
|
112
|
+
// process that owns the claim. A shell or terminal multiplexer above it can
|
|
113
|
+
// outlive the session, so only the session's own exit proves release.
|
|
114
|
+
func (p holderProbe) holder(now time.Time) *claimProc {
|
|
115
|
+
for _, proc := range p.anchors(now) {
|
|
116
|
+
name := strings.TrimPrefix(filepath.Base(p.table[proc.PID].comm), "-")
|
|
117
|
+
if name != "" && !shells[name] {
|
|
118
|
+
return &proc
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return nil
|
|
122
|
+
}
|
|
123
|
+
|
|
102
124
|
// gone reports whether rec's holder is proven exited: the claim was taken on
|
|
103
|
-
// this host and
|
|
125
|
+
// this host and its recorded holder session has exited, or, for a claim with
|
|
126
|
+
// no recorded holder, every anchor it does not share with the caller has.
|
|
104
127
|
func (p holderProbe) gone(rec *claimRecord) bool {
|
|
105
128
|
if p.table == nil || rec.Host == "" || rec.Host != p.host {
|
|
106
129
|
return false
|
|
107
130
|
}
|
|
131
|
+
if h := rec.Holder; h != nil {
|
|
132
|
+
if slices.Contains(p.self, *h) {
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
135
|
+
proc, ok := p.table[h.PID]
|
|
136
|
+
return !ok || proc.start != h.Start
|
|
137
|
+
}
|
|
108
138
|
unique := 0
|
|
109
139
|
for _, a := range rec.Anchors {
|
|
110
140
|
if slices.Contains(p.self, a) {
|
|
@@ -441,6 +441,9 @@ func admitWrightRounds(root, slug string, f *dispatchFile, w *dispatchWave, revi
|
|
|
441
441
|
lines = append(lines, fmt.Sprintf("dispatch: WARNING: %s earlier wave(s) %s never returned; record `dispatch return` or `dispatch abandon --reason`", id, strings.Join(unreturned, ",")))
|
|
442
442
|
}
|
|
443
443
|
}
|
|
444
|
+
if kind == "wright" {
|
|
445
|
+
lines = append(lines, "dispatch: wright contract: name the writer-safe checks the wright runs (syntax/type/lint, unit and self-check suites, a narrow local smoke) and never forbid them; only artifact-producing, consumptive, or full canonical gates stay root-owned")
|
|
446
|
+
}
|
|
444
447
|
if len(overCap) == 0 {
|
|
445
448
|
if review != "" {
|
|
446
449
|
fmt.Fprintf(stderr, "dispatch: --reslice-review given but no slice has reached its %d-round cap\n", wrightRoundsPerReview)
|
|
@@ -27,6 +27,9 @@ Execute [`reference/phase-contract.md`](reference/phase-contract.md)
|
|
|
27
27
|
([`one-slice-cycle.md`](reference/one-slice-cycle.md),
|
|
28
28
|
[`afk-discipline.md`](reference/afk-discipline.md)); dispatch uses
|
|
29
29
|
[`reference/wright-dispatch.md`](reference/wright-dispatch.md).
|
|
30
|
+
Every wright contract names the writer-safe checks the wright runs (syntax/type/lint,
|
|
31
|
+
unit and self-check suites, a narrow local smoke) and never forbids them; only
|
|
32
|
+
artifact-producing, consumptive, or full canonical gates stay root-owned.
|
|
30
33
|
|
|
31
34
|
## Required rules
|
|
32
35
|
|
|
@@ -27,6 +27,9 @@ Execute [`reference/phase-contract.md`](reference/phase-contract.md)
|
|
|
27
27
|
([`one-slice-cycle.md`](reference/one-slice-cycle.md),
|
|
28
28
|
[`afk-discipline.md`](reference/afk-discipline.md)); dispatch uses
|
|
29
29
|
[`reference/wright-dispatch.md`](reference/wright-dispatch.md).
|
|
30
|
+
Every wright contract names the writer-safe checks the wright runs (syntax/type/lint,
|
|
31
|
+
unit and self-check suites, a narrow local smoke) and never forbids them; only
|
|
32
|
+
artifact-producing, consumptive, or full canonical gates stay root-owned.
|
|
30
33
|
|
|
31
34
|
## Required rules
|
|
32
35
|
|
|
@@ -27,6 +27,9 @@ Execute [`reference/phase-contract.md`](reference/phase-contract.md)
|
|
|
27
27
|
([`one-slice-cycle.md`](reference/one-slice-cycle.md),
|
|
28
28
|
[`afk-discipline.md`](reference/afk-discipline.md)); dispatch uses
|
|
29
29
|
[`reference/wright-dispatch.md`](reference/wright-dispatch.md).
|
|
30
|
+
Every wright contract names the writer-safe checks the wright runs (syntax/type/lint,
|
|
31
|
+
unit and self-check suites, a narrow local smoke) and never forbids them; only
|
|
32
|
+
artifact-producing, consumptive, or full canonical gates stay root-owned.
|
|
30
33
|
|
|
31
34
|
## Required rules
|
|
32
35
|
|
|
@@ -26,6 +26,9 @@ Execute [`reference/phase-contract.md`](reference/phase-contract.md)
|
|
|
26
26
|
([`one-slice-cycle.md`](reference/one-slice-cycle.md),
|
|
27
27
|
[`afk-discipline.md`](reference/afk-discipline.md)); dispatch uses
|
|
28
28
|
[`reference/wright-dispatch.md`](reference/wright-dispatch.md).
|
|
29
|
+
Every wright contract names the writer-safe checks the wright runs (syntax/type/lint,
|
|
30
|
+
unit and self-check suites, a narrow local smoke) and never forbids them; only
|
|
31
|
+
artifact-producing, consumptive, or full canonical gates stay root-owned.
|
|
29
32
|
|
|
30
33
|
## Required rules
|
|
31
34
|
|
|
@@ -27,6 +27,9 @@ Execute [`reference/phase-contract.md`](reference/phase-contract.md)
|
|
|
27
27
|
([`one-slice-cycle.md`](reference/one-slice-cycle.md),
|
|
28
28
|
[`afk-discipline.md`](reference/afk-discipline.md)); dispatch uses
|
|
29
29
|
[`reference/wright-dispatch.md`](reference/wright-dispatch.md).
|
|
30
|
+
Every wright contract names the writer-safe checks the wright runs (syntax/type/lint,
|
|
31
|
+
unit and self-check suites, a narrow local smoke) and never forbids them; only
|
|
32
|
+
artifact-producing, consumptive, or full canonical gates stay root-owned.
|
|
30
33
|
|
|
31
34
|
## Required rules
|
|
32
35
|
|
|
@@ -27,6 +27,9 @@ Execute [`reference/phase-contract.md`](reference/phase-contract.md)
|
|
|
27
27
|
([`one-slice-cycle.md`](reference/one-slice-cycle.md),
|
|
28
28
|
[`afk-discipline.md`](reference/afk-discipline.md)); dispatch uses
|
|
29
29
|
[`reference/wright-dispatch.md`](reference/wright-dispatch.md).
|
|
30
|
+
Every wright contract names the writer-safe checks the wright runs (syntax/type/lint,
|
|
31
|
+
unit and self-check suites, a narrow local smoke) and never forbids them; only
|
|
32
|
+
artifact-producing, consumptive, or full canonical gates stay root-owned.
|
|
30
33
|
|
|
31
34
|
## Required rules
|
|
32
35
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devrites",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.4",
|
|
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",
|