@beremaran/ralphie 0.0.0-stage → 0.2.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 (37) hide show
  1. package/CHANGELOG.md +843 -0
  2. package/LICENSE +21 -0
  3. package/README.md +53 -2
  4. package/dist/ralphie.js +33689 -0
  5. package/docs/README.md +71 -0
  6. package/docs/architecture.md +159 -0
  7. package/docs/cli-reference.md +126 -0
  8. package/docs/configuration.md +269 -0
  9. package/docs/development.md +249 -0
  10. package/docs/getting-started.md +134 -0
  11. package/docs/operations-and-recovery.md +328 -0
  12. package/docs/safety.md +189 -0
  13. package/docs/workflows.md +411 -0
  14. package/package.json +83 -3
  15. package/vendor/mattpocock-skills/LICENSE +21 -0
  16. package/vendor/mattpocock-skills/code-review/SKILL.md +87 -0
  17. package/vendor/mattpocock-skills/code-review/agents/openai.yaml +3 -0
  18. package/vendor/mattpocock-skills/codebase-design/DEEPENING.md +37 -0
  19. package/vendor/mattpocock-skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
  20. package/vendor/mattpocock-skills/codebase-design/SKILL.md +114 -0
  21. package/vendor/mattpocock-skills/codebase-design/agents/openai.yaml +3 -0
  22. package/vendor/mattpocock-skills/diagnosing-bugs/SKILL.md +138 -0
  23. package/vendor/mattpocock-skills/diagnosing-bugs/agents/openai.yaml +3 -0
  24. package/vendor/mattpocock-skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
  25. package/vendor/mattpocock-skills/implement/SKILL.md +15 -0
  26. package/vendor/mattpocock-skills/implement/agents/openai.yaml +5 -0
  27. package/vendor/mattpocock-skills/lock.json +37 -0
  28. package/vendor/mattpocock-skills/tdd/SKILL.md +38 -0
  29. package/vendor/mattpocock-skills/tdd/agents/openai.yaml +3 -0
  30. package/vendor/mattpocock-skills/tdd/mocking.md +59 -0
  31. package/vendor/mattpocock-skills/tdd/tests.md +77 -0
  32. package/vendor/mattpocock-skills/to-tickets/SKILL.md +105 -0
  33. package/vendor/mattpocock-skills/to-tickets/agents/openai.yaml +5 -0
  34. package/vendor/mattpocock-skills/triage/AGENT-BRIEF.md +207 -0
  35. package/vendor/mattpocock-skills/triage/OUT-OF-SCOPE.md +105 -0
  36. package/vendor/mattpocock-skills/triage/SKILL.md +112 -0
  37. package/vendor/mattpocock-skills/triage/agents/openai.yaml +5 -0
