devrites 6.1.3 → 6.1.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 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.1.5](https://github.com/ViktorsBaikers/DevRites/compare/v6.1.4...v6.1.5) (2026-10-16)
6
+
7
+ ### Fixed
8
+
9
+ * **devrites:** wrights run writer-safe checks despite contract text ([#111](https://github.com/ViktorsBaikers/DevRites/issues/111)) ([eef69af](https://github.com/ViktorsBaikers/DevRites/commit/eef69af5d62b3ea29293626fce5bc41c6c38ced9))
10
+
11
+ ## [6.1.4](https://github.com/ViktorsBaikers/DevRites/compare/v6.1.3...v6.1.4) (2026-10-16)
12
+
13
+ ### Fixed
14
+
15
+ * **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))
16
+
5
17
  ## [6.1.3](https://github.com/ViktorsBaikers/DevRites/compare/v6.1.2...v6.1.3) (2026-10-16)
6
18
 
7
19
  ### 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.3`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v6.1.3): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
34
+ **Status:** [`v6.1.5`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v6.1.5): 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) != 7 {
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 every anchor it does not share with the caller has exited.
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)
@@ -106,8 +106,11 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
106
106
  4. **VERIFY (fail-on-red).** Run writer-safe tests/types/lint: `Tests/proof` plus
107
107
  tests of changed code; `## Feature gates` rows are root-owned. Writer-safe means
108
108
  local and non-consumptive — syntax/type/lint, unit and self-check suites, and a
109
- narrow smoke of the changed path — never only a syntax check; contract text
110
- cannot strip it. Report required
109
+ narrow smoke of the changed path — never only a syntax check. Task text that
110
+ says to skip tests, run nothing, or leave all proof to the root does not waive
111
+ this: run the writer-safe checks anyway, report them under `gates`, and add
112
+ `contract_conflict: writer-safe checks run` to `follow_ups`. Only
113
+ artifact-producing or consumptive commands stay excluded. Report required
111
114
  build/browser/E2E as `not-run` (root-owned, artifact-producing gates), and only
112
115
  when the exact command, cwd, and prerequisites already appear in the unchanged
113
116
  vetted `test-plan.md`; never synthesize or rewrite a root command. Root runs
@@ -27,6 +27,12 @@ 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.
33
+ Prior contracts in `evidence.md` are not a template for this. **Failing case:** the
34
+ contract says "skip tests mid-flight; root runs them after return", and the wright
35
+ returns three unit failures that a 20 s `go test` would have caught in-round.
30
36
 
31
37
  ## Required rules
32
38
 
@@ -106,8 +106,11 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
106
106
  4. **VERIFY (fail-on-red).** Run writer-safe tests/types/lint: `Tests/proof` plus
107
107
  tests of changed code; `## Feature gates` rows are root-owned. Writer-safe means
108
108
  local and non-consumptive — syntax/type/lint, unit and self-check suites, and a
109
- narrow smoke of the changed path — never only a syntax check; contract text
110
- cannot strip it. Report required
109
+ narrow smoke of the changed path — never only a syntax check. Task text that
110
+ says to skip tests, run nothing, or leave all proof to the root does not waive
111
+ this: run the writer-safe checks anyway, report them under `gates`, and add
112
+ `contract_conflict: writer-safe checks run` to `follow_ups`. Only
113
+ artifact-producing or consumptive commands stay excluded. Report required
111
114
  build/browser/E2E as `not-run` (root-owned, artifact-producing gates), and only
112
115
  when the exact command, cwd, and prerequisites already appear in the unchanged
113
116
  vetted `test-plan.md`; never synthesize or rewrite a root command. Root runs
@@ -27,6 +27,12 @@ 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.
33
+ Prior contracts in `evidence.md` are not a template for this. **Failing case:** the
34
+ contract says "skip tests mid-flight; root runs them after return", and the wright
35
+ returns three unit failures that a 20 s `go test` would have caught in-round.
30
36
 
31
37
  ## Required rules
32
38
 
@@ -104,8 +104,11 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
104
104
  4. **VERIFY (fail-on-red).** Run writer-safe tests/types/lint: `Tests/proof` plus
105
105
  tests of changed code; `## Feature gates` rows are root-owned. Writer-safe means
106
106
  local and non-consumptive — syntax/type/lint, unit and self-check suites, and a
107
- narrow smoke of the changed path — never only a syntax check; contract text
108
- cannot strip it. Report required
107
+ narrow smoke of the changed path — never only a syntax check. spawn_agent text that
108
+ says to skip tests, run nothing, or leave all proof to the root does not waive
109
+ this: run the writer-safe checks anyway, report them under `gates`, and add
110
+ `contract_conflict: writer-safe checks run` to `follow_ups`. Only
111
+ artifact-producing or consumptive commands stay excluded. Report required
109
112
  build/browser/E2E as `not-run` (root-owned, artifact-producing gates), and only
