devrites 6.8.1 → 6.9.1

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 (35) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/docs/engine/commands.md +1 -1
  4. package/engine/help.go +3 -2
  5. package/engine/internal/acceptance/cli.go +32 -6
  6. package/engine/internal/acceptance/run.go +13 -1
  7. package/engine/internal/acceptance/transcript.go +116 -0
  8. package/engine/internal/lib/secretscan.go +6 -15
  9. package/engine/internal/secretpattern/secretpattern.go +37 -0
  10. package/engine/main.go +9 -0
  11. package/pack/.claude/agents/devrites-proof-runner.md +6 -1
  12. package/pack/.claude/skills/devrites-lib/reference/standards/gates.md +6 -1
  13. package/pack/.claude/skills/rite-prove/SKILL.md +7 -4
  14. package/pack/.claude/skills/rite-seal/SKILL.md +3 -1
  15. package/pack/generated/claude/agents/devrites-proof-runner.md +6 -1
  16. package/pack/generated/claude/skills/devrites-lib/reference/standards/gates.md +6 -1
  17. package/pack/generated/claude/skills/rite-prove/SKILL.md +7 -4
  18. package/pack/generated/claude/skills/rite-seal/SKILL.md +3 -1
  19. package/pack/generated/codex/agents/devrites-proof-runner.toml +6 -1
  20. package/pack/generated/codex/skills/devrites-lib/reference/standards/gates.md +6 -1
  21. package/pack/generated/codex/skills/rite-prove/SKILL.md +7 -4
  22. package/pack/generated/codex/skills/rite-seal/SKILL.md +3 -1
  23. package/pack/generated/devin/agents/devrites-proof-runner.md +6 -1
  24. package/pack/generated/devin/skills/devrites-lib/reference/standards/gates.md +6 -1
  25. package/pack/generated/devin/skills/rite-prove/SKILL.md +7 -4
  26. package/pack/generated/devin/skills/rite-seal/SKILL.md +3 -1
  27. package/pack/generated/omp/agents/devrites-proof-runner.md +6 -1
  28. package/pack/generated/omp/skills/devrites-lib/reference/standards/gates.md +6 -1
  29. package/pack/generated/omp/skills/rite-prove/SKILL.md +7 -4
  30. package/pack/generated/omp/skills/rite-seal/SKILL.md +3 -1
  31. package/pack/generated/pi/agents/devrites-proof-runner.md +6 -1
  32. package/pack/generated/pi/skills/devrites-lib/reference/standards/gates.md +6 -1
  33. package/pack/generated/pi/skills/rite-prove/SKILL.md +7 -4
  34. package/pack/generated/pi/skills/rite-seal/SKILL.md +3 -1
  35. package/package.json +1 -1
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.9.1](https://github.com/ViktorsBaikers/DevRites/compare/v6.9.0...v6.9.1) (2026-10-18)
6
+
7
+ ### Fixed
8
+
9
+ * **devrites:** list --transcripts in the main gates help ([#132](https://github.com/ViktorsBaikers/DevRites/issues/132)) ([f9da5f6](https://github.com/ViktorsBaikers/DevRites/commit/f9da5f6b6940b1728925eff92f5f0e58a30d962d)), references [#131](https://github.com/ViktorsBaikers/DevRites/issues/131)
10
+
11
+ ## [6.9.0](https://github.com/ViktorsBaikers/DevRites/compare/v6.8.1...v6.9.0) (2026-10-18)
12
+
13
+ ### Added
14
+
15
+ * **devrites:** write candidate-bound gate transcripts ([#131](https://github.com/ViktorsBaikers/DevRites/issues/131)) ([4e7f2ef](https://github.com/ViktorsBaikers/DevRites/commit/4e7f2ef8393754b53f794f9dfabdd7e69a5bd16c))
16
+
5
17
  ## [6.8.1](https://github.com/ViktorsBaikers/DevRites/compare/v6.8.0...v6.8.1) (2026-10-18)
6
18
 
7
19
  ### Fixed
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.8.1`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v6.8.1): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
34
+ **Status:** [`v6.9.1`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v6.9.1): 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
 
@@ -39,7 +39,7 @@ workflow strategy.
39
39
  | `metrics summary [slug] [--by-slice]` | Reduce `metrics.jsonl` into a per-phase roll-up. `--by-slice` reads `dispatch.json` instead: per slice, build wright rounds, slice-bound Vet rechecks, and agent-minutes (role start to `returned_at`; abandoned waves excluded). |
40
40
  | `claim <add\|release\|list\|check>` | Advisory session-scoped file claims in `.devrites/claims.jsonl` (append-only). `add` records intent to write a path set with a TTL (1–240 min, default 30); a live claim by another session exits `3` and names the holder. `release` appends a release event owned by the claiming session. `list` shows live claims (`--all` includes expired/released); a claim whose holding session has provably exited on this host shows `dead` and no longer conflicts. `check` is the read-only preflight for a path set. Coordination aid only — never a substitute for one-writer-per-worktree. |
41
41
  | `note <add\|list\|check\|rm> <slug> ...` | Anchored workspace notes in `notes.md`: `add <slug> <subject> <quote> <title> [body]` binds a note to a verbatim quote in one repository file and assigns `NOTE-###`; `list` and `check` regrade each anchor `exact`/`moved`/`stale`/`ambiguous`/`lost` by searching the repo for the quote (dependency and workspace trees excluded); `check --repair` rewrites a `moved` subject to its new file; `rm` deletes by ID. `check seal` refuses a malformed `notes.md` and any non-`exact` anchor. |
42
- | `gates <scaffold\|status\|run\|reverify\|lint\|attest\|abandon> <slug>` | Operate the `gates.md` acceptance ledger: seed per-AC gates, execute runnable gates bound to `test-plan.md` preflight rows, attest manual gates, and reduce to `all-met`/`not-met`/`handoff`/`malformed`. A gate with `INPUTS:` records its files' content digest and is reused by `run` and `reverify` while they are unchanged. |
42
+ | `gates <scaffold\|status\|run\|reverify\|lint\|attest\|abandon> <slug>` | Operate the `gates.md` acceptance ledger: seed per-AC gates, execute runnable gates bound to `test-plan.md` preflight rows, attest manual gates, and reduce to `all-met`/`not-met`/`handoff`/`malformed`. A gate with `INPUTS:` records its files' content digest and is reused by `run` and `reverify` while they are unchanged. `run`/`reverify --transcripts` writes a redacted, candidate-bound transcript per executed or reused gate under `references/gates/<gate-id>/`. |
43
43
  | `state resolve <qid> "<answer>"` | Resolve an open question and update `questions.md` plus `state.md` atomically. |
44
44
  | `state merge-manifest <slug> [pred...]` | Fold the recorded predecessor chain's manifests into the release candidate manifest. |
45
45
  | `state close <slug>` | Archive a shipped workspace and clear matching `ACTIVE`. |
package/engine/help.go CHANGED
@@ -95,8 +95,9 @@ const (
95
95
 
96
96
  gates scaffold <slug> Create gates.md from spec.md AC ids (only when absent)
97
97
  gates status <slug> Reduce the ledger without executing; exit 3 unless ALL MET
98
- gates run <slug> [--timeout N] Execute unmet runnable gates and record evidence
99
- gates reverify <slug> [--timeout N] Re-execute every runnable gate, demoting stale passes
98
+ gates run <slug> [--timeout N] [--transcripts] Execute unmet runnable gates and record evidence
99
+ gates reverify <slug> [--timeout N] [--transcripts] Re-execute every runnable gate, demoting stale passes
100
+ (--transcripts: one redacted, candidate-bound file per gate in references/gates/)
100
101
  gates lint <slug> [--strict] Audit oracle quality without executing
101
102
  gates attest <slug> <id> <note> Record human evidence on a manual gate
102
103
  gates abandon <slug> <id> <why> Record a terminal ABANDON handoff on a gate
@@ -26,8 +26,12 @@ const gatesUsage = `usage: devrites-engine gates <subcommand> <slug>
26
26
  Subcommands:
27
27
  scaffold <slug> Create gates.md from spec.md AC ids (only when absent)
28
28
  status <slug> Reduce the ledger without executing; exit 3 unless ALL MET
29
- run <slug> [--timeout N] Execute unmet runnable gates and record evidence
30
- reverify <slug> [--timeout N] Re-execute every runnable gate, demoting stale passes
29
+ run <slug> [--timeout N] [--transcripts]
30
+ Execute unmet runnable gates and record evidence
31
+ reverify <slug> [--timeout N] [--transcripts]
32
+ Re-execute every runnable gate, demoting stale passes
33
+ (--transcripts writes one redacted, candidate-bound
34
+ file per gate under references/gates/<gate-id>/)
31
35
  lint <slug> [--strict] Audit oracle quality without executing
32
36
  attest <slug> <id> <note> Record human evidence on a manual gate
33
37
  abandon <slug> <id> <why> Record a terminal ABANDON handoff on a gate
@@ -216,7 +220,12 @@ func cmdRun(root string, args []string, reverify bool, stdout, stderr io.Writer)
216
220
  var slug string
217
221
  timeout := DefaultTimeout
218
222
  positional := 0
223
+ transcripts := false
219
224
  for i := 0; i < len(args); i++ {
225
+ if args[i] == "--transcripts" {
226
+ transcripts = true
227
+ continue
228
+ }
220
229
  if args[i] == "--timeout" {
221
230
  if i+1 >= len(args) {
222
231
  fmt.Fprintf(stderr, "gates %s: --timeout requires seconds\n", verb)
@@ -232,21 +241,27 @@ func cmdRun(root string, args []string, reverify bool, stdout, stderr io.Writer)
232
241
  continue
233
242
  }
234
243
  if strings.HasPrefix(args[i], "-") || positional > 0 {
235
- fmt.Fprintf(stderr, "usage: devrites-engine gates %s <slug> [--timeout N]\n", verb)
244
+ fmt.Fprintf(stderr, "usage: devrites-engine gates %s <slug> [--timeout N] [--transcripts]\n", verb)
236
245
  return ExitUsage
237
246
  }
238
247
  slug = args[i]
239
248
  positional++
240
249
  }
241
250
  if slug == "" {
242
- fmt.Fprintf(stderr, "usage: devrites-engine gates %s <slug> [--timeout N]\n", verb)
251
+ fmt.Fprintf(stderr, "usage: devrites-engine gates %s <slug> [--timeout N] [--transcripts]\n", verb)
243
252
  return ExitUsage
244
253
  }
245
- _, ledgerPath, testPlanPath, project, err := featurePaths(root, slug)
254
+ work, ledgerPath, testPlanPath, project, err := featurePaths(root, slug)
246
255
  if err != nil {
247
256
  fmt.Fprintf(stderr, "gates %s: %v\n", verb, err)
248
257
  return ExitBlocked
249
258
  }
259
+ before := ""
260
+ if transcripts {
261
+ before = candidateLabel(root, slug)
262
+ }
263
+ var ran []GateResult
264
+ var ranDoc *Document
250
265
 
251
266
  // The gates lock serializes check execution; the feature lock is held only
252
267
  // to read inputs and per writeback, so dispatch and other workspace writers
@@ -270,7 +285,8 @@ func cmdRun(root string, args []string, reverify bool, stdout, stderr io.Writer)
270
285
  }
271
286
  return nil
272
287
  }
273
- for _, result := range RunGates(context.Background(), project, ledgerPath, doc, approved, reverify, timeout, featureLock) {
288
+ ran, ranDoc = RunGates(context.Background(), project, ledgerPath, doc, approved, reverify, timeout, featureLock), doc
289
+ for _, result := range ran {
274
290
  switch {
275
291
  case !result.Ran:
276
292
  fmt.Fprintf(stdout, "gate %s: skipped (%s)\n", result.ID, result.Detail)
@@ -286,6 +302,16 @@ func cmdRun(root string, args []string, reverify bool, stdout, stderr io.Writer)
286
302
  fmt.Fprintf(stderr, "gates %s: %v\n", verb, lockErr)
287
303
  return ExitBlocked
288
304
  }
305
+ if transcripts && ranDoc != nil {
306
+ paths, err := writeTranscripts(work, ranDoc, ran, before, candidateLabel(root, slug))
307
+ for _, path := range paths {
308
+ fmt.Fprintf(stdout, "transcript: %s\n", path)
309
+ }
310
+ if err != nil {
311
+ fmt.Fprintf(stderr, "gates %s: transcript: %v\n", verb, err)
312
+ return ExitBlocked
313
+ }
314
+ }
289
315
  fmt.Fprintln(stdout, "---")
290
316
  return reportStatus(slug, ledgerPath, testPlanPath, stdout, stderr)
291
317
  }
@@ -30,6 +30,12 @@ type GateResult struct {
30
30
  Detail string // exit/match/timeout/approval diagnostic
31
31
  Discarded bool // result dropped: ledger changed under the run
32
32
  WriteErr error // evidence writeback failed for a reason other than drift
33
+ Reused bool // met gate kept because its INPUTS are unchanged
34
+ Inputs string // INPUTS content digest at run time, when declared
35
+ // Outcome, Started and Ended describe the execution this result judged;
36
+ // they are zero when the gate did not execute.
37
+ Outcome RunOutcome
38
+ Started, Ended time.Time
33
39
  }
34
40
 
35
41
  var (
@@ -72,6 +78,7 @@ func ReadLedgerFile(ledgerPath string) (string, error) {
72
78
  func RunGates(ctx context.Context, root, ledgerPath string, doc *Document, approved []ApprovedCommand, reverify bool, timeout time.Duration, writeLock func(func() error) error) []GateResult {
73
79
  var results []GateResult
74
80
  outcomes := map[string]RunOutcome{}
81
+ spans := map[string][2]time.Time{}
75
82
  write := func(gate *Gate, evidence string, checked bool) error {
76
83
  if writeLock == nil {
77
84
  return writeResult(ledgerPath, gate, evidence, checked)
@@ -100,7 +107,7 @@ func RunGates(ctx context.Context, root, ledgerPath string, doc *Document, appro
100
107
  // A gate that declares INPUTS is reused, under run and reverify alike,
101
108
  // exactly while the files it depends on are byte-identical; any change re-executes it.
102
109
  if inputs != "" && inputs == recordedInputs(gate) {
103
- results = append(results, GateResult{ID: gate.ID, Passed: true, Detail: "reused: inputs unchanged"})
110
+ results = append(results, GateResult{ID: gate.ID, Passed: true, Reused: true, Inputs: inputs, Detail: "reused: inputs unchanged"})
104
111
  continue
105
112
  }
106
113
  }
@@ -120,9 +127,12 @@ func RunGates(ctx context.Context, root, ledgerPath string, doc *Document, appro
120
127
  key := gate.Check + "\x00" + cwd
121
128
  outcome, ran := outcomes[key]
122
129
  if !ran {
130
+ started := time.Now().UTC()
123
131
  outcome = Execute(ctx, gate.Check, cwd, timeout)
124
132
  outcomes[key] = outcome
133
+ spans[key] = [2]time.Time{started, time.Now().UTC()}
125
134
  }
135
+ span := spans[key]
126
136
  if outcome.Err == nil && outcome.ExitCode == 0 && expectation.Matches(outcome.Output) {
127
137
  evidence := withInputs(FormatEvidence(gate, OutputDigest(outcome.Output), outcome.OutputBytes, time.Now().UTC().Format(time.RFC3339)), inputs)
128
138
  writeErr := write(gate, evidence, true)
@@ -130,6 +140,7 @@ func RunGates(ctx context.Context, root, ledgerPath string, doc *Document, appro
130
140
  ID: gate.ID, Ran: true, Passed: true,
131
141
  Discarded: errors.Is(writeErr, ErrOracleMoved), WriteErr: writeErr,
132
142
  Detail: resultDetail(writeErr, "pass"),
143
+ Inputs: inputs, Outcome: outcome, Started: span[0], Ended: span[1],
133
144
  })
134
145
  } else {
135
146
  writeErr := write(gate, "pending", false)
@@ -137,6 +148,7 @@ func RunGates(ctx context.Context, root, ledgerPath string, doc *Document, appro
137
148
  ID: gate.ID, Ran: true,
138
149
  Discarded: errors.Is(writeErr, ErrOracleMoved), WriteErr: writeErr,
139
150
  Detail: resultDetail(writeErr, failDetail(outcome, expectation)),
151
+ Inputs: inputs, Outcome: outcome, Started: span[0], Ended: span[1],
140
152
  })
141
153
  }
142
154
  }
@@ -0,0 +1,116 @@
1
+ package acceptance
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+ "path/filepath"
7
+ "regexp"
8
+ "strings"
9
+ "time"
10
+
11
+ "github.com/devrites/devrites/internal/secretpattern"
12
+ )
13
+
14
+ // CandidateDigest computes the feature's candidate identity. main wires it to
15
+ // the candidate hash in package lib, which imports this package; nil reports
16
+ // the identity as unavailable.
17
+ var CandidateDigest func(root, slug string) (string, error)
18
+
19
+ // maxTranscriptOutput caps the redacted output a transcript keeps.
20
+ const maxTranscriptOutput = 1 << 20
21
+
22
+ var unsafeNameRE = regexp.MustCompile(`[^A-Za-z0-9._-]`)
23
+
24
+ func candidateLabel(root, slug string) string {
25
+ if CandidateDigest == nil {
26
+ return "unavailable: no candidate identity"
27
+ }
28
+ digest, err := CandidateDigest(root, slug)
29
+ if err != nil {
30
+ return "unavailable: " + oneLine(err.Error())
31
+ }
32
+ return digest
33
+ }
34
+
35
+ // writeTranscripts records one immutable file per executed or reused gate under
36
+ // <work>/references/gates/<gate-id>/, binding the exact command, cwd, attempt,
37
+ // exit, EXPECT verdict and redacted output to the candidate identity read
38
+ // before and after the run. OUTPUT_SHA256 equals the ledger evidence digest.
39
+ func writeTranscripts(work string, doc *Document, results []GateResult, before, after string) ([]string, error) {
40
+ gates := map[string]*Gate{}
41
+ for _, gate := range doc.Gates {
42
+ gates[gate.ID] = gate
43
+ }
44
+ prefix := "nodigest"
45
+ if len(before) >= 12 && !strings.HasPrefix(before, "unavailable") {
46
+ prefix = before[:12]
47
+ }
48
+ var paths []string
49
+ for _, result := range results {
50
+ gate := gates[result.ID]
51
+ if gate == nil || (!result.Ran && !result.Reused) {
52
+ continue
53
+ }
54
+ name := unsafeNameRE.ReplaceAllString(gate.ID, "_")
55
+ if name == "." || name == ".." {
56
+ name = "_"
57
+ }
58
+ dir := filepath.Join(work, "references", "gates", name)
59
+ if err := os.MkdirAll(dir, 0o755); err != nil {
60
+ return paths, err
61
+ }
62
+ var body strings.Builder
63
+ fmt.Fprintf(&body, "DEVRITES-GATE-TRANSCRIPT v1\nGATE: %s\nTITLE: %s\nCOMMAND: %s\nCWD: %s\n", gate.ID, gate.Title, gate.Check, valueOr(gate.Cwd, "."))
64
+ fmt.Fprintf(&body, "INPUTS: %s\nINPUTS_SHA256: %s\nCANDIDATE_BEFORE: %s\nCANDIDATE_AFTER: %s\n", valueOr(gate.Inputs, "none"), valueOr(result.Inputs, "none"), before, after)
65
+ var file string
66
+ if result.Reused {
67
+ file = filepath.Join(dir, prefix+"-reused.log")
68
+ fmt.Fprintf(&body, "RESULT: reused (inputs unchanged; not executed)\nPRIOR_EVIDENCE: %s\n", gate.Evidence)
69
+ } else {
70
+ attempt := 1
71
+ if entries, err := os.ReadDir(dir); err == nil {
72
+ for _, entry := range entries {
73
+ if strings.HasPrefix(entry.Name(), prefix+"-a") {
74
+ attempt++
75
+ }
76
+ }
77
+ }
78
+ file = filepath.Join(dir, fmt.Sprintf("%s-a%d.log", prefix, attempt))
79
+ output, masked := secretpattern.Redact(result.Outcome.Output)
80
+ truncated := len(output) > maxTranscriptOutput
81
+ if truncated {
82
+ output = output[:maxTranscriptOutput]
83
+ }
84
+ verdict := "fail"
85
+ if result.Passed {
86
+ verdict = "pass"
87
+ }
88
+ fmt.Fprintf(&body, "ATTEMPT: %d\nSTARTED: %s\nENDED: %s\nEXIT: %s\nEXPECT: %s\nRESULT: %s\n",
89
+ attempt, result.Started.Format(time.RFC3339Nano), result.Ended.Format(time.RFC3339Nano), exitLabel(result.Outcome), gate.Expect, verdict)
90
+ fmt.Fprintf(&body, "OUTPUT_SHA256: %s\nOUTPUT_BYTES: %d\nREDACTIONS: %d\nTRUNCATED: %t\n--- OUTPUT ---\n%s\n--- END OUTPUT ---\n",
91
+ OutputDigest(result.Outcome.Output), result.Outcome.OutputBytes, masked, truncated, output)
92
+ }
93
+ if err := os.WriteFile(file, []byte(body.String()), 0o644); err != nil {
94
+ return paths, err
95
+ }
96
+ paths = append(paths, file)
97
+ }
98
+ return paths, nil
99
+ }
100
+
101
+ func exitLabel(outcome RunOutcome) string {
102
+ switch {
103
+ case outcome.TimedOut:
104
+ return "timeout"
105
+ case outcome.Err != nil:
106
+ return "error: " + oneLine(outcome.Err.Error())
107
+ }
108
+ return fmt.Sprint(outcome.ExitCode)
109
+ }
110
+
111
+ func valueOr(value, fallback string) string {
112
+ if value == "" {
113
+ return fallback
114
+ }
115
+ return value
116
+ }
@@ -8,10 +8,11 @@ import (
8
8
  "io"
9
9
  "os"
10
10
  "path/filepath"
11
- "regexp"
12
11
  "sort"
13
12
  "strconv"
14
13
  "strings"
14
+
15
+ "github.com/devrites/devrites/internal/secretpattern"
15
16
  )
16
17
 
17
18
  type secretFinding struct {
@@ -71,17 +72,7 @@ func (w *boundedSecretScanBuffer) Len() int {
71
72
  return w.buffer.Len()
72
73
  }
73
74
 
74
- var secretPatterns = []struct {
75
- sev, kind string
76
- re *regexp.Regexp
77
- }{
78
- {"HIGH", "private-key", regexp.MustCompile(`-----BEGIN (RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----`)},
79
- {"HIGH", "aws-secret", regexp.MustCompile(`(?i)aws(.{0,20})?(secret|access).{0,20}[=:]\s*['\"]?[A-Za-z0-9/+=]{30,}`)},
80
- {"HIGH", "github-token", regexp.MustCompile(`gh[pousr]_[A-Za-z0-9_]{30,}`)},
81
- {"HIGH", "slack-token", regexp.MustCompile(`xox[baprs]-[A-Za-z0-9-]{20,}`)},
82
- {"MEDIUM", "generic-secret", regexp.MustCompile(`(?i)(api[_-]?key|token|secret|password)\s*[:=]\s*['\"][^'\"\n]{16,}['\"]`)},
83
- {"LOW", "placeholder-secret", regexp.MustCompile(`(?i)(api[_-]?key|token|secret|password).{0,20}(example|changeme|placeholder|dummy)`)},
84
- }
75
+ var secretPatterns = secretpattern.Patterns
85
76
 
86
77
  // SecretScan scans staged blobs, stdin, or touched files for credentials. HIGH exits 3.
87
78
  func SecretScan(root string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
@@ -253,12 +244,12 @@ func scanCountedSecrets(path string, content []byte, budget *secretScanBudget) (
253
244
  var out []secretFinding
254
245
  source := secretSourceLabel(path)
255
246
  for _, pattern := range secretPatterns {
256
- matches := pattern.re.FindAllIndex(content, budget.findingsRemaining+1)
247
+ matches := pattern.Re.FindAllIndex(content, budget.findingsRemaining+1)
257
248
  if len(matches) > budget.findingsRemaining {
258
249
  return nil, errSecretScanLimit
259
250
  }
260
251
  for _, match := range matches {
261
- out = append(out, secretFinding{Severity: pattern.sev, Path: source, Kind: pattern.kind, Offset: match[0]})
252
+ out = append(out, secretFinding{Severity: pattern.Sev, Path: source, Kind: pattern.Kind, Offset: match[0]})
262
253
  }
263
254
  budget.findingsRemaining -= len(matches)
264
255
  }
@@ -267,7 +258,7 @@ func scanCountedSecrets(path string, content []byte, budget *secretScanBudget) (
267
258
 
268
259
  func secretSourceLabel(path string) string {
269
260
  for _, pattern := range secretPatterns {
270
- if pattern.re.FindStringIndex(path) != nil {
261
+ if pattern.Re.FindStringIndex(path) != nil {
271
262
  return "<redacted-path>"
272
263
  }
273
264
  }
@@ -0,0 +1,37 @@
1
+ // Package secretpattern holds the credential patterns shared by the secret
2
+ // scanner and the gate transcript writer, so both mask the same shapes.
3
+ package secretpattern
4
+
5
+ import "regexp"
6
+
7
+ // Pattern is one credential shape with its scanner severity.
8
+ type Pattern struct {
9
+ Sev, Kind string
10
+ Re *regexp.Regexp
11
+ }
12
+
13
+ // Patterns are the credential shapes, most severe first.
14
+ var Patterns = []Pattern{
15
+ {"HIGH", "private-key", regexp.MustCompile(`-----BEGIN (RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----`)},
16
+ {"HIGH", "aws-secret", regexp.MustCompile(`(?i)aws(.{0,20})?(secret|access).{0,20}[=:]\s*['\"]?[A-Za-z0-9/+=]{30,}`)},
17
+ {"HIGH", "github-token", regexp.MustCompile(`gh[pousr]_[A-Za-z0-9_]{30,}`)},
18
+ {"HIGH", "slack-token", regexp.MustCompile(`xox[baprs]-[A-Za-z0-9-]{20,}`)},
19
+ {"MEDIUM", "generic-secret", regexp.MustCompile(`(?i)(api[_-]?key|token|secret|password)\s*[:=]\s*['\"][^'\"\n]{16,}['\"]`)},
20
+ {"LOW", "placeholder-secret", regexp.MustCompile(`(?i)(api[_-]?key|token|secret|password).{0,20}(example|changeme|placeholder|dummy)`)},
21
+ }
22
+
23
+ // Redact masks every HIGH or MEDIUM match in text and returns the masked text
24
+ // and the number of masks. LOW placeholders are not credentials and stay.
25
+ func Redact(text string) (string, int) {
26
+ masked := 0
27
+ for _, pattern := range Patterns {
28
+ if pattern.Sev == "LOW" {
29
+ continue
30
+ }
31
+ text = pattern.Re.ReplaceAllStringFunc(text, func(string) string {
32
+ masked++
33
+ return "[REDACTED:" + pattern.Kind + "]"
34
+ })
35
+ }
36
+ return text, masked
37
+ }
package/engine/main.go CHANGED
@@ -99,6 +99,15 @@ func main() {
99
99
  os.Exit(run(os.Args[1:], os.Stdin, os.Stdout, os.Stderr))
100
100
  }
101
101
 
102
+ func init() {
103
+ // acceptance cannot import lib (lib imports it), so gate transcripts get
104
+ // the candidate identity through this hook.
105
+ acceptance.CandidateDigest = func(root, slug string) (string, error) {
106
+ digest, _, err := lib.CandidateIdentity(root, slug)
107
+ return digest, err
108
+ }
109
+ }
110
+
102
111
  // run is the testable entry point. It returns the process exit code.
103
112
  func run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
104
113
  if len(args) == 0 {
@@ -49,7 +49,12 @@ and only the orchestrator-supplied changed paths.
49
49
  named blocker, never a pass. Otherwise use `cannot_verify`.
50
50
  8. A manual gate whose `gates.md` note lacks the human's own words or source, is a
51
51
  placeholder, or was runnable at Vet is `cannot_verify` ([`gates.md`](../skills/devrites-lib/reference/standards/gates.md)).
52
- 9. Recheck supplied before/after candidate identities. A mismatch or unexpected
52
+ 9. An engine transcript (`DEVRITES-GATE-TRANSCRIPT v1` under `references/gates/`)
53
+ is immutable proof of its COMMAND, CWD, ATTEMPT, EXIT and output for the
54
+ candidate in CANDIDATE_BEFORE/AFTER. A `-reused.log` proves a gate for the
55
+ current candidate only when its INPUTS_SHA256 equals that of the gate's latest
56
+ executed, passing transcript.
57
+ 10. Recheck supplied before/after candidate identities. A mismatch or unexpected
53
58
  repository mutation fails the side-effect boundary.
54
59
 
55
60
  ## Rules
@@ -55,7 +55,12 @@ execute unreviewed code.
55
55
 
56
56
  Execution contract: the command runs through the platform shell in the named
57
57
  directory with a bounded timeout; pass requires exit 0 **and** output matching
58
- EXPECT. Output is captured but never persisted — only its digest.
58
+ EXPECT. Without `--transcripts`, output is captured but never persisted, only its
59
+ digest. `--transcripts` writes one file per executed or reused gate under
60
+ `references/gates/<gate-id>/`. Each file holds the command, cwd, attempt, times,
61
+ exit, EXPECT verdict, the candidate digest read before and after the run, and the
62
+ output with known credential shapes masked and capped at 1 MiB. Its
63
+ `OUTPUT_SHA256` equals the ledger evidence digest.
59
64
 
60
65
  After Vet READY the runnable set is fixed: converting a runnable gate to manual,
61
66
  loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
@@ -84,10 +84,13 @@ test changes.
84
84
  pass is `flaky` per [`testing.md`](../devrites-lib/reference/standards/testing.md#determinism-no-flaky-tests),
85
85
  never green. Static gates prove only their named
86
86
  static criterion.
87
- Run the acceptance ledger: `devrites-engine gates run <slug>` executes every
88
- unmet runnable gate against the same approved preflight rows and records
89
- definition-bound evidence; `gates reverify <slug>` re-executes all of them
90
- when any prior pass is in doubt. Attest manual gates with `gates attest
87
+ Run the acceptance ledger with `--transcripts`: `devrites-engine gates run <slug>
88
+ --transcripts` executes every unmet runnable gate against the same approved
89
+ preflight rows, records definition-bound evidence, and writes one redacted,
90
+ candidate-bound transcript per gate under `references/gates/<gate-id>/`;
91
+ `gates reverify <slug> --transcripts` re-executes all of them when any prior
92
+ pass is in doubt. Hand the proof runner those transcripts, never hand-built
93
+ logs. Attest manual gates with `gates attest
91
94
  <slug> <id> <note>` only against observed judgment whose note cites the
92
95
  human's own words or source; placeholder notes are unmet and an AFK run never
93
96
  self-attests. Only gates manual at Vet are attested; runnable-to-manual
@@ -82,7 +82,9 @@ Follow [`reference/phase-contract.md`](reference/phase-contract.md)
82
82
  that is malformed, unmet,
83
83
  stale, unapproved, or abandoned — a seal-side `gates status` reading anything
84
84
  but `all-met` means Prove's evidence is not current; return there, never
85
- attest past it. It likewise refuses a malformed `notes.md` or any anchor
85
+ attest past it. Before the Seal wave, run `devrites-engine gates reverify <slug>
86
+ --transcripts` on the frozen candidate, so every gate has a transcript bound to
87
+ this digest for the proof runner. It likewise refuses a malformed `notes.md` or any anchor
86
88
  graded other than `exact`: `devrites-engine note check <slug> --repair`
87
89
  re-anchors `moved` notes; `stale`/`ambiguous`/`lost` ones are corrected or
88
90
  removed before GO.
@@ -55,7 +55,12 @@ and only the orchestrator-supplied changed paths.
55
55
  named blocker, never a pass. Otherwise use `cannot_verify`.
56
56
  8. A manual gate whose `gates.md` note lacks the human's own words or source, is a
57
57
  placeholder, or was runnable at Vet is `cannot_verify` ([`gates.md`](../skills/devrites-lib/reference/standards/gates.md)).
58
- 9. Recheck supplied before/after candidate identities. A mismatch or unexpected
58
+ 9. An engine transcript (`DEVRITES-GATE-TRANSCRIPT v1` under `references/gates/`)
59
+ is immutable proof of its COMMAND, CWD, ATTEMPT, EXIT and output for the
60
+ candidate in CANDIDATE_BEFORE/AFTER. A `-reused.log` proves a gate for the
61
+ current candidate only when its INPUTS_SHA256 equals that of the gate's latest
62
+ executed, passing transcript.
63
+ 10. Recheck supplied before/after candidate identities. A mismatch or unexpected
59
64
  repository mutation fails the side-effect boundary.
60
65
 
61
66
  ## Rules
@@ -55,7 +55,12 @@ execute unreviewed code.
55
55
 
56
56
  Execution contract: the command runs through the platform shell in the named
57
57
  directory with a bounded timeout; pass requires exit 0 **and** output matching
58
- EXPECT. Output is captured but never persisted — only its digest.
58
+ EXPECT. Without `--transcripts`, output is captured but never persisted, only its
59
+ digest. `--transcripts` writes one file per executed or reused gate under
60
+ `references/gates/<gate-id>/`. Each file holds the command, cwd, attempt, times,
61
+ exit, EXPECT verdict, the candidate digest read before and after the run, and the
62
+ output with known credential shapes masked and capped at 1 MiB. Its
63
+ `OUTPUT_SHA256` equals the ledger evidence digest.
59
64
 
60
65
  After Vet READY the runnable set is fixed: converting a runnable gate to manual,
61
66
  loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
@@ -84,10 +84,13 @@ test changes.
84
84
  pass is `flaky` per [`testing.md`](../devrites-lib/reference/standards/testing.md#determinism-no-flaky-tests),
85
85
  never green. Static gates prove only their named
86
86
  static criterion.
87
- Run the acceptance ledger: `devrites-engine gates run <slug>` executes every
88
- unmet runnable gate against the same approved preflight rows and records
89
- definition-bound evidence; `gates reverify <slug>` re-executes all of them
90
- when any prior pass is in doubt. Attest manual gates with `gates attest
87
+ Run the acceptance ledger with `--transcripts`: `devrites-engine gates run <slug>
88
+ --transcripts` executes every unmet runnable gate against the same approved
89
+ preflight rows, records definition-bound evidence, and writes one redacted,
90
+ candidate-bound transcript per gate under `references/gates/<gate-id>/`;
91
+ `gates reverify <slug> --transcripts` re-executes all of them when any prior
92
+ pass is in doubt. Hand the proof runner those transcripts, never hand-built
93
+ logs. Attest manual gates with `gates attest
91
94
  <slug> <id> <note>` only against observed judgment whose note cites the
92
95
  human's own words or source; placeholder notes are unmet and an AFK run never
93
96
  self-attests. Only gates manual at Vet are attested; runnable-to-manual
@@ -82,7 +82,9 @@ Follow [`reference/phase-contract.md`](reference/phase-contract.md)
82
82
  that is malformed, unmet,
83
83
  stale, unapproved, or abandoned — a seal-side `gates status` reading anything
84
84
  but `all-met` means Prove's evidence is not current; return there, never
85
- attest past it. It likewise refuses a malformed `notes.md` or any anchor
85
+ attest past it. Before the Seal wave, run `devrites-engine gates reverify <slug>
86
+ --transcripts` on the frozen candidate, so every gate has a transcript bound to
87
+ this digest for the proof runner. It likewise refuses a malformed `notes.md` or any anchor
86
88
  graded other than `exact`: `devrites-engine note check <slug> --repair`
87
89
  re-anchors `moved` notes; `stale`/`ambiguous`/`lost` ones are corrected or
88
90
  removed before GO.
@@ -53,7 +53,12 @@ and only the orchestrator-supplied changed paths.
53
53
  named blocker, never a pass. Otherwise use `cannot_verify`.
54
54
  8. A manual gate whose `gates.md` note lacks the human's own words or source, is a
55
55
  placeholder, or was runnable at Vet is `cannot_verify` ([`gates.md`](.agents/skills/devrites-lib/reference/standards/gates.md)).
56
- 9. Recheck supplied before/after candidate identities. A mismatch or unexpected
56
+ 9. An engine transcript (`DEVRITES-GATE-TRANSCRIPT v1` under `references/gates/`)
57
+ is immutable proof of its COMMAND, CWD, ATTEMPT, EXIT and output for the
58
+ candidate in CANDIDATE_BEFORE/AFTER. A `-reused.log` proves a gate for the
59
+ current candidate only when its INPUTS_SHA256 equals that of the gate's latest
60
+ executed, passing transcript.
61
+ 10. Recheck supplied before/after candidate identities. A mismatch or unexpected
57
62
  repository mutation fails the side-effect boundary.
58
63
 
59
64
  ## Rules
@@ -55,7 +55,12 @@ execute unreviewed code.
55
55
 
56
56
  Execution contract: the command runs through the platform shell in the named
57
57
  directory with a bounded timeout; pass requires exit 0 **and** output matching
58
- EXPECT. Output is captured but never persisted — only its digest.
58
+ EXPECT. Without `--transcripts`, output is captured but never persisted, only its
59
+ digest. `--transcripts` writes one file per executed or reused gate under
60
+ `references/gates/<gate-id>/`. Each file holds the command, cwd, attempt, times,
61
+ exit, EXPECT verdict, the candidate digest read before and after the run, and the
62
+ output with known credential shapes masked and capped at 1 MiB. Its
63
+ `OUTPUT_SHA256` equals the ledger evidence digest.
59
64
 
60
65
  After Vet READY the runnable set is fixed: converting a runnable gate to manual,
61
66
  loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
@@ -84,10 +84,13 @@ test changes.
84
84
  pass is `flaky` per [`testing.md`](../devrites-lib/reference/standards/testing.md#determinism-no-flaky-tests),
85
85
  never green. Static gates prove only their named
86
86
  static criterion.
87
- Run the acceptance ledger: `devrites-engine gates run <slug>` executes every
88
- unmet runnable gate against the same approved preflight rows and records
89
- definition-bound evidence; `gates reverify <slug>` re-executes all of them
90
- when any prior pass is in doubt. Attest manual gates with `gates attest
87
+ Run the acceptance ledger with `--transcripts`: `devrites-engine gates run <slug>
88
+ --transcripts` executes every unmet runnable gate against the same approved
89
+ preflight rows, records definition-bound evidence, and writes one redacted,
90
+ candidate-bound transcript per gate under `references/gates/<gate-id>/`;
91
+ `gates reverify <slug> --transcripts` re-executes all of them when any prior
92
+ pass is in doubt. Hand the proof runner those transcripts, never hand-built
93
+ logs. Attest manual gates with `gates attest
91
94
  <slug> <id> <note>` only against observed judgment whose note cites the
92
95
  human's own words or source; placeholder notes are unmet and an AFK run never
93
96
  self-attests. Only gates manual at Vet are attested; runnable-to-manual
@@ -82,7 +82,9 @@ Follow [`reference/phase-contract.md`](reference/phase-contract.md)
82
82
  that is malformed, unmet,
83
83
  stale, unapproved, or abandoned — a seal-side `gates status` reading anything
84
84
  but `all-met` means Prove's evidence is not current; return there, never
85
- attest past it. It likewise refuses a malformed `notes.md` or any anchor
85
+ attest past it. Before the Seal wave, run `devrites-engine gates reverify <slug>
86
+ --transcripts` on the frozen candidate, so every gate has a transcript bound to
87
+ this digest for the proof runner. It likewise refuses a malformed `notes.md` or any anchor
86
88
  graded other than `exact`: `devrites-engine note check <slug> --repair`
87
89
  re-anchors `moved` notes; `stale`/`ambiguous`/`lost` ones are corrected or
88
90
  removed before GO.
@@ -57,7 +57,12 @@ and only the orchestrator-supplied changed paths.
57
57
  named blocker, never a pass. Otherwise use `cannot_verify`.
58
58
  8. A manual gate whose `gates.md` note lacks the human's own words or source, is a
59
59
  placeholder, or was runnable at Vet is `cannot_verify` ([`gates.md`](../skills/devrites-lib/reference/standards/gates.md)).
60
- 9. Recheck supplied before/after candidate identities. A mismatch or unexpected
60
+ 9. An engine transcript (`DEVRITES-GATE-TRANSCRIPT v1` under `references/gates/`)
61
+ is immutable proof of its COMMAND, CWD, ATTEMPT, EXIT and output for the
62
+ candidate in CANDIDATE_BEFORE/AFTER. A `-reused.log` proves a gate for the
63
+ current candidate only when its INPUTS_SHA256 equals that of the gate's latest
64
+ executed, passing transcript.
65
+ 10. Recheck supplied before/after candidate identities. A mismatch or unexpected
61
66
  repository mutation fails the side-effect boundary.
62
67
 
63
68
  ## Rules
@@ -55,7 +55,12 @@ execute unreviewed code.
55
55
 
56
56
  Execution contract: the command runs through the platform shell in the named
57
57
  directory with a bounded timeout; pass requires exit 0 **and** output matching
58
- EXPECT. Output is captured but never persisted — only its digest.
58
+ EXPECT. Without `--transcripts`, output is captured but never persisted, only its
59
+ digest. `--transcripts` writes one file per executed or reused gate under
60
+ `references/gates/<gate-id>/`. Each file holds the command, cwd, attempt, times,
61
+ exit, EXPECT verdict, the candidate digest read before and after the run, and the
62
+ output with known credential shapes masked and capped at 1 MiB. Its
63
+ `OUTPUT_SHA256` equals the ledger evidence digest.
59
64
 
60
65
  After Vet READY the runnable set is fixed: converting a runnable gate to manual,
61
66
  loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
@@ -83,10 +83,13 @@ test changes.
83
83
  pass is `flaky` per [`testing.md`](../devrites-lib/reference/standards/testing.md#determinism-no-flaky-tests),
84
84
  never green. Static gates prove only their named
85
85
  static criterion.
86
- Run the acceptance ledger: `devrites-engine gates run <slug>` executes every
87
- unmet runnable gate against the same approved preflight rows and records
88
- definition-bound evidence; `gates reverify <slug>` re-executes all of them
89
- when any prior pass is in doubt. Attest manual gates with `gates attest
86
+ Run the acceptance ledger with `--transcripts`: `devrites-engine gates run <slug>
87
+ --transcripts` executes every unmet runnable gate against the same approved
88
+ preflight rows, records definition-bound evidence, and writes one redacted,
89
+ candidate-bound transcript per gate under `references/gates/<gate-id>/`;
90
+ `gates reverify <slug> --transcripts` re-executes all of them when any prior
91
+ pass is in doubt. Hand the proof runner those transcripts, never hand-built
92
+ logs. Attest manual gates with `gates attest
90
93
  <slug> <id> <note>` only against observed judgment whose note cites the
91
94
  human's own words or source; placeholder notes are unmet and an AFK run never
92
95
  self-attests. Only gates manual at Vet are attested; runnable-to-manual
@@ -81,7 +81,9 @@ Follow [`reference/phase-contract.md`](reference/phase-contract.md)
81
81
  that is malformed, unmet,
82
82
  stale, unapproved, or abandoned — a seal-side `gates status` reading anything
83
83
  but `all-met` means Prove's evidence is not current; return there, never
84
- attest past it. It likewise refuses a malformed `notes.md` or any anchor
84
+ attest past it. Before the Seal wave, run `devrites-engine gates reverify <slug>
85
+ --transcripts` on the frozen candidate, so every gate has a transcript bound to
86
+ this digest for the proof runner. It likewise refuses a malformed `notes.md` or any anchor
85
87
  graded other than `exact`: `devrites-engine note check <slug> --repair`
86
88
  re-anchors `moved` notes; `stale`/`ambiguous`/`lost` ones are corrected or
87
89
  removed before GO.
@@ -54,7 +54,12 @@ and only the orchestrator-supplied changed paths.
54
54
  named blocker, never a pass. Otherwise use `cannot_verify`.
55
55
  8. A manual gate whose `gates.md` note lacks the human's own words or source, is a
56
56
  placeholder, or was runnable at Vet is `cannot_verify` ([`gates.md`](skill://devrites-lib/reference/standards/gates.md)).
57
- 9. Recheck supplied before/after candidate identities. A mismatch or unexpected
57
+ 9. An engine transcript (`DEVRITES-GATE-TRANSCRIPT v1` under `references/gates/`)
58
+ is immutable proof of its COMMAND, CWD, ATTEMPT, EXIT and output for the
59
+ candidate in CANDIDATE_BEFORE/AFTER. A `-reused.log` proves a gate for the
60
+ current candidate only when its INPUTS_SHA256 equals that of the gate's latest
61
+ executed, passing transcript.
62
+ 10. Recheck supplied before/after candidate identities. A mismatch or unexpected
58
63
  repository mutation fails the side-effect boundary.
59
64
 
60
65
  ## Rules
@@ -55,7 +55,12 @@ execute unreviewed code.
55
55
 
56
56
  Execution contract: the command runs through the platform shell in the named
57
57
  directory with a bounded timeout; pass requires exit 0 **and** output matching
58
- EXPECT. Output is captured but never persisted — only its digest.
58
+ EXPECT. Without `--transcripts`, output is captured but never persisted, only its
59
+ digest. `--transcripts` writes one file per executed or reused gate under
60
+ `references/gates/<gate-id>/`. Each file holds the command, cwd, attempt, times,
61
+ exit, EXPECT verdict, the candidate digest read before and after the run, and the
62
+ output with known credential shapes masked and capped at 1 MiB. Its
63
+ `OUTPUT_SHA256` equals the ledger evidence digest.
59
64
 
60
65
  After Vet READY the runnable set is fixed: converting a runnable gate to manual,
61
66
  loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
@@ -84,10 +84,13 @@ test changes.
84
84
  pass is `flaky` per [`testing.md`](skill://devrites-lib/reference/standards/testing.md#determinism-no-flaky-tests),
85
85
  never green. Static gates prove only their named
86
86
  static criterion.
87
- Run the acceptance ledger: `devrites-engine gates run <slug>` executes every
88
- unmet runnable gate against the same approved preflight rows and records
89
- definition-bound evidence; `gates reverify <slug>` re-executes all of them
90
- when any prior pass is in doubt. Attest manual gates with `gates attest
87
+ Run the acceptance ledger with `--transcripts`: `devrites-engine gates run <slug>
88
+ --transcripts` executes every unmet runnable gate against the same approved
89
+ preflight rows, records definition-bound evidence, and writes one redacted,
90
+ candidate-bound transcript per gate under `references/gates/<gate-id>/`;
91
+ `gates reverify <slug> --transcripts` re-executes all of them when any prior
92
+ pass is in doubt. Hand the proof runner those transcripts, never hand-built
93
+ logs. Attest manual gates with `gates attest
91
94
  <slug> <id> <note>` only against observed judgment whose note cites the
92
95
  human's own words or source; placeholder notes are unmet and an AFK run never
93
96
  self-attests. Only gates manual at Vet are attested; runnable-to-manual
@@ -82,7 +82,9 @@ Follow [`reference/phase-contract.md`](reference/phase-contract.md)
82
82
  that is malformed, unmet,
83
83
  stale, unapproved, or abandoned — a seal-side `gates status` reading anything
84
84
  but `all-met` means Prove's evidence is not current; return there, never
85
- attest past it. It likewise refuses a malformed `notes.md` or any anchor
85
+ attest past it. Before the Seal wave, run `devrites-engine gates reverify <slug>
86
+ --transcripts` on the frozen candidate, so every gate has a transcript bound to
87
+ this digest for the proof runner. It likewise refuses a malformed `notes.md` or any anchor
86
88
  graded other than `exact`: `devrites-engine note check <slug> --repair`
87
89
  re-anchors `moved` notes; `stale`/`ambiguous`/`lost` ones are corrected or
88
90
  removed before GO.
@@ -55,7 +55,12 @@ and only the orchestrator-supplied changed paths.
55
55
  named blocker, never a pass. Otherwise use `cannot_verify`.
56
56
  8. A manual gate whose `gates.md` note lacks the human's own words or source, is a
57
57
  placeholder, or was runnable at Vet is `cannot_verify` ([`gates.md`](../skills/devrites-lib/reference/standards/gates.md)).
58
- 9. Recheck supplied before/after candidate identities. A mismatch or unexpected
58
+ 9. An engine transcript (`DEVRITES-GATE-TRANSCRIPT v1` under `references/gates/`)
59
+ is immutable proof of its COMMAND, CWD, ATTEMPT, EXIT and output for the
60
+ candidate in CANDIDATE_BEFORE/AFTER. A `-reused.log` proves a gate for the
61
+ current candidate only when its INPUTS_SHA256 equals that of the gate's latest
62
+ executed, passing transcript.
63
+ 10. Recheck supplied before/after candidate identities. A mismatch or unexpected
59
64
  repository mutation fails the side-effect boundary.
60
65
 
61
66
  ## Rules
@@ -55,7 +55,12 @@ execute unreviewed code.
55
55
 
56
56
  Execution contract: the command runs through the platform shell in the named
57
57
  directory with a bounded timeout; pass requires exit 0 **and** output matching
58
- EXPECT. Output is captured but never persisted — only its digest.
58
+ EXPECT. Without `--transcripts`, output is captured but never persisted, only its
59
+ digest. `--transcripts` writes one file per executed or reused gate under
60
+ `references/gates/<gate-id>/`. Each file holds the command, cwd, attempt, times,
61
+ exit, EXPECT verdict, the candidate digest read before and after the run, and the
62
+ output with known credential shapes masked and capped at 1 MiB. Its
63
+ `OUTPUT_SHA256` equals the ledger evidence digest.
59
64
 
60
65
  After Vet READY the runnable set is fixed: converting a runnable gate to manual,
61
66
  loosening EXPECT, changing CHECK or CWD, adding or narrowing INPUTS, or removing
@@ -84,10 +84,13 @@ test changes.
84
84
  pass is `flaky` per [`testing.md`](../devrites-lib/reference/standards/testing.md#determinism-no-flaky-tests),
85
85
  never green. Static gates prove only their named
86
86
  static criterion.
87
- Run the acceptance ledger: `devrites-engine gates run <slug>` executes every
88
- unmet runnable gate against the same approved preflight rows and records
89
- definition-bound evidence; `gates reverify <slug>` re-executes all of them
90
- when any prior pass is in doubt. Attest manual gates with `gates attest
87
+ Run the acceptance ledger with `--transcripts`: `devrites-engine gates run <slug>
88
+ --transcripts` executes every unmet runnable gate against the same approved
89
+ preflight rows, records definition-bound evidence, and writes one redacted,
90
+ candidate-bound transcript per gate under `references/gates/<gate-id>/`;
91
+ `gates reverify <slug> --transcripts` re-executes all of them when any prior
92
+ pass is in doubt. Hand the proof runner those transcripts, never hand-built
93
+ logs. Attest manual gates with `gates attest
91
94
  <slug> <id> <note>` only against observed judgment whose note cites the
92
95
  human's own words or source; placeholder notes are unmet and an AFK run never
93
96
  self-attests. Only gates manual at Vet are attested; runnable-to-manual
@@ -82,7 +82,9 @@ Follow [`reference/phase-contract.md`](reference/phase-contract.md)
82
82
  that is malformed, unmet,
83
83
  stale, unapproved, or abandoned — a seal-side `gates status` reading anything
84
84
  but `all-met` means Prove's evidence is not current; return there, never
85
- attest past it. It likewise refuses a malformed `notes.md` or any anchor
85
+ attest past it. Before the Seal wave, run `devrites-engine gates reverify <slug>
86
+ --transcripts` on the frozen candidate, so every gate has a transcript bound to
87
+ this digest for the proof runner. It likewise refuses a malformed `notes.md` or any anchor
86
88
  graded other than `exact`: `devrites-engine note check <slug> --repair`
87
89
  re-anchors `moved` notes; `stale`/`ambiguous`/`lost` ones are corrected or
88
90
  removed before GO.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devrites",
3
- "version": "6.8.1",
3
+ "version": "6.9.1",
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",