@@ -0,0 +1,328 @@
1
+ # Operations and recovery
2
+
3
+ This page is for operators inspecting a run, integrating Ralphie's output,
4
+ or understanding what remains after interruption or failure. It is the
5
+ authoritative reference for progress output, artifacts, state, cancellation,
6
+ and cleanup. Start at the [documentation index](README.md) for other audience
7
+ paths.
8
+
9
+ ## Progress output
10
+
11
+ While each session runs, Ralphie translates its harness's native event stream
12
+ into one harness-neutral set of session events: assistant text and thinking,
13
+ tool calls, tool results, errors, and token usage. Every output mode renders
14
+ only these events, so output looks the same whichever harness ran the session.
15
+ Tasks and issues are intentionally processed sequentially so the event stream
16
+ remains ordered. JSON output carries every session event for integrations; see
17
+ [Session events](#session-events).
18
+
19
+ Ralphie adapts its presentation to its environment. `--output default`
20
+ resolves to the full-screen TUI only when stdin and stderr are both TTYs and
21
+ `CI` is neither `"true"` nor `"1"`; otherwise it uses append-only `plain`
22
+ lines.
23
+
24
+ - Interactive terminals get an OpenTUI application (the same rendering core
25
+ OpenCode 1.0 uses): a borderless layout with a background header line
26
+ (repository, pause state), an issue sidebar, a scrollable
27
+ transcript that streams assistant text as it arrives, and a footer status
28
+ line (stage, activity, elapsed time). Transcript turns start with a colored
29
+ `● <harness> · <title>` role label and blank-line separation; assistant text
30
+ is indented and plain, thinking is dim, each tool call is one row
31
+ (`✓ $ <command> · 1.2s`, `✓ read <path>`, `✗ <tool> <path> · failed:
32
+ <detail>`), and a session error is a red `✗ <message>` row. The sidebar
33
+ lists every issue discovered in the run with its outcome (`○` queued, `▶`
34
+ active, `✓` completed, `✗` failed, `⚠` hand-off, `−` skipped) and
35
+ follows the active issue until you navigate away with `[`/`]` or
36
+ Ctrl+Left/Right; each issue keeps its own transcript, so processed issues
37
+ stay browsable while the run continues. The queue starts
38
+ paused so the discovered plan can be inspected before work begins; `p`
39
+ resumes or pauses it between issues, `s` stops the queue after the active
40
+ issue and drains the run normally, and `q` (like Ctrl-C) cancels immediately.
41
+ The footer and sidebar hints show the pending pause or stop. Tool output, long commands, and deep paths stay inside the
42
+ transcript panel; resize is handled by the renderer; Ctrl-C is forwarded as
43
+ SIGINT so cancellation still restores the checkout and saves state; disposal
44
+ destroys the renderer and restores the terminal.
45
+ - CI and redirected output are the deterministic noninteractive fallback:
46
+ append-only, byte-identical across identical runs, with neither ANSI cursor
47
+ controls (`ESC`) nor carriage-return bytes; `stripTerminalControls` is an
48
+ identity no-op on these streams. Each session is a block that opens with
49
+ `╭─ <harness> · <title>` and closes with `╰─ done`. Assistant and thinking
50
+ text are buffered per block and printed as complete `│ ` lines; each tool
51
+ call gets one line, each tool completion one summary line
52
+ (`│ ✓ <tool> done` or `│ ✗ <tool> failed: <output>`), and each session
53
+ error one `│ ✗ <message>` line. Usage is not printed.
54
+ - `--output json` writes progress records and `session_event` records one per
55
+ line to stdout with stderr empty: every non-empty line parses as one
56
+ complete JSON record, human headers/glyphs never appear, and values are
57
+ preserved as supplied.
58
+
59
+ JSON events use a stable operational vocabulary and include `runId`,
60
+ `timestamp`, `stage`, `status`, and `message`. Pre-flight and hand-off events identify
61
+ whether agent work was skipped. Human-readable hand-off decisions name
62
+ the issue number and title and show the current/total queue position. JSON
63
+ output retains the complete event payload, including the structured details
64
+ field; human-readable output never renders that field. A
65
+ `hand-off` event includes its reason, summary, evidence, questions,
66
+ diagnostic or artifact path, and queue position.
67
+ Depending on the event, it may also include the repository, review attempt,
68
+ session ID, commit SHA, created issue numbers, or diagnostic paths. Supplied
69
+ progress-event values are preserved as-is; session transcripts are never
70
+ redacted, and terminal control sequences are stripped at the
71
+ reporting boundary.
72
+
73
+ ### Session events
74
+
75
+ Each `session_event` record wraps one session event with the session it
76
+ belongs to:
77
+
78
+ ```json
79
+ {"type":"session_event","sessionID":"claude-7d3e2a1b","directory":"/work/owner/repo","harness":"claude","title":"Implement #42","event":{"type":"tool_call","callId":"call-1","name":"Bash","input":{"command":"bun test"}}}
80
+ ```
81
+
82
+ | Field | Meaning |
83
+ | ----------- | --------------------------------------------------------- |
84
+ | `sessionID` | Ralphie's id for the session. |
85
+ | `directory` | Working directory of the session. |
86
+ | `harness` | Name of the harness that ran the session, such as `claude`. |
87
+ | `title` | Human-readable session label; omitted when there is none. |
88
+ | `event` | One of the event shapes below, discriminated by `type`. |
89
+
90
+ `event` is one of:
91
+
92
+ - `{"type":"session_started"}`: the session began producing work.
93
+ - `{"type":"assistant_text","kind":"text"|"thinking","text":…,"done":…}`: a
94
+ fragment of assistant output. Fragments of one `kind` concatenate into a
95
+ block until one arrives with `done: true`. `text` is only the fragment: a
96
+ closing fragment may be empty, and a whole block may arrive as a single
97
+ fragment with `done: true`.
98
+ - `{"type":"tool_call","callId":…,"name":…,"input":{…}}`: the assistant
99
+ started a tool. `callId` pairs the call with its result.
100
+ - `{"type":"tool_result","callId":…,"name":…,"output":…,"isError":…}`: the
101
+ tool finished. `output` is its text output, empty when it has none.
102
+ - `{"type":"error","message":…}`: the harness or model reported a failure.
103
+ - `{"type":"usage","inputTokens":…,"outputTokens":…}`, optionally with
104
+ `cacheReadTokens`, `cacheWriteTokens`, and `costUsd`: tokens, and cost where
105
+ the harness reports it, consumed since the session's previous `usage` event.
106
+ Sum the events for a session total.
107
+ - `{"type":"session_finished"}`: the session stopped producing work. Failures
108
+ are reported by `error` events, not here.
109
+
110
+ Tool names and `input` fields are the harness's own (for example Claude Code's
111
+ `Bash` tool takes `command`), so consumers that need tool-specific detail should key
112
+ on `harness` as well as `name`.
113
+
114
+ ## State and artifacts
115
+
116
+ The workspace's `.ralphie` directory contains only repository checkouts and
117
+ Ralphie's run state, events, and recovery artifacts. Harness credentials and
118
+ settings belong to each harness CLI and are never written under this path.
119
+
120
+ Run artifacts live under:
121
+
122
+ ```text
123
+ <workspace>/.ralphie/runs/<run-id>/
124
+ ├── state.json
125
+ ├── events.jsonl
126
+ └── issues/
127
+ ```
128
+
129
+ New runs write the durable event log to
130
+ `<workspace>/.ralphie/runs/<run-id>/events.jsonl`. The run closes the log
131
+ immediately before removing the workspace, so post-cleanup progress still
132
+ renders but is not persisted.
133
+
134
+ A normal issue execution obtains a durable per-issue artifact store at:
135
+
136
+ ```text
137
+ <workspace>/.ralphie/runs/<run-id>/issues/<issue-number>/artifacts.json
138
+ ```
139
+
140
+ The store prevents accidental overwrites and records readiness deferrals,
141
+ pre-flight decisions, checkpoints, review attempts, commit messages, created
142
+ commits, resolution proof, decomposition decisions, and created child-number
143
+ mappings. Stale or legacy un-fingerprinted decisions are removed on load
144
+ without disturbing the other artifacts for the issue.
145
+
146
+ A successful or interrupted run uses this more detailed layout (harness
147
+ configuration is not stored in this tree):
148
+
149
+ ```text
150
+ <workspace>/.ralphie/runs/<run-id>/
151
+ ├── state.json
152
+ ├── events.jsonl
153
+ └── issues/
154
+ └── <issue-number>/
155
+ ├── artifacts.json
156
+ ├── review-exhaustion/
157
+ │ ├── changes.patch
158
+ │ └── metadata.json
159
+ └── hand-off-<id>/
160
+ ├── changes.patch
161
+ └── metadata.json
162
+ ```
163
+
164
+ `state.json` is versioned, schema-validated, and atomically replaced. It
165
+ contains the repository/branch, the role assignments
166
+ (harness, model, effort),
167
+ pending and completed queue numbers, processed count, outcomes, active
168
+ issue/stage, checkout invariant, and update time. State is saved before the
169
+ queue starts, when an issue becomes active, after each issue outcome, after
170
+ queue refreshes, and at final completion. State is written for observability
171
+ only: Ralphie never loads a previous run's state.
172
+
173
+ ## Failure, cancellation, and exit status
174
+
175
+ ```mermaid
176
+ stateDiagram-v2
177
+ [*] --> Active: start
178
+ Active --> Active: persist issue/queue progress
179
+ Active --> Complete: queue empty
180
+ Active --> Stopped: error (saved as active)
181
+ Active --> Stopped: AbortSignal
182
+ Complete --> Cleaned: workspace removed after success
183
+ Stopped --> Retained: keep state/artifacts
184
+ Cleaned --> [*]
185
+ Retained --> [*]
186
+ ```
187
+
188
+ - One issue failure restores its checkout, persists the failed outcome, retains
189
+ artifacts, and continues to later issues.
190
+ - Ordinary failures set process exit code `1`.
191
+ - A limit, outage or expired login ends the run early. The affected issue is
192
+ recorded as `deferred` after its checkout is restored, nothing on GitHub
193
+ changes, no later issue starts, and the process exits `75` with a message
194
+ that names the failure and the reset time when the harness reports one. State
195
+ and artifacts are kept and cleanup is skipped. Rerun after the limit clears
196
+ or you sign in again. Why this halts instead of handing off is recorded in
197
+ [ADR-0004](adr/0004-environmental-failures-halt-instead-of-handing-off.md).
198
+ - Cancellation is checked before long-running boundaries and passed to the running session, which is killed.
199
+ Ralphie attempts to restore the clean issue checkpoint, saves state with the
200
+ active issue, skips cleanup, and exits `130`.
201
+ - Successful completion persists `complete`, then removes the entire workspace
202
+ (after protected-path checks). Cleanup is skipped when the run drains with
203
+ issue failures, fails, or is cancelled, so state and diagnostics remain
204
+ available.
205
+
206
+ An ordinary issue failure never stops the queue. Ralphie restores the failed
207
+ issue checkout, records its outcome, and continues independent issues. Failed
208
+ prerequisites are not marked complete, so dependent issues remain blocked.
209
+ After draining all reachable work, the run exits with status `1` and an
210
+ aggregate partial-failure summary.
211
+
212
+ Hand-off outcomes also continue the queue. A drained run completes with
213
+ status `0`, and the handed-off issue remains open but leaves the intake queue
214
+ because it no longer carries the agent-ready label. The deterministic
215
+ `decomposition_limit_reached` boundary behaves the same way: raise the
216
+ persisted `limits.maxDecompositionDepth`, narrow the issue, or resolve its review
217
+ findings manually, then relabel the issue `ready-for-agent` for a later run.
218
+ It never closes or marks the capped issue complete, so dependent work remains
219
+ blocked.
220
+
221
+ ## Hand-off handling
222
+
223
+ A validated hand-off decision is not an ordinary failure. Ralphie
224
+ persists the summary, evidence, questions, and issue
225
+ freshness metadata in the run artifacts, keeps the issue open, and continues
226
+ with later work.
227
+
228
+ The labels and comments a hand-off publishes are described under
229
+ [Hand-offs](workflows.md#hand-offs). They are published after recording the
230
+ outcome and before moving to the next issue. The comment carries a hidden
231
+ `ralphie:hand-off` marker, so a retry updates it instead of posting another.
232
+ Issues held back by open
233
+ blockers, whether reported by pre-flight or by queue order, are recorded as
234
+ skipped and change nothing on GitHub. A hand-off publishing failure fails the
235
+ run; the issue keeps its labels and a later run re-evaluates it from scratch.
236
+
237
+ When an executor session or pre-flight asks for a hand-off, Ralphie first persists the
238
+ bounded request, clean checkpoint, and issue freshness fingerprint. Exactly one
239
+ fresh read-only hand-off verification session verifies that request before the next artifact,
240
+ Git, or GitHub mutation. Only a `hand_off` verifier disposition confirms
241
+ it; actionable and already-resolved dispositions continue the original flow.
242
+ The confirmed decision is persisted before recovery writes a bounded binary-safe
243
+ patch and decision diagnostic, then restores and verifies the exact clean
244
+ checkpoint. A verifier or recovery interruption retains the pending hand-off so a later
245
+ attempt can retry verification or recovery without rerunning completed agent
246
+ work. The saved decision and pending hand-off are reused only when live `updatedAt` and
247
+ comment freshness metadata exactly match; a changed or invalid fingerprint
248
+ removes both atomically before routing continues.
249
+
250
+ ```mermaid
251
+ stateDiagram-v2
252
+ state "Issue in progress" as IssueInProgress
253
+ state "Recoverable stop" as RecoverableStop
254
+ state "Artifacts retained" as Retained
255
+ state "Workspace cleaned" as Cleaned
256
+
257
+ [*] --> Active: Start
258
+ Active --> IssueInProgress: Dequeue issue
259
+ IssueInProgress --> Active: Persist outcome and queue
260
+ IssueInProgress --> RecoverableStop: Failure or interruption
261
+ Active --> Complete: Queue empty
262
+ Complete --> Cleaned: Remove workspace after success
263
+ RecoverableStop --> Retained: Keep workspace
264
+ Retained --> [*]
265
+ Cleaned --> [*]
266
+ ```
267
+
268
+ Hand-off recovery diagnostics use the same issue directory and contain
269
+ `changes.patch` plus `metadata.json` under a fingerprint-bound
270
+ `hand-off-<id>/` directory. The patch includes tracked staged and unstaged
271
+ changes as well as untracked files. Matching diagnostics are reused within the
272
+ run; a fresh fingerprint receives a distinct directory. Diagnostics are
273
+ published atomically before the exact checkpoint is restored and verified.
274
+
275
+ ## Interruption and recovery
276
+
277
+ There is no resume command. When a run fails or is interrupted:
278
+
279
+ 1. the process exits non-zero (see
280
+ [exit status](#failure-cancellation-and-exit-status));
281
+ 2. the workspace retains `state.json`, `events.jsonl`, and per-issue artifacts
282
+ for diagnosis;
283
+ 3. issues that were not closed remain open and are selected again on the next
284
+ run; and
285
+ 4. the next run removes the workspace, prepares a fresh checkout, and
286
+ re-evaluates every matching open issue from scratch.
287
+
288
+ A hard crash (a killed process, a power loss) in the middle of the review gate
289
+ is the one case that leaves the retained checkout unclean: Ralphie creates
290
+ local candidate commits while review is in progress, so HEAD may sit at a
291
+ candidate commit. Candidate commits are never pushed, and the next run
292
+ discards the whole workspace; inspect the retained checkout before starting
293
+ it if you need the work. Ordinary failures and cancellation restore the
294
+ checkpoint first.
295
+
296
+ Because each run starts clean, recovery is a new run rather than a continuation:
297
+ completed issues are already closed and no longer selected, while interrupted
298
+ issues repeat pre-flight, implementation, and review. Inspect the retained
299
+ `state.json` and artifacts before deleting them if the failure needs
300
+ investigation.
301
+
302
+ Native sub-issue and dependency endpoints require `github.com`; GitHub
303
+ Enterprise Server is not supported by the current client. With an unavailable
304
+ endpoint or a token lacking issue write permission, live decomposition fails with
305
+ an actionable error naming the missing capability; there is no body-link fallback.
306
+ See [Workflows](workflows.md#platform-support-for-native-sub-issues-and-dependencies).
307
+
308
+ An ordinary issue failure never stops the queue. Ralphie restores the failed
309
+ issue checkout, records its outcome, and continues independent work; the
310
+ drained run exits `1` if any issue failed.
311
+
312
+ A configured deterministic verification command returning non-zero is handled
313
+ before it becomes an issue failure. Ralphie gives the bounded command output and
314
+ staged diff to the implementer's session (resumed with `/diagnosing-bugs`, or a
315
+ fresh fixer session when it cannot be resumed), restages its changes, and
316
+ retries up to `limits.verificationFixes` times. Only repair exhaustion or a non-repairable
317
+ verification fault (for example a command changing the staged tree) reaches the
318
+ ordinary failure boundary. When no `verify` commands are configured, the gate
319
+ is skipped.
320
+
321
+ ## Cleanup
322
+
323
+ Ralphie removes the entire workspace before preparing a run, after
324
+ protected-path checks, and again after a successful run. This deletes completed
325
+ state, events, diagnostics, and the repository checkout. Cleanup is skipped
326
+ when the run drains with issue failures, fails, or is cancelled, so state and
327
+ diagnostics remain available. Use a path dedicated to Ralphie; see
328
+ [Safety](safety.md) for the destructive workspace contract.
package/docs/safety.md ADDED
@@ -0,0 +1,189 @@
1
+ # Safety model
2
+
3
+ This page is for operators before they run Ralphie against a repository and for
4
+ contributors changing mutation paths. It is the authoritative reference for
5
+ Git/GitHub mutation boundaries, remote invariants, and workspace risks. Return
6
+ to the [documentation index](README.md) for the full
7
+ reading map.
8
+
9
+ > [!CAUTION]
10
+ > Ralphie commits approved work and pushes directly to the branch selected by
11
+ > `repos."owner/repo".branch` (default `main`, otherwise `master`). Test against a disposable repository before enabling mutations.
12
+
13
+ ## Delivery guardrails
14
+
15
+ Delivery automation deserves explicit guardrails. Before agent work and again
16
+ before a push, Ralphie verifies that:
17
+
18
+ - the checkout and `origin` match the requested GitHub repository;
19
+ - the local checkout is still on the selected branch and expected commit;
20
+ - the remote branch has not moved from the captured base;
21
+ - the result is exactly the expected local commit; and
22
+ - the push is non-force.
23
+
24
+ If any invariant fails, Ralphie halts instead of guessing or retrying a
25
+ dangerous operation.
26
+
27
+ Delivery is one deterministic operation: it creates exactly one commit from the
28
+ allowed staged tree, re-checks the local branch/head and the remote base
29
+ immediately before the push, and pushes only with Git's non-force mode. A push
30
+ response is not proof; an authoritative remote branch read establishes whether
31
+ the commit arrived, including reconciliation of a lost push response. Movement
32
+ detected before staging/commit prevents the commit from being created; movement
33
+ detected before or during delivery is never followed, reset over, or
34
+ force-pushed over. Cancellation is checked at every mutation boundary, the push
35
+ is attempted at most once, and failures and cancellations leave a clean,
36
+ recoverable checkout.
37
+
38
+ Implementation agents may use whatever shell their harness grants. Ralphie does
39
+ not filter their commands: the guardrails are the session environment (see
40
+ [Session isolation](#session-isolation)) and the deterministic repository
41
+ invariants and delivery services, which remain authoritative.
42
+
43
+ ## Workspace risk
44
+
45
+ There is one intentionally destructive local behavior: when reusing an existing
46
+ repository checkout that is not clean, Ralphie runs the equivalent of `git reset
47
+ --hard` and `git clean -fd`, then aligns it with the selected remote branch.
48
+ Tracked modifications and untracked, non-ignored files inside that checkout are
49
+ discarded. Keep unrelated work outside Ralphie's workspace.
50
+
51
+ The workspace's `.ralphie` directory contains only repository checkouts and
52
+ Ralphie's run state, events, and recovery artifacts. Harness credentials and
53
+ settings belong to each harness CLI and are never written under this path;
54
+ keep provider configuration outside the workspace.
55
+
56
+ Ralphie removes the entire workspace recursively before preparing a run and
57
+ again after a successful run, after protected-path checks. The retained
58
+ workspace after a failure is
59
+ [documented with cleanup and recovery](operations-and-recovery.md#cleanup). Use
60
+ a path dedicated to Ralphie:
61
+
62
+ ```bash
63
+ bunx @beremaran/ralphie owner/repository \
64
+ --set workspace=/tmp/ralphie
65
+ ```
66
+
67
+ ## Agent and mutation boundaries
68
+
69
+ Agent sessions run as headless harness CLI invocations rooted at the
70
+ repository checkout. Read-only roles run in the harness's read-only mode
71
+ (Claude Code plan mode limited to the Read, Glob and Grep tools, so there is
72
+ no shell: reviewers cannot run `git diff`, and Ralphie puts the diff in their
73
+ prompt instead); the `implementer` and `fixer` run under their
74
+ [approval mode](#approval-modes) and may edit the checkout. Post-task
75
+ verification fails the task when the checkout's branch or head moved anyway.
76
+ Structured decisions are returned as a result validated against the canonical
77
+ Zod schema (natively where the harness supports it, otherwise from a final
78
+ JSON block, with a bounded number of corrections), and the validated value is
79
+ what the domain boundary accepts. A repository-backed blocker is the
80
+ implementer's `needs_attention` status with a `needsAttention` object (`reason`
81
+ and `questions`) in that result, or a pre-flight `hand_off` disposition; neither
82
+ is a mutation-capable tool, and Ralphie verifies the request before handing
83
+ off. Ralphie
84
+ stages, verifies, commits, pushes, and mutates
85
+ GitHub through deterministic domain services. Invalid output or a harness failure
86
+ becomes a failed issue outcome without proceeding to the next operation.
87
+ A turn that produces no assistant message fails instead of producing a
88
+ decision.
89
+
90
+ Protected maintainer choices are also enforced before verification: a staged
91
+ change that selects a project license fails closed unless that exact license
92
+ is authorized by the issue text, deferring to a maintainer decision instead of
93
+ silently establishing policy.
94
+
95
+ Verification is opt-in: Ralphie runs only the commands listed under
96
+ `repos."owner/repo".verify`, and when none are listed the gate is skipped and review
97
+ proceeds on the staged diff. Configured commands run against the staged tree
98
+ and their evidence is
99
+ bound to that tree before review or commit. A non-zero command exit is treated
100
+ as actionable implementation feedback: the fix session (the implementer's, resumed) receives bounded
101
+ failure evidence, and Ralphie restages and retries up to `limits.verificationFixes` times.
102
+ Staged-tree mutation and exhausted repair remain
103
+ hard safety stops. The direct-push path never uses force. See
104
+ [Workflows](workflows.md) for the complete implementation and delivery sequence,
105
+ and [Operations and recovery](operations-and-recovery.md) for what remains
106
+ available after a safety stop.
107
+
108
+ ## Approval modes
109
+
110
+ Read-only roles never edit and ignore the approval mode. The editing roles
111
+ (`implementer` and `fixer`) run under `approval`, set at the top level of the
112
+ configuration and overridable per repository and per harness
113
+ (`harnesses.<name>.approval`):
114
+
115
+ - `safe` (default) uses the harness's own approval or sandbox: Claude Code
116
+ auto mode, or the Codex workspace-write sandbox.
117
+ - `yolo` turns off every approval and sandbox check (Claude Code
118
+ `bypassPermissions`, Codex `--dangerously-bypass-approvals-and-sandbox`, and
119
+ the only mode pi and OpenCode have). Use it only in an environment that is
120
+ already isolated.
121
+
122
+ Before any work starts, Ralphie checks that every assigned harness starts,
123
+ that `safe` is actually granted where configured (Claude Code can silently
124
+ fall back from auto mode), and that no editing role runs on pi or OpenCode
125
+ without `yolo`, because neither has a sandbox or approval system. A failure
126
+ stops the run within seconds and names the configuration change that fixes it,
127
+ such as `harnesses.pi.approval: yolo` or moving the role with
128
+ `roles.implementer`. Spend caps (`limits.maxBudgetUsd`) are covered in
129
+ [Configuration](configuration.md#limits).
130
+
131
+ Each session also has a wall-clock limit
132
+ ([`limits.sessionTimeoutMinutes`](configuration.md#limits)); exceeding it kills
133
+ the session's whole process group, so tools the harness started die with it.
134
+
135
+ ## Session isolation
136
+
137
+ Sessions never hold GitHub or push authority (ADR-0003); the session
138
+ environment enforces it instead of the prompts.
139
+
140
+ - **No credentials.** Every session starts without `GH_TOKEN`, `GITHUB_TOKEN`,
141
+ `GH_ENTERPRISE_TOKEN` and `GITHUB_ENTERPRISE_TOKEN`, and with `GH_CONFIG_DIR`
142
+ pointing at a fresh, empty temporary directory that is removed when the
143
+ session ends, so a stored `gh` login is not visible either. These entries
144
+ override anything a request sets.
145
+ - **No SSH or git credentials.** Every session also starts without
146
+ `SSH_AUTH_SOCK`, `SSH_ASKPASS` and `GIT_ASKPASS`, with `GIT_CONFIG_GLOBAL`
147
+ and `GIT_CONFIG_SYSTEM` set to `/dev/null` and the repository's
148
+ `credential.helper` list reset (so no credential helper applies),
149
+ `GIT_TERMINAL_PROMPT=0`, and `GIT_SSH_COMMAND=false`, so a git remote cannot
150
+ authenticate through an ssh agent, a helper or a prompt. Ralphie's own
151
+ delivery push runs outside sessions and is unaffected.
152
+ - **Residual limitation.** The environment cannot hide credentials that a
153
+ process running as the same OS user can read directly: a private key under
154
+ `~/.ssh` used through an explicit `ssh -i`, a `gh` token read straight out of
155
+ the operating system keyring (for example with `security find-generic-password`
156
+ on macOS), or a credential file read by path. `gh` itself is logged out in a
157
+ session even when its token lives in the keyring, because it finds keyring
158
+ entries through the hosts file in its now-empty config directory. Ralphie does not claim to block these, and a determined
159
+ yolo session could still use them. What it guarantees is that it never hands
160
+ a token to a session, that the checkout's push URL is disabled, and that its
161
+ own delivery push is verified against the remote. To close the gap, run
162
+ Ralphie as a dedicated OS user whose home, keyring and ssh keys hold no
163
+ credentials for the repository.
164
+ - **No push from the workspace.** After preparing the checkout Ralphie sets
165
+ origin's push URL to a disabled value, so `git push` inside the workspace
166
+ fails. Ralphie's own delivery push names the fetch URL explicitly, never
167
+ uses force, and is verified against the remote afterwards.
168
+ - **Read-only means unchanged.** Before and after every read-only session
169
+ Ralphie fingerprints HEAD, the index, tracked changes and untracked file
170
+ contents. Any difference fails the session (kind `access`), which fails the
171
+ issue closed.
172
+
173
+ ## Bounded command execution
174
+
175
+ No command runs unbounded. Every process Ralphie spawns carries a hard
176
+ deadline so a hung process fails loudly instead of stalling an issue run:
177
+
178
+ - **Sessions** are bounded by `limits.sessionTimeoutMinutes`, described above.
179
+ - **Ralphie-owned commands** (git and `gh` operations against the repository,
180
+ workspace preparation, authentication checks) default to a 10-minute timeout.
181
+ - **Verification commands** (`verify`) run under a 30-minute timeout
182
+ because they execute the repository's full gate; they are the deliberate
183
+ exception to the shorter defaults. When no command is configured, no
184
+ verification process runs.
185
+
186
+ A timed-out command is killed (including its process tree) and reported as
187
+ `CommandTimeoutError` with the deadline and command in the message. These
188
+ deadlines are fail-closed bounds, not retry budgets: they turn an indefinitely
189
+ stuck session into a recoverable, reported failure.