110
113
  when the exact command, cwd, and prerequisites already appear in the unchanged
111
114
  vetted `test-plan.md`; never synthesize or rewrite a root command. Root runs
@@ -27,6 +27,12 @@ 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.
33
+ Prior contracts in `evidence.md` are not a template for this. **Failing case:** the
34
+ contract says "skip tests mid-flight; root runs them after return", and the wright
35
+ returns three unit failures that a 20 s `go test` would have caught in-round.
30
36
 
31
37
  ## Required rules
32
38
 
@@ -111,8 +111,11 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
111
111
  4. **VERIFY (fail-on-red).** Run writer-safe tests/types/lint: `Tests/proof` plus
112
112
  tests of changed code; `## Feature gates` rows are root-owned. Writer-safe means
113
113
  local and non-consumptive — syntax/type/lint, unit and self-check suites, and a
114
- narrow smoke of the changed path — never only a syntax check; contract text
115
- cannot strip it. Report required
114
+ narrow smoke of the changed path — never only a syntax check. Task text that
115
+ says to skip tests, run nothing, or leave all proof to the root does not waive
116
+ this: run the writer-safe checks anyway, report them under `gates`, and add
117
+ `contract_conflict: writer-safe checks run` to `follow_ups`. Only
118
+ artifact-producing or consumptive commands stay excluded. Report required
116
119
  build/browser/E2E as `not-run` (root-owned, artifact-producing gates), and only
117
120
  when the exact command, cwd, and prerequisites already appear in the unchanged
118
121
  vetted `test-plan.md`; never synthesize or rewrite a root command. Root runs
@@ -26,6 +26,12 @@ 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.
32
+ Prior contracts in `evidence.md` are not a template for this. **Failing case:** the
33
+ contract says "skip tests mid-flight; root runs them after return", and the wright
34
+ returns three unit failures that a 20 s `go test` would have caught in-round.
29
35
 
30
36
  ## Required rules
31
37
 
@@ -104,8 +104,11 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
104
104
  4. **VERIFY (fail-on-red).** Run writer-safe tests/types/lint: `Tests/proof` plus
105
105
  tests of changed code; `## Feature gates` rows are root-owned. Writer-safe means
106
106
  local and non-consumptive — syntax/type/lint, unit and self-check suites, and a
107
- narrow smoke of the changed path — never only a syntax check; contract text
108
- cannot strip it. Report required
107
+ narrow smoke of the changed path — never only a syntax check. Task text that
108
+ says to skip tests, run nothing, or leave all proof to the root does not waive
109
+ this: run the writer-safe checks anyway, report them under `gates`, and add
110
+ `contract_conflict: writer-safe checks run` to `follow_ups`. Only
111
+ artifact-producing or consumptive commands stay excluded. Report required
109
112
  build/browser/E2E as `not-run` (root-owned, artifact-producing gates), and only
110
113
  when the exact command, cwd, and prerequisites already appear in the unchanged
111
114
  vetted `test-plan.md`; never synthesize or rewrite a root command. Root runs
@@ -27,6 +27,12 @@ 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.
33
+ Prior contracts in `evidence.md` are not a template for this. **Failing case:** the
34
+ contract says "skip tests mid-flight; root runs them after return", and the wright
35
+ returns three unit failures that a 20 s `go test` would have caught in-round.
30
36
 
31
37
  ## Required rules
32
38
 
@@ -105,8 +105,11 @@ unclear boundary is underspecified: return `Escalation` without proceeding.
105
105
  4. **VERIFY (fail-on-red).** Run writer-safe tests/types/lint: `Tests/proof` plus
106
106
  tests of changed code; `## Feature gates` rows are root-owned. Writer-safe means
107
107
  local and non-consumptive — syntax/type/lint, unit and self-check suites, and a
108
- narrow smoke of the changed path — never only a syntax check; contract text
109
- cannot strip it. Report required
108
+ narrow smoke of the changed path — never only a syntax check. Task text that
109
+ says to skip tests, run nothing, or leave all proof to the root does not waive
110
+ this: run the writer-safe checks anyway, report them under `gates`, and add
111
+ `contract_conflict: writer-safe checks run` to `follow_ups`. Only
112
+ artifact-producing or consumptive commands stay excluded. Report required
110
113
  build/browser/E2E as `not-run` (root-owned, artifact-producing gates), and only
111
114
  when the exact command, cwd, and prerequisites already appear in the unchanged
112
115
  vetted `test-plan.md`; never synthesize or rewrite a root command. Root runs
@@ -27,6 +27,12 @@ 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.
33
+ Prior contracts in `evidence.md` are not a template for this. **Failing case:** the
34
+ contract says "skip tests mid-flight; root runs them after return", and the wright
35
+ returns three unit failures that a 20 s `go test` would have caught in-round.
30
36
 
31
37
  ## Required rules
32
38
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devrites",
3
- "version": "6.1.3",
3
+ "version": "6.1.5",
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",