@open-tomato/rafa 0.45.0 → 0.46.0

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 (140) hide show
  1. package/README.md +255 -23
  2. package/dist/PROMPT.md +18 -1
  3. package/dist/adapters/logger/settings.d.ts +132 -0
  4. package/dist/bugs/closest.d.ts +19 -0
  5. package/dist/bugs/fenced.d.ts +8 -0
  6. package/dist/bugs/places.d.ts +20 -0
  7. package/dist/bugs/record.d.ts +145 -0
  8. package/dist/bugs/title.d.ts +83 -0
  9. package/dist/bundled/agents/doc-updater.md +17 -3
  10. package/dist/bundled/agents/loop-implementer.md +14 -2
  11. package/dist/bundled/operators/skills/rafa-stretch-pit-stop/SKILL.md +10 -3
  12. package/dist/bundled/operators/skills/rafa-stretch-sweep/SKILL.md +5 -0
  13. package/dist/bundled/skills/dev-planner/SKILL.md +101 -18
  14. package/dist/cli.js +74726 -62960
  15. package/dist/config/entry.d.ts +119 -0
  16. package/dist/config/index.d.ts +36 -0
  17. package/dist/config/index.js +51 -0
  18. package/dist/config/shape.d.ts +232 -0
  19. package/dist/config-registry.d.ts +127 -0
  20. package/dist/config-schema-board-project.d.ts +157 -0
  21. package/dist/config-schema-bugs.d.ts +71 -0
  22. package/dist/config-schema-errors.d.ts +54 -0
  23. package/dist/config-schema-features.d.ts +64 -0
  24. package/dist/config-schema-hub.d.ts +150 -0
  25. package/dist/config-schema-issues.d.ts +95 -0
  26. package/dist/config-schema-logger.d.ts +91 -0
  27. package/dist/config-schema-loop-continue.d.ts +122 -0
  28. package/dist/config-schema-loop-retries.d.ts +94 -0
  29. package/dist/config-schema-release.d.ts +307 -0
  30. package/dist/config-schema-tests.d.ts +170 -0
  31. package/dist/config-schema-triage.d.ts +142 -0
  32. package/dist/config-schema-wrap-up.d.ts +96 -0
  33. package/dist/core/command/command.d.ts +228 -0
  34. package/dist/core/command/confirm.d.ts +67 -0
  35. package/dist/core/command/index.d.ts +112 -0
  36. package/dist/core/command/lazy.d.ts +54 -0
  37. package/dist/core/command/registry-types.d.ts +96 -0
  38. package/dist/core/command/running.d.ts +96 -0
  39. package/dist/core/command/spends.d.ts +39 -0
  40. package/dist/core/command/types.d.ts +92 -0
  41. package/dist/core/command/version.d.ts +4 -0
  42. package/dist/core/config/branch-template.d.ts +20 -0
  43. package/dist/core/config/config-items.d.ts +86 -0
  44. package/dist/core/config/config-load.d.ts +47 -0
  45. package/dist/core/config/config-origins.d.ts +57 -0
  46. package/dist/core/config/config-readers.d.ts +167 -0
  47. package/dist/core/config/config-schema.d.ts +207 -0
  48. package/dist/core/config/config-sections.d.ts +493 -0
  49. package/dist/core/config/config.d.ts +320 -0
  50. package/dist/core/config/core-section.d.ts +39 -0
  51. package/dist/core/config/dangerous.d.ts +58 -0
  52. package/dist/core/config/default-routing.d.ts +16 -0
  53. package/dist/core/config/index.d.ts +141 -0
  54. package/dist/core/config/scope.d.ts +42 -0
  55. package/dist/core/config/section.d.ts +206 -0
  56. package/dist/core/config/sections.d.ts +45 -0
  57. package/dist/core/config/standard-schema.d.ts +27 -0
  58. package/dist/core/git/commit.d.ts +232 -0
  59. package/dist/core/git/gh-runner.d.ts +24 -0
  60. package/dist/core/git/git.d.ts +22 -0
  61. package/dist/core/git/index.d.ts +85 -0
  62. package/dist/core/loop/claude.d.ts +388 -0
  63. package/dist/core/loop/hop-record.d.ts +52 -0
  64. package/dist/core/loop/index.d.ts +179 -0
  65. package/dist/core/loop/session-env.d.ts +20 -0
  66. package/dist/core/loop/session-record-parse.d.ts +63 -0
  67. package/dist/core/loop/sessions.d.ts +408 -0
  68. package/dist/core/loop/suite-failure.d.ts +25 -0
  69. package/dist/core/output/active.d.ts +100 -0
  70. package/dist/core/output/stream.d.ts +20 -0
  71. package/dist/core/output/text.d.ts +55 -0
  72. package/dist/core/plan/block-reader.d.ts +46 -0
  73. package/dist/core/plan/blocks.d.ts +139 -0
  74. package/dist/core/plan/declaration.d.ts +366 -0
  75. package/dist/core/plan/plan-stamp.d.ts +75 -0
  76. package/dist/core/runtime/identity.d.ts +21 -0
  77. package/dist/core/runtime/index.d.ts +46 -0
  78. package/dist/effort/store/bring-forward.d.ts +131 -0
  79. package/dist/effort/store/check-rows.d.ts +31 -0
  80. package/dist/effort/store/commit-stats.d.ts +42 -0
  81. package/dist/effort/store/copy.d.ts +35 -0
  82. package/dist/effort/store/development-build.d.ts +105 -0
  83. package/dist/effort/store/effort-levels.d.ts +13 -0
  84. package/dist/effort/store/findings.d.ts +214 -0
  85. package/dist/effort/store/fix-schema.d.ts +184 -0
  86. package/dist/effort/store/index.js +10 -7
  87. package/dist/effort/store/location.d.ts +44 -0
  88. package/dist/effort/store/merge-commit-gaps.d.ts +68 -0
  89. package/dist/effort/store/merge-conflicts.d.ts +91 -0
  90. package/dist/effort/store/merge-rules.d.ts +82 -0
  91. package/dist/effort/store/merge-store.d.ts +211 -0
  92. package/dist/effort/store/merge-union.d.ts +92 -0
  93. package/dist/effort/store/migrate.d.ts +146 -0
  94. package/dist/effort/store/migrations.d.ts +92 -0
  95. package/dist/effort/store/ndjson-files.d.ts +114 -0
  96. package/dist/effort/store/origins.d.ts +116 -0
  97. package/dist/effort/store/plan-stubs.d.ts +79 -0
  98. package/dist/effort/store/rebuild-aside.d.ts +172 -0
  99. package/dist/effort/store/schema-plan.d.ts +102 -0
  100. package/dist/effort/store/session-row-types.d.ts +64 -0
  101. package/dist/effort/store/settings.d.ts +13 -0
  102. package/dist/effort/store/sqlite.d.ts +340 -0
  103. package/dist/effort/store/store-generation.d.ts +17 -0
  104. package/dist/effort/store/store-identity.d.ts +220 -0
  105. package/dist/effort/store/store-meta.d.ts +305 -0
  106. package/dist/effort/store/store-replaced.d.ts +72 -0
  107. package/dist/effort/store/types.d.ts +223 -0
  108. package/dist/errors/codes.d.ts +56 -0
  109. package/dist/errors/rafa-codes.d.ts +36 -0
  110. package/dist/index-2a7nb3an.js +125 -0
  111. package/dist/{index-r28fzsvc.js → index-39kbcw68.js} +350 -165
  112. package/dist/{index-nv34ptht.js → index-77ajfffn.js} +14 -9
  113. package/dist/index-cy98y87p.js +2597 -0
  114. package/dist/index-gqz4jn6j.js +13447 -0
  115. package/dist/index-gvek4r8m.js +710 -0
  116. package/dist/index-j4k2vdz3.js +1125 -0
  117. package/dist/index-jh1r4kcn.js +1067 -0
  118. package/dist/{index-mgh2s9bj.js → index-ykggr043.js} +58 -455
  119. package/dist/index-z1w83f81.js +4 -0
  120. package/dist/index.js +14715 -21516
  121. package/dist/learning/index.js +5 -6
  122. package/dist/plan/index.js +12 -11
  123. package/dist/plan-prompt.md +3 -1
  124. package/dist/ports/index.d.ts +732 -0
  125. package/dist/ports/learning.d.ts +101 -0
  126. package/dist/ports/log-level.d.ts +13 -0
  127. package/dist/ports/spec-review.d.ts +66 -0
  128. package/dist/pr/checks.d.ts +133 -0
  129. package/dist/pr/types.d.ts +422 -0
  130. package/dist/ready-4f8edezt.js +2016 -0
  131. package/dist/release-templates/release-bot-fragment.yml +153 -0
  132. package/dist/release-templates/release-publish.yml +110 -0
  133. package/dist/release-templates/release-settle.yml +122 -0
  134. package/dist/release-templates/release-tag.yml +102 -0
  135. package/dist/report/parse.d.ts +415 -0
  136. package/dist/schema/project-id.d.ts +79 -0
  137. package/dist/schema/tiers.d.ts +107 -0
  138. package/package.json +41 -3
  139. package/dist/index-hf2k497d.js +0 -16
  140. package/dist/index-nsr78ct2.js +0 -8219
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Spawning a Claude session, and reading how much of the plan's budget
3
+ * has gone.
4
+ *
5
+ * {@link runClaude} is the loop's door onto the CLI for a session whose
6
+ * output only the operator reads. It has one call site, the CI-repair
7
+ * session in `start/pr-lifecycle.ts`, which is not routed — one model,
8
+ * one effort, every tool. So the flags are a parameter with an EMPTY
9
+ * default: `runClaude(prompt, settingSources)` spawns the base
10
+ * arguments and the setting sources, and nothing a declaration could
11
+ * add.
12
+ *
13
+ * {@link runClaudeCaptured} is the door for a session whose output the
14
+ * LOOP reads as well. It has three callers: the per-task dispatch,
15
+ * through `runTaskSession` in `start/dispatch.ts`, the wrap-up session
16
+ * in `start/wrap-up.ts` and the backfill proposal pass in
17
+ * `backfill/propose.ts`. Plan generation in the `claude` planner,
18
+ * `adapters/planner/claude.ts`, which `plan.ts` makes, captures its
19
+ * session through the same spawner without that door, for the reason
20
+ * the search runner does below. Each of the four parses what its
21
+ * session wrote: a task session ends its final message
22
+ * with a `rafa:report` block, the wrap-up with a `rafa:promoted` one
23
+ * when it was listed lessons to promote (`start/promoted-check.ts`), a
24
+ * plan session ends its final message with a `rafa:spec-review` one,
25
+ * and a proposal session's answer goes through `parseSessionAnswer`.
26
+ * The wrap-up, like a task session, also hands over the flags serving
27
+ * it the rafa-tier winners (`start/serving.ts`), and is not routed.
28
+ * {@link spawnClaude} answers the exit code alone, so a loop holding
29
+ * that session's exit code holds nothing else. A task session's flags are
30
+ * the ones its routing declaration resolved to, with the
31
+ * `--session-id` the loop picked for that session ahead of them and
32
+ * its served flags (`start/serving.ts`) ahead of both, and the planner
33
+ * hands over none: its argument list is `claudeArgs(settingSources)`. The captured entry builds its argument
34
+ * list through the same {@link claudeArgs} and hands the prompt over the
35
+ * same way; only the spawner differs, {@link spawnClaudeCaptured} piping
36
+ * stdout, echoing it on to the operator as it arrives and keeping the
37
+ * same bytes for the answer. It sits BESIDE `runClaude` rather than
38
+ * replacing its spawner, so in text mode a session nothing parses keeps
39
+ * spawning exactly what it spawned before. What json mode changes is in
40
+ * the section "What reaches the operator" below.
41
+ *
42
+ * The search runner (`inventory/search/index.ts`) captures its one
43
+ * session through the same spawner without going through
44
+ * `runClaudeCaptured`: it builds its argument list with
45
+ * {@link claudeArgs} and hands the spawner its one option,
46
+ * {@link CapturedSpawnOptions.cwd}, since that session runs inside the
47
+ * scratch copy of its candidates. The epic verification runner
48
+ * (`epic/verify-run.ts`) does the same for each check session, which
49
+ * runs inside a worktree made for the run, and the `claude` planner for
50
+ * its plan session, which runs in the project root `rafa plan` resolved
51
+ * rather than in the caller's directory (#171). The loop's sessions
52
+ * name the same option through `runClaude` and `runClaudeCaptured`,
53
+ * which take it last: each task, wrap-up and CI repair session runs in
54
+ * the run's checkout (`start/checkout.ts`), a linked worktree's own
55
+ * directory when the loop runs in one, and never in the directory the
56
+ * process happens to stand in.
57
+ *
58
+ * The flags land AFTER {@link CLAUDE_BASE_ARGS} and the setting sources
59
+ * rather than before, and the ordering is load-bearing rather than
60
+ * cosmetic. `--tools` is
61
+ * VARIADIC in the CLI's own help (`--tools <tools...>`), so it keeps
62
+ * consuming tokens until one that starts with a dash. `--tools` is the
63
+ * LAST flag `resolveDeclarationFlags` emits, and the resolved flags are
64
+ * the last thing appended here, so its value is the final element of
65
+ * the argument list with nothing after it to be swallowed. Putting the
66
+ * resolved flags FIRST would put `-p` behind that variadic, which is
67
+ * the one arrangement that breaks. `-p` itself takes no value
68
+ * (`-p, --print` is a boolean in the same help text), so nothing after
69
+ * it is at risk either.
70
+ *
71
+ * ## Setting sources
72
+ *
73
+ * Every session, through either door, is spawned with
74
+ * {@link SETTING_SOURCES_FLAG} naming the run's `loop.settingSources`
75
+ * (`config.ts`), `project,local` unless a config names others. Finding
76
+ * 3 of `.rafa/specs/phase-1-installable.md` measured the user scope at about
77
+ * 14,900 tokens of a 58,989-token turn. The sources are a REQUIRED
78
+ * parameter of {@link claudeArgs} and of both doors. A default here
79
+ * would be a second one beside `CONFIG_DEFAULTS`, and a caller that
80
+ * forgot to hand on the value its config resolved to would spawn under
81
+ * that default with nothing to say so; with no default, that caller
82
+ * does not compile.
83
+ *
84
+ * They go between the base arguments and the flags. The CLI's help, on
85
+ * Claude Code 2.1.268, lists `--setting-sources <sources>` as one
86
+ * comma-separated value where `--tools <tools...>` is variadic, so the
87
+ * sources swallow nothing and `--tools` stays last. What they change is
88
+ * measurable without a model call: an `--agent` name no scope defines
89
+ * exits 1 and lists the agents it could have run. That list held this
90
+ * repo's `.claude/agents` and none of `~/.claude/agents` under
91
+ * `project,local`, both under `user,project,local`, and neither under
92
+ * `local`.
93
+ *
94
+ * The prompt goes on STDIN and never into the argument list. That is
95
+ * not a style choice: a plan's task prompt here is the injected
96
+ * instructions plus `PROMPT.md` plus the plan, the whole file under the
97
+ * `full` injection mode and in every wrap-up prompt, which is far past
98
+ * what an argument list can carry, and an argument list is also
99
+ * visible to every `ps` on the machine.
100
+ *
101
+ * {@link ClaudeSpawner} exists because the real spawn is
102
+ * `Bun.spawn` and the root suite runs vitest under NODE — measured,
103
+ * `globalThis.Bun` is `undefined` and `process.versions.bun` is null
104
+ * in that runtime. So the default spawner cannot be driven from a test
105
+ * at all, and the seam is what makes the argument list an assertion
106
+ * rather than something inferred from a session that appeared to work.
107
+ *
108
+ * ## What reaches the operator
109
+ *
110
+ * A session's stdout reaches the operator in the mode of the active
111
+ * output (`core/output/active.ts`). In text mode it is the bytes the
112
+ * session wrote, in the order it wrote them: {@link spawnClaude} inherits
113
+ * the stream, and {@link spawnClaudeCaptured} writes each chunk to
114
+ * `process.stdout` as it arrives. In json mode those bytes would put
115
+ * lines no NDJSON reader parses among the events, so neither door lets
116
+ * them through. {@link spawnClaudeCaptured} hands each line, its newline
117
+ * off, to the active output's `info`, which the `json` adapter writes as
118
+ * one `log` event. The `events` mode routes them the same way, and its
119
+ * adapter drops every `info`, so the screen keeps only its `rafa·` lines.
120
+ * {@link spawnClaude} spawns through
121
+ * {@link spawnClaudeCaptured} and answers its exit code alone. A last
122
+ * line with no newline goes once stdout closes, and a blank line is an
123
+ * event with an empty message, so the messages joined with newlines are
124
+ * the session's stdout. The argument list, the prompt, the environment
125
+ * and the inherited stderr are the same in both modes.
126
+ *
127
+ * The environment is `sessionSpawnEnv(process.env)` at both doors
128
+ * (`session-env.ts`), the one function a reading of the session's
129
+ * environment calls too, so neither door spells an entry of its own.
130
+ * That puts the running entry's `bundled/bin` at the front of every
131
+ * session's `PATH`.
132
+ *
133
+ * ## Interrupting a running session
134
+ *
135
+ * Both doors hold the process they spawn among the live sessions until it
136
+ * has exited, and {@link interruptClaudeSessions} sends each of those
137
+ * SIGINT. `loop start` calls it from its own SIGINT handler (`src/start.ts`).
138
+ * A terminal's Ctrl-C reaches the loop and its session at once, as one
139
+ * process group, but `rafa loop stop` signals the loop's pid alone, and a
140
+ * signal to that pid never reaches the session. Measured on 2026-09-15 by
141
+ * spawning `loop start` over a stand-in `claude` sleeping 20 seconds: a
142
+ * SIGINT to the loop's pid ended the run after 20.06 s, once the session
143
+ * had run to its own end, and one to its process group after 0.03 s.
144
+ *
145
+ * A signalled session has ended once its process exits, but the captured
146
+ * door reads the session's stdout to its end first, and a process the
147
+ * session left behind holding that stdout keeps it open. Measured on
148
+ * 2026-09-15 with a stand-in that trapped SIGINT and exited 130 while a
149
+ * sleep it had put in the background held the pipe: `rafa loop stop` took
150
+ * 19.9 s, ending when the sleep did, against 0.28 s with the sleep's output
151
+ * sent to `/dev/null`. Both doors answered 130 for a stand-in the signal
152
+ * ended, under bun 1.3.14.
153
+ *
154
+ * ## The spend guard
155
+ *
156
+ * Both doors warn when the running command's `spends` declaration
157
+ * (`packages/rafa/src/core/command/spends.ts`) does not cover the session they start,
158
+ * and start it anyway: usage is never a gate. The running command is the
159
+ * one the dispatcher recorded with its parsed flags
160
+ * (`packages/rafa/src/core/command/running.ts`).
161
+ * The check runs once per session, before `Bun.spawn`: {@link spawnClaude}
162
+ * in json mode hands its session to the same unchecked spawner
163
+ * {@link spawnClaudeCaptured} uses rather than to that door, and so does
164
+ * not warn a second time.
165
+ *
166
+ * A run is covered when its command declares `always` or `through`, a
167
+ * `with` declaration and the run carries the flag, or an `unless`
168
+ * declaration and the run does not. `through` covers a run because the
169
+ * command runs another's action in-process (`rafa next`,
170
+ * `src/next/actions.ts`), and the recorded command stays the outer one.
171
+ * A flag is matched by its name without the dashes, and `--no-<name>`
172
+ * also as the parser keys it, `<name>` set to `false`; a flag recorded
173
+ * as `false` is not carried. With no command recorded, as for a caller
174
+ * that never went through the dispatcher, nothing is warned.
175
+ *
176
+ * The warning is one `warn` line at the active output, `⚠️ ` and then
177
+ * {@link spendWarning}'s sentence, which names the command as typed
178
+ * after `rafa` and says to declare `spends` on it, with the flag missing
179
+ * for a `with` form and the flag present for an `unless` one. It changes
180
+ * no exit code. In text mode it is a `warn:` line on the output's one
181
+ * stream (`packages/rafa/src/core/output/text.ts`); in json mode a `log` event at
182
+ * level `warn`; in events mode nothing, since that output drops `warn`.
183
+ */
184
+ import type { RunningCommand } from '../command/running.js';
185
+ import type { ClaudeSettingSource } from '../config/config-sections.js';
186
+ /**
187
+ * The executable every session is spawned as.
188
+ *
189
+ * Resolved on PATH rather than by absolute path, which is what lets a
190
+ * version manager or a shim answer for it.
191
+ */
192
+ export declare const CLAUDE_BIN = "claude";
193
+ /**
194
+ * Arguments every session gets, before any resolved flag.
195
+ *
196
+ * `-p` prints and exits, which is what makes the session
197
+ * non-interactive; `--dangerously-skip-permissions` is what lets it
198
+ * run unattended. Neither is a task's to choose, so neither is
199
+ * reachable from a declaration.
200
+ */
201
+ export declare const CLAUDE_BASE_ARGS: readonly string[];
202
+ /**
203
+ * The flag every session names its setting sources with. See the
204
+ * module note.
205
+ */
206
+ export declare const SETTING_SOURCES_FLAG = "--setting-sources";
207
+ /**
208
+ * Builds the argument list for one session: the base arguments, the
209
+ * setting sources, the served flags, then the flags.
210
+ *
211
+ * `settingSources` is the run's resolved `loop.settingSources`, joined
212
+ * with commas in the order given. `config.ts` has already refused a
213
+ * source the CLI does not name, a repeat and an empty list, so nothing
214
+ * is checked again here.
215
+ *
216
+ * `flags` is whatever a task's declaration resolved to, already
217
+ * validated and already ordered by its own resolver. Nothing is
218
+ * reordered, deduped or filtered here: this module has no opinion on
219
+ * which flags are legal, and one that did would be a second authority
220
+ * for a decision `core/plan/declaration.ts` already makes.
221
+ *
222
+ * `served` is `ServedSet.flags` from `serveResolution`
223
+ * (`src/tiers/serve.ts`), the session-only flags handing the run's
224
+ * served directory over, and is taken as it is for the same reason.
225
+ * It defaults to empty, so a caller that serves nothing spawns what it
226
+ * spawned before. It goes BEFORE `flags`, never after: `--tools` has to
227
+ * stay the last element (see the module note), and the served flags
228
+ * are safe ahead of the resolved ones, since `serve.ts` ends them with
229
+ * `--agents <json>` when an agent is served and every resolved flag
230
+ * starts with a dash, which is where the variadic `--add-dir` stops.
231
+ */
232
+ export declare function claudeArgs(settingSources: readonly ClaudeSettingSource[], flags?: readonly string[], served?: readonly string[]): string[];
233
+ /**
234
+ * Runs `claude` with an argument list and a prompt on stdin, and
235
+ * answers the exit code.
236
+ *
237
+ * The seam every test drives, since the default {@link spawnClaude}
238
+ * cannot run under the root suite's runtime.
239
+ */
240
+ export type ClaudeSpawner = (args: readonly string[], prompt: string, options?: CapturedSpawnOptions) => Promise<number>;
241
+ /**
242
+ * Sends SIGINT to every session either door spawned and has not yet seen
243
+ * exit, and answers how many that is. A process that exited before its
244
+ * door saw it is signalled too, which does nothing: measured under bun
245
+ * 1.3.14, `kill` on a subprocess that has exited throws nothing. See the
246
+ * module note.
247
+ */
248
+ export declare function interruptClaudeSessions(): number;
249
+ /**
250
+ * True when `flags` carry `flag`, written as typed with its dashes: its
251
+ * name is recorded as anything but `false`, or, for a `--no-<name>`
252
+ * flag, `<name>` is recorded as `false`, which is how the parser keys it.
253
+ */
254
+ export declare function carriesFlag(flags: RunningCommand['flags'], flag: string): boolean;
255
+ /**
256
+ * Why `running`'s declaration does not cover the session it starts, as a
257
+ * sentence, or null when it does or nothing is recorded.
258
+ */
259
+ export declare function spendWarning(running: RunningCommand | null): string | null;
260
+ /**
261
+ * The real spawner: `Bun.spawn`, streams inherited so the session's
262
+ * output reaches the operator as it happens, in the working directory
263
+ * `options.cwd` names, or the loop's own when it names none.
264
+ *
265
+ * In json or events mode it spawns through {@link spawnClaudeCaptured}
266
+ * instead and answers that session's exit code, so the session's stdout
267
+ * reaches the active output's `info` and never the screen as bytes; see
268
+ * the module note.
269
+ *
270
+ * An exit code of `undefined` — which is what a signalled process
271
+ * answers — is reported as 1, because every caller here treats a
272
+ * non-zero as a failed session and a killed one is not a success.
273
+ *
274
+ * Warns, and spawns anyway, when the running command's `spends` does not
275
+ * cover the run; see the module note.
276
+ */
277
+ export declare function spawnClaude(args: readonly string[], prompt: string, options?: CapturedSpawnOptions): Promise<number>;
278
+ /**
279
+ * Spawns one Claude session with `prompt` on stdin, loading settings
280
+ * from `settingSources`.
281
+ *
282
+ * `flags` defaults to empty, so the CI-repair session, which hands
283
+ * over none, cannot be routed by accident: it spawns the base arguments
284
+ * and its setting sources with no routing flag. `settingSources` has
285
+ * no default; see the module note.
286
+ *
287
+ * `served` is the flags handing a served directory over, which
288
+ * {@link claudeArgs} places between the setting sources and `flags`. It
289
+ * defaults to empty, and the CI repair hands none. `options` is where
290
+ * the session runs, handed to the spawner as it is; the CI repair names
291
+ * the run's checkout there (`start/pr-lifecycle.ts`). Both come last so
292
+ * that no call written before they existed has to change.
293
+ */
294
+ export declare function runClaude(prompt: string, settingSources: readonly ClaudeSettingSource[], flags?: readonly string[], spawn?: ClaudeSpawner, served?: readonly string[], options?: CapturedSpawnOptions): Promise<number>;
295
+ /**
296
+ * What a captured session answers: its exit code, and everything it
297
+ * wrote to stdout, decoded as UTF-8.
298
+ */
299
+ export interface CapturedSession {
300
+ readonly exitCode: number;
301
+ readonly stdout: string;
302
+ }
303
+ /**
304
+ * Where a captured session runs. Every field is optional, and an empty
305
+ * object spawns exactly what no options spawn.
306
+ */
307
+ export interface CapturedSpawnOptions {
308
+ /**
309
+ * The session's working directory, or the loop's own when absent. The
310
+ * search session (`src/inventory/search/index.ts`) names the scratch
311
+ * copy of its candidates, each check session of the epic verification
312
+ * run (`src/epic/verify-run.ts`) a detached worktree of main, the plan
313
+ * session of the `claude` planner (`src/adapters/planner/claude.ts`)
314
+ * the project root it writes the plan under, and the loop's task,
315
+ * wrap-up and CI repair sessions the run's checkout
316
+ * (`src/start/checkout.ts`).
317
+ */
318
+ readonly cwd?: string;
319
+ }
320
+ /**
321
+ * Runs `claude` with an argument list and a prompt on stdin, and
322
+ * answers the exit code together with the session's stdout.
323
+ *
324
+ * A seam of its own rather than a widened {@link ClaudeSpawner}:
325
+ * widening that one would change what every existing `runClaude`
326
+ * double has to answer, for sessions whose output nothing reads. The
327
+ * third parameter is optional for the same reason: a double written
328
+ * for `(args, prompt)` is still a `CapturingSpawner`.
329
+ */
330
+ export type CapturingSpawner = (args: readonly string[], prompt: string, options?: CapturedSpawnOptions) => Promise<CapturedSession>;
331
+ /**
332
+ * The capturing spawner: `Bun.spawn` with stdout PIPED, each chunk
333
+ * written on to the operator's stdout as it arrives and kept for the
334
+ * answer.
335
+ *
336
+ * Everything else matches {@link spawnClaude}: the executable, the
337
+ * prompt on stdin, the environment. The working directory is the
338
+ * loop's own unless `options.cwd` names another, as it is for
339
+ * {@link spawnClaude}. Stderr stays inherited, so the
340
+ * operator sees it and the capture never holds it, and a report parsed
341
+ * out of `stdout` cannot have been a warning line.
342
+ *
343
+ * The bytes go through ONE streaming `TextDecoder`, never a decode per
344
+ * chunk, because a pipe read can end inside a character. Measured on
345
+ * bun 1.3.14, a `€` written in two halves arrived as the chunks
346
+ * `78 e2` and `82 ac 79`; decoded chunk by chunk that is three U+FFFD
347
+ * replacement characters where the streaming decoder answers the `€`.
348
+ * The decoder is not `fatal`, so bytes that are not UTF-8 at all
349
+ * become U+FFFD rather than a rejection.
350
+ *
351
+ * The answer waits for stdout to CLOSE as well as for the exit, which
352
+ * is what reading a pipe to its end means: a process the session left
353
+ * behind that still holds the pipe holds the answer too. Measured, a
354
+ * shell that backgrounded a `sleep 1.5` exited at 17ms and closed its
355
+ * stdout at 1,530ms.
356
+ *
357
+ * A signalled session needs no fallback here. Measured on bun 1.3.14,
358
+ * `exited` answers 128 plus the signal number (137 for a SIGKILL)
359
+ * while `exitCode` is null, so a killed session is already a non-zero,
360
+ * which every caller reads as a failure.
361
+ *
362
+ * When a write to the operator throws, the call rejects with that
363
+ * error, but only once the session has EXITED. Stopping the read does
364
+ * not stop the process: measured, a shell writing into a pipe whose
365
+ * reader had quit kept running, each write failing with `Broken pipe`,
366
+ * until it was killed 3s later. Rejecting at once would hand the loop
367
+ * back a tree that a session is still changing.
368
+ *
369
+ * Warns, and spawns anyway, when the running command's `spends` does not
370
+ * cover the run; see the module note.
371
+ */
372
+ export declare function spawnClaudeCaptured(args: readonly string[], prompt: string, options?: CapturedSpawnOptions): Promise<CapturedSession>;
373
+ /**
374
+ * Spawns one Claude session with `prompt` on stdin, and answers its
375
+ * exit code together with everything it wrote to stdout.
376
+ *
377
+ * The argument list is the one {@link runClaude} builds for the same
378
+ * setting sources and flags, both going through {@link claudeArgs}, so
379
+ * capturing a session changes where its stdout goes and nothing about
380
+ * what is run. The operator still sees that output as it is written,
381
+ * through the tee in {@link spawnClaudeCaptured}.
382
+ *
383
+ * `served` and `options` are taken as {@link runClaude} takes them. The
384
+ * task session (`runTaskSession` in `start/dispatch.ts`) and the wrap-up
385
+ * (`start/wrap-up.ts`) are the two callers that hand any, and both name
386
+ * the run's checkout as the session's working directory.
387
+ */
388
+ export declare function runClaudeCaptured(prompt: string, settingSources: readonly ClaudeSettingSource[], flags?: readonly string[], spawn?: CapturingSpawner, served?: readonly string[], options?: CapturedSpawnOptions): Promise<CapturedSession>;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The shapes of the hop record and of a place, with the checks that read
3
+ * them from parsed JSON: the part of `src/next/hop-record.ts` and
4
+ * `src/project/position.ts` a session record needs (`sessions.ts` keeps a
5
+ * hop record in its `hop` field).
6
+ *
7
+ * It sits in core so `sessions.ts` reads them without importing above
8
+ * core. `src/project/position.ts` re-exports `Place` and `asPlace`, and
9
+ * `src/next/hop-record.ts` re-exports `HopKind`, `HopState`, `HopRecord`
10
+ * and `asHopRecord`, each under the same name; the reading and writing of
11
+ * `.rafa/position.json` and `.rafa/hop.json`, and the module notes that
12
+ * describe both files, stay there. `isRecord` is exported for the
13
+ * position's own check in `position.ts`.
14
+ *
15
+ * The file imports nothing.
16
+ */
17
+ /** A (board, epic) pair of issue numbers; `epic` is null on a board with no epic chosen. */
18
+ export interface Place {
19
+ readonly board: number;
20
+ readonly epic: number | null;
21
+ }
22
+ /** Why the hop went: H's blocker, or a home epic that ran dry. */
23
+ export type HopKind = 'blocker' | 'dry';
24
+ /** Where the hop stands: working its target, back home waiting on it or with it merged, or back home from a halt. */
25
+ export type HopState = 'away' | 'waiting' | 'merged' | 'halted';
26
+ /** The record `.rafa/hop.json` holds; see the module note of `src/next/hop-record.ts`. */
27
+ export interface HopRecord {
28
+ readonly kind: HopKind;
29
+ readonly home: Place;
30
+ readonly from: Place;
31
+ /** H, the home issue its blocker stopped; null on a dry hop. */
32
+ readonly blocked: number | null;
33
+ /** C, H's blocker the hop works; null on a dry hop. */
34
+ readonly target: number | null;
35
+ readonly targetEpic: number;
36
+ readonly targetBoard: number;
37
+ readonly state: HopState;
38
+ /** The target's pull request, null before it is opened. */
39
+ readonly pullRequest: number | null;
40
+ /** ISO 8601. */
41
+ readonly startedAt: string;
42
+ }
43
+ /** True for a plain object. */
44
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
45
+ /** `value` as a place, or null when it is not one; the hop record reads its places through it. */
46
+ export declare function asPlace(value: unknown): Place | null;
47
+ /**
48
+ * `value` as a hop record, or null when it is not one: the check
49
+ * `readHopRecord` (`src/next/hop-record.ts`) makes on the parsed file,
50
+ * which the session record makes on its `hop` field (`sessions.ts`).
51
+ */
52
+ export declare function asHopRecord(value: unknown): HopRecord | null;
@@ -0,0 +1,179 @@
1
+ /**
2
+ * The barrel over the loop's session records and its doors onto the
3
+ * Claude CLI: every name `sessions.ts`, `claude.ts`, `session-env.ts` and
4
+ * `suite-failure.ts` export, and nothing else.
5
+ *
6
+ * A file outside `packages/rafa/src/core/` imports this file and never the two behind
7
+ * it (`scripts/survey/core-barrels.ts` measures that, and
8
+ * `src/tests/core-barrels.sweep.test.ts` holds it against the tree). A
9
+ * file inside `packages/rafa/src/core/` imports the two directly and never this file:
10
+ * `session-readings.ts` and the six test files of `sessions.ts` import
11
+ * that file, and `packages/rafa/src/core/adapters/registry.ts` and
12
+ * `claude.test.ts` import `claude.ts`. `index.test.ts`, this file's own
13
+ * test, is the one file of `packages/rafa/src/core/` that imports it.
14
+ *
15
+ * ## What sits behind it
16
+ *
17
+ * - `sessions.ts`: the session records of `loop start`, one JSON file
18
+ * per run under `<root>/.rafa/runs/`, with their reads, the state a
19
+ * record reads as, the refusal of a second run of a plan, and the
20
+ * writes;
21
+ * - `claude.ts`: the two doors a Claude session is spawned through,
22
+ * the argument list both build, the spawners behind them, the
23
+ * interrupt of the sessions still running, and the spend guard's
24
+ * sentence;
25
+ * - `session-env.ts`: the environment a Claude session is spawned
26
+ * with, which `claude.ts` imports by path;
27
+ * - `suite-failure.ts`: the type of one failing test, which
28
+ * `sessions.ts` imports by path and `src/suite/run.ts` re-exports.
29
+ *
30
+ * The four files share no name, and only `sessions.ts` imports
31
+ * `suite-failure.ts`, for a type. One
32
+ * import of this barrel loads both: a file that takes `isPidAlive`
33
+ * alone loads `claude.ts` with it.
34
+ *
35
+ * The reading of one record is `session-record-parse.ts`, beside
36
+ * `sessions.ts`, which imports it by path: the sets a record's fields
37
+ * are checked against, `parseSessionRecord`, `SessionRecordError` and
38
+ * the readers of the optional fields. It was
39
+ * `src/loop/session-record-parse.ts` until it moved here, and a
40
+ * re-export stays at that path. `sessions.ts` re-exports what a caller
41
+ * of a record reads from there, four types and thirteen runtime names,
42
+ * and this barrel carries them with the rest, so the parser takes no
43
+ * line of its own here. So a file outside `packages/rafa/src/core/` that reads a
44
+ * record needs this one import, as it needed `sessions.ts` alone before
45
+ * the move.
46
+ *
47
+ * ## Why `claude.ts` is here
48
+ *
49
+ * A barrel is split when it would close a runtime cycle, and never
50
+ * otherwise. `claude.ts` was the third file to move into this folder,
51
+ * and it closes none behind this barrel, so it took no barrel of its
52
+ * own. The section "Why the barrel closes no cycle" holds the
53
+ * measurement.
54
+ *
55
+ * ## Why the session readings are not here
56
+ *
57
+ * `packages/rafa/src/core/loop/` has three barrels, all listed under `CORE_BARRELS` in
58
+ * `scripts/survey/core-moves.ts`. `session-readings.ts`, the second
59
+ * file that moved here, sits alone behind `readings.ts`. It reaches
60
+ * `packages/rafa/src/effort/store/development-build.ts` by value, through
61
+ * `packages/rafa/src/effort/store/task-finishes.ts`, and that file imports three
62
+ * runtime names from this barrel. With `session-readings.ts` behind
63
+ * this barrel the store would import this file, and this file the
64
+ * store: a cycle of six files. `readings.ts` records the measurement,
65
+ * and `readings.test.ts` holds that no name is on both barrels.
66
+ *
67
+ * ## Why the hop record is not here
68
+ *
69
+ * `hop-record.ts`, the shapes of the hop record and of a place, sits
70
+ * alone behind `hops.ts`. `src/next/hop-record.ts` and
71
+ * `src/project/position.ts` re-export its names, so they import a
72
+ * barrel over it by value, and `src/loop/session-record-parse.ts`,
73
+ * which `sessions.ts` imported by value, imported `asHopRecord` from
74
+ * `src/next/hop-record.ts` by value when the split was made; it has
75
+ * since moved here and imports `hop-record.ts` by path. With `hop-record.ts` behind this
76
+ * barrel that is a cycle of four files: this one, `sessions.ts`, the
77
+ * parser and `src/next/hop-record.ts`. `hops.ts` records the
78
+ * measurement, and `hops.test.ts` holds that no name is on two loop
79
+ * barrels. `sessions.ts` imports `hop-record.ts` by path, for
80
+ * `HopRecord`.
81
+ *
82
+ * ## Why the barrel closes no cycle
83
+ *
84
+ * A barrel that groups a file with a file outside `packages/rafa/src/core/` that
85
+ * imports the barrel back by value is a runtime cycle. `sessions.ts`
86
+ * imported three files by value when it moved:
87
+ * `packages/rafa/src/core/config/config-sections.ts`, for `messageOf`, and two outside
88
+ * `packages/rafa/src/core/`, `src/loop/session-record-parse.ts` and
89
+ * `src/project/scope.ts`. Through them it reached 38 files by value, and
90
+ * none of the 38 imported it by value. Both have since moved into
91
+ * `packages/rafa/src/core/`, as `session-record-parse.ts` beside it and
92
+ * `packages/rafa/src/core/config/scope.ts`.
93
+ *
94
+ * The parser, then `src/loop/session-record-parse.ts`, imported this
95
+ * barrel for the record's interfaces with `import type` alone, which is
96
+ * erased before anything loads; since it moved here as
97
+ * `session-record-parse.ts` it imports `sessions.ts` by path, the same
98
+ * way. Its own note says why its sets, its error and its id check live
99
+ * on its side: so that it takes no value from `sessions.ts`. That rule
100
+ * holds this barrel as well.
101
+ *
102
+ * Measured when the file moved: the value imports of the non-test files
103
+ * under `src/` and `packages/` held six cycles before the move and the
104
+ * same six after it, none through a file of this folder. The control,
105
+ * the same tree with `src/loop/session-record-parse.ts` taking one
106
+ * function (`errorCode`) from this barrel and calling it inside a
107
+ * function, held a seventh: this file, `sessions.ts` and that one.
108
+ *
109
+ * That seventh loaded in the three orders tried: this file,
110
+ * `sessions.ts` and `src/loop/session-record-parse.ts` each imported
111
+ * first in a fresh `bun` process, then one record parsed through this
112
+ * barrel. A second control took a `const` (`RUNS_DIR`) instead and read
113
+ * it at the top level of `src/loop/session-record-parse.ts`. It loaded
114
+ * when that file came first, and failed with
115
+ * `ReferenceError: Cannot access 'RUNS_DIR' before initialization.`
116
+ * when this file or `sessions.ts` did. The barrel holds the rule rather
117
+ * than the first reading.
118
+ *
119
+ * `src/tests/import-cycle.sweep.test.ts` would have refused both
120
+ * controls. It fails on a cycle across two clusters of
121
+ * `docs/survey/cluster-map.json`, which places `sessions.ts` in c2 and
122
+ * `src/loop/session-record-parse.ts` in c3; this file, which the map
123
+ * does not name, lands in c2 by its folder. Read the way the sweep
124
+ * reads the graph, each control held one cross-cluster cycle and the
125
+ * tree as moved held none.
126
+ *
127
+ * `claude.ts` imports four files by value: three of `packages/rafa/src/core/`,
128
+ * `packages/rafa/src/core/command/command.ts` for `commandSpelling`,
129
+ * `packages/rafa/src/core/command/running.ts` for the running command and
130
+ * `packages/rafa/src/core/output/active.ts` for the active output, and one outside
131
+ * it, `src/utils/session-env.ts`, which has since moved here as
132
+ * `session-env.ts`. When it moved, `running.ts` was still
133
+ * `src/cli/running.ts`; through the four it reached 13 files by value, and none of the 13
134
+ * imported this barrel or `claude.ts` by value. None of the 38 files
135
+ * `sessions.ts` reached did either. Eleven non-test files imported
136
+ * `claude.ts` by value before the move and import this barrel since.
137
+ *
138
+ * Measured when `claude.ts` moved: the same six cycles before the move
139
+ * and after it, none through a file of this folder, with this barrel
140
+ * reaching 46 files by value. The control, the same tree with
141
+ * `src/utils/session-env.ts` taking `CLAUDE_BIN` from this barrel and
142
+ * returning it from a function, held a seventh of three files: this
143
+ * file, `claude.ts` and that one.
144
+ *
145
+ * That seventh loaded in the three orders tried: each of the three
146
+ * files imported first in a fresh `bun` process, then `claudeArgs`
147
+ * called and `CLAUDE_BIN` and `RUNS_DIR` read through this barrel. A
148
+ * second control read `CLAUDE_BIN` at the top level of
149
+ * `src/utils/session-env.ts` instead. It loaded when that file came
150
+ * first, and failed with
151
+ * `ReferenceError: Cannot access 'CLAUDE_BIN' before initialization.`
152
+ * when this file or `claude.ts` did.
153
+ *
154
+ * The map places `claude.ts` in c3 and `src/utils/session-env.ts` in
155
+ * c4. The folder's two mapped files now tie, one in c2 and one in c3,
156
+ * and a tie goes to the name that sorts first, so this file still lands
157
+ * in c2. Read the way the sweep reads the graph, the first control held
158
+ * one cross-cluster cycle, c2 with c3 and c4, and the tree as moved
159
+ * held none.
160
+ *
161
+ * ## What it exports
162
+ *
163
+ * Each name is spelled, types apart from values, as the other core
164
+ * barrels spell theirs. No name here is part of the published surface:
165
+ * `src/index.ts` re-exports nothing from this file.
166
+ *
167
+ * `index.test.ts` holds the list to the four files: every name each
168
+ * exports is here once, as the same binding, the first three share no name, the
169
+ * names `sessions.ts` re-exports are the bindings
170
+ * `session-record-parse.ts` declares, and no other name is
171
+ * here.
172
+ */
173
+ export type { PidProbe, SessionChange, SessionConflict, SessionConflictReason, SessionDecision, SessionDraft, SessionPhase, SessionReadSeams, SessionRecord, SessionState, SessionStep, SessionStepKind, SessionStepOnly, SessionStepReason, SessionTask, SessionTaskRef, } from './sessions.js';
174
+ export type { CapturedSession, CapturedSpawnOptions, CapturingSpawner, ClaudeSpawner } from './claude.js';
175
+ export type { SpawnEnv } from './session-env.js';
176
+ export type { SuiteFailure } from './suite-failure.js';
177
+ export { beginSession, errorCode, isPidAlive, isPositiveWhole, isSessionId, parseSessionRecord, readSession, readSessions, readState, RECORD_EXTENSION, RUNS_DIR, runsDir, samePlan, SESSION_DECISION_STRATEGIES, SESSION_PHASES, SESSION_STATES, SESSION_STEP_KINDS, SESSION_STEP_REASONS, SessionConflictError, sessionConflicts, sessionDecisions, sessionFilePath, sessionPhase, SessionRecordError, SessionStateError, sessionSteps, updateSession, } from './sessions.js';
178
+ export { carriesFlag, CLAUDE_BASE_ARGS, CLAUDE_BIN, claudeArgs, interruptClaudeSessions, runClaude, runClaudeCaptured, SETTING_SOURCES_FLAG, spawnClaude, spawnClaudeCaptured, spendWarning, } from './claude.js';
179
+ export { sessionPath, sessionSpawnEnv } from './session-env.js';
@@ -0,0 +1,20 @@
1
+ /** An environment as `process.env` and `Bun.spawn` spell it. */
2
+ export type SpawnEnv = Record<string, string | undefined>;
3
+ /**
4
+ * `base`'s `PATH` with `binDir` in front, or `binDir` alone for a base
5
+ * with no `PATH` or an empty one. See "`bundled/bin` goes first on
6
+ * `PATH`".
7
+ */
8
+ export declare function sessionPath(base: string | undefined, binDir: string): string;
9
+ /**
10
+ * The environment every session is spawned with: `base`, plus the two
11
+ * entries a session must see whichever spawner started it.
12
+ *
13
+ * `PATH` is `binDir`, the running entry's `bundled/bin` unless another
14
+ * is handed, in front of `base`'s own ({@link sessionPath}).
15
+ * `CLAUDE_CODE_ENTRYPOINT` is set to `cli` over whatever `base` holds
16
+ * for it, so a shell that already carries another value still spawns
17
+ * `cli`. Every other entry of `base` is handed on as it is, an entry
18
+ * holding `undefined` included.
19
+ */
20
+ export declare function sessionSpawnEnv(base: Readonly<SpawnEnv>, binDir?: string): SpawnEnv;