bareloop 0.28.0 → 0.30.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.
- package/CHANGELOG.md +115 -0
- package/bareloop.context.md +79 -0
- package/package.json +1 -1
- package/src/authorrun.js +9 -1
- package/src/behaviour.js +5 -2
- package/src/cli.js +72 -3
- package/src/panel/index.html +2156 -0
- package/src/panel/server.js +1583 -0
- package/src/replay.js +678 -13
- package/src/replayio.js +7 -1
- package/src/runlist.js +281 -0
- package/src/userrun.js +26 -0
- package/types/behaviour.d.ts +5 -3
- package/types/cli.d.ts +2 -1
- package/types/panel/server.d.ts +265 -0
- package/types/replay.d.ts +104 -6
- package/types/runlist.d.ts +112 -0
- package/types/userrun.d.ts +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,121 @@ All notable changes to bareloop are documented here. Format:
|
|
|
5
5
|
[SemVer](https://semver.org/spec/v2.0.0.html). Pre-1.0: **minor** = a ladder rung or
|
|
6
6
|
feature lands, **patch** = docs, fixes, scaffolding.
|
|
7
7
|
|
|
8
|
+
## [0.30.0] — 2026-09-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Panel step map draws the retry loop** — a multi-attempt step (and the post-step fix loop)
|
|
13
|
+
now renders its "try N" curve and label on the map, alongside a pulsing amber dot on the
|
|
14
|
+
live-running box (same geometry as `design/panel-mockup.html`).
|
|
15
|
+
- **Runs list and open run poll live** — a single poll owner refreshes the Runs list every
|
|
16
|
+
tick and the open run's own detail while it is still live (`▶`), stopping once it dies
|
|
17
|
+
(`?`) or gets a real `✓`/`✗` verdict; polling pauses on a hidden tab and catches up on
|
|
18
|
+
return. Re-renders are skipped when the fetched payload is unchanged, and scroll position
|
|
19
|
+
is preserved across poll-driven rebuilds.
|
|
20
|
+
- `scripts/replay-live.mjs` (**dev-only, not shipped** — excluded from the published
|
|
21
|
+
package) paces an archived spine into a live-looking run for panel development and
|
|
22
|
+
screenshot proof, without touching the source spine or `~/.config/bareloop`.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **Job tab field order**: Check type, Model, `$` cap / Time cap, then the rest — moved per
|
|
27
|
+
hamr's ruling.
|
|
28
|
+
- Job tab no longer shows a separate Job name field; the run's own card title already
|
|
29
|
+
carries it.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- The retry loop's "try N" curve and label now stay inside their own step box at every
|
|
34
|
+
width, including narrow/mobile widths (previously could render past the box's left edge
|
|
35
|
+
at widths like 350px).
|
|
36
|
+
- An open (still-running) attempt on a live run now reads `▶` everywhere — map, part cards,
|
|
37
|
+
and Audit tab — never the died `?`, which is reserved for a genuinely died attempt.
|
|
38
|
+
- The `[▶]` glyph now pulses for a live run everywhere it appears (runs list rows,
|
|
39
|
+
Workflows, the run header).
|
|
40
|
+
- A running step's own in-progress attempt no longer leaks into a phantom fix-loop part on
|
|
41
|
+
the map.
|
|
42
|
+
- A live fix-loop attempt whose window is still open now reports its outcome as unknown
|
|
43
|
+
rather than a fabricated red, so the map correctly shows it as running (with its pulsing
|
|
44
|
+
dot) instead of prematurely stopped.
|
|
45
|
+
- A live run's Audit tab and tool-call/cache summary now fall back to the during-run
|
|
46
|
+
tree/worktree gate-audit path, so tool calls are visible while a run is still in progress,
|
|
47
|
+
not only after it finishes.
|
|
48
|
+
|
|
49
|
+
### Notes
|
|
50
|
+
|
|
51
|
+
- F195 (open): `/api/runs` re-replays every archived spine on every call (~457ms CPU at 250
|
|
52
|
+
rows, no per-row cache). Mitigated in this release by throttling the Runs list poll to
|
|
53
|
+
10s; the root cause is still open.
|
|
54
|
+
- F196 (parked): a finished bundle run's tool log can be unreadable due to a sidecar naming
|
|
55
|
+
mismatch between `spine.jsonl` and its renamed gate-audit sidecar. Not fixed in this
|
|
56
|
+
release.
|
|
57
|
+
|
|
58
|
+
## [0.29.0] — 2026-09-26
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
|
|
62
|
+
- **`bareloop` panel (N6/P0/P1)** — a read-only HTTP server (`bareloop panel`, bound to
|
|
63
|
+
`127.0.0.1:4700`) serving a single-page app built from one run list
|
|
64
|
+
(`~/.config/bareloop/runs.jsonl`, populated by `bareloop run`/`bareloop runs backfill`).
|
|
65
|
+
The page reads the library's own `replayRun`/spine/gate-audit functions directly, in-process
|
|
66
|
+
— the same read side `bareloop replay` uses, never a duplicated parser.
|
|
67
|
+
- **Run tab: step-map + per-part cards.** A plan-shape run renders one two-line card per PART
|
|
68
|
+
(scout, plan, each step, each replan, the post-step fix loop, judge when recorded), each
|
|
69
|
+
clickable through to its own slice of the Audit tab; an old-shape (iterations, no
|
|
70
|
+
step-start) run collapses to one "run" card.
|
|
71
|
+
- **Audit tab: Grouped (part → attempt → rounds table) and Flat views**, a search box
|
|
72
|
+
(matches job/runid/model), and All/Writes/Blocked filter chips that auto-open every
|
|
73
|
+
matching group.
|
|
74
|
+
- **Job tab** resolves the real spec (bundle `spec.json`, `jobs/<job>.json`, or the run's own
|
|
75
|
+
job-start record) and shows Success/Guardrails/Tools/Judge-examples fields plus the full
|
|
76
|
+
granted-tools "offered" list.
|
|
77
|
+
- **Run Summary** gained `model` (with provider) and `judge` lines, and a died glyph `[?]`
|
|
78
|
+
distinct from a real failed verdict `[✗]`.
|
|
79
|
+
|
|
80
|
+
### Changed
|
|
81
|
+
|
|
82
|
+
- **Workflows and History merged into one `Runs` tab**, toggled between a grouped-by-job
|
|
83
|
+
"Workflows" view (default) and a flat "History" view — both built from the one `/api/runs`
|
|
84
|
+
list; `/api/workflows` and the client's `listWorkflows` were deleted.
|
|
85
|
+
- Audit-tab paths shorten to the run's own resolved tree root (`pathShort`) when one can be
|
|
86
|
+
found; an older run with no resolvable root keeps its full absolute path.
|
|
87
|
+
|
|
88
|
+
### Fixed
|
|
89
|
+
|
|
90
|
+
- **`bareloop replay`'s tool-call count was contaminated by a shared gate-audit sidecar** — a
|
|
91
|
+
sidecar file can carry rows from several runs that happen to reuse the same filename;
|
|
92
|
+
`auditWindow` now scopes every reader (CLI and panel alike) to the run's own
|
|
93
|
+
`job-start`..`job-end` window, so the two can never disagree.
|
|
94
|
+
- **Panel tests bound a fixed port, flaking under a taken port** — every test now asks the OS
|
|
95
|
+
for an ephemeral port (`port: 0`) instead of guessing a free one.
|
|
96
|
+
- Audit tab's "Raw log" leaked an EARLIER unrelated run's rows when a gate-audit sidecar is
|
|
97
|
+
shared across runs; it now shows only this run's own ts-windowed lines, byte-for-byte.
|
|
98
|
+
- A `~2`-style backfill-collision runid (two archived spines sharing a basename in different
|
|
99
|
+
directories) echoed the wrong, unsuffixed runid in the run header and its sibling lookups.
|
|
100
|
+
- `toolCalls`/`behaviour` report `null` (unknown), never a fabricated `0`, when a run has no
|
|
101
|
+
gate-audit sidecar at all.
|
|
102
|
+
- Backfill scans recursively (bounded depth) instead of the top directory plus its immediate
|
|
103
|
+
subdirectories only, and dedups by resolved spine path rather than a filename-derived runid
|
|
104
|
+
(two same-basename spines in different directories are different runs, not duplicates).
|
|
105
|
+
- **Workflows job (parent) row is its latest run**: clicking it opens that run (previously a
|
|
106
|
+
dead click after the Runs-tab merge); the expanded child list shows only OLDER runs, never
|
|
107
|
+
repeating the latest as its own first child; the expand caret appears only when a matching
|
|
108
|
+
older run exists under the active filters/search (not just `runCount > 1`); and the row's
|
|
109
|
+
"selected" state and click target both follow whichever run is actually active
|
|
110
|
+
(`representedRun`) — so a search hit or a History pick on an older run correctly expands
|
|
111
|
+
and highlights that run's job, instead of always marking/opening the job's latest.
|
|
112
|
+
- Various Workflows/History/Audit rendering fixes: rows stay two lines and never wrap; the
|
|
113
|
+
meta-line separator carries a leading space; a died run's "why" line uses one plain
|
|
114
|
+
timestamp format; a run that died before its first step reads "0 of 0 done" (never "0 of
|
|
115
|
+
1"); the Audit tab distinguishes "no sidecar ever written" from "sidecar exists but empty";
|
|
116
|
+
History's filter chips no longer needed two clicks to register; the desktop list scrolls in
|
|
117
|
+
its own pane and mobile gets a "back to list" affordance; check-type labelling correctly
|
|
118
|
+
falls back to "deterministic" for runs that predate `verdictType` (2026-08-18); no-verdict
|
|
119
|
+
parts (scout/plan/replan/judge) render no result glyph rather than a guessed one; the Run
|
|
120
|
+
tab's step-map numbering and card order matched (F197/F198); Summary's "offered" line shows
|
|
121
|
+
the FULL granted-tools list, never truncated to the top few.
|
|
122
|
+
|
|
8
123
|
## [0.28.0] — 2026-09-24
|
|
9
124
|
|
|
10
125
|
### Added
|
package/bareloop.context.md
CHANGED
|
@@ -3186,6 +3186,85 @@ interviews of their own. All three are dispatched by name only: `bareloop run-u
|
|
|
3186
3186
|
this command reports, it never grades), `1` on a missing file/directory or a non-spine
|
|
3187
3187
|
file, `1` with a usage line on no args.
|
|
3188
3188
|
|
|
3189
|
+
- **`bareloop runs`** / **`bareloop runs backfill <dir>`** (PANEL-BUILD.md P1, 2026-09-24
|
|
3190
|
+
rulings — "one home for runs", hamr's option B) → the one run list, at `~/.config/bareloop/
|
|
3191
|
+
runs.jsonl` (the same directory the keys file, PRD §7d, lives in — outside any repo tree,
|
|
3192
|
+
never moved into one). This is a LIST, not a relocation: patient copies stay exactly where
|
|
3193
|
+
they already are (`bareloop-patients/…` for `run-u`, `<bundleDir>/runs/<runid>/` for
|
|
3194
|
+
`bareloop run`); jobs stay in `jobs/` (moving them into the home too is deferred to P3,
|
|
3195
|
+
hamr picked option "A"). `bareloop runs` with no subcommand prints every listed row, one
|
|
3196
|
+
line each — `job (runid) · date · spine path`, with ` — file missing` appended when the
|
|
3197
|
+
row's `spine` no longer exists on disk. `bareloop runs backfill <dir>` scans `dir` and its
|
|
3198
|
+
immediate subdirectories for archived spines already on disk (both the free-standing
|
|
3199
|
+
layout, e.g. `<dir>/<x>/u-<id>.jsonl`, and the bundle layout, `<dir>/<x>/runs/<runid>/
|
|
3200
|
+
spine.jsonl`), reusing `src/replayio.js`'s `parseJsonl`/`isSidecarByName`/`looksLikeSpine`
|
|
3201
|
+
(never a second parser), and adds one row per spine whose runid is not already listed —
|
|
3202
|
+
idempotent, running it twice adds nothing the second time. Prints `added N, already listed
|
|
3203
|
+
M, skipped K (not a spine or unreadable)` — never silent about what it found. Read-only,
|
|
3204
|
+
$0: no interview, no author, no run trigger, no key ever read. A row is `{ at, runid, job,
|
|
3205
|
+
spine, patient, via }` — `spine`/`patient` are always absolute paths (`patient: null` when
|
|
3206
|
+
not known, e.g. every `via:"backfill"` row: no spine record carries a run's workdir);
|
|
3207
|
+
`via` is `'run-u'`, `'bundle'`, or `'backfill'`. `bareloop run-u` and `bareloop run` (the
|
|
3208
|
+
bundle path) each append their own row at run START, before the first paid call — a
|
|
3209
|
+
list-append failure is caught at both call sites and printed loudly to stderr
|
|
3210
|
+
(`WARNING: could not add this run to ~/.config/bareloop/runs.jsonl (…)`); the run itself
|
|
3211
|
+
always continues (a panel list must never block real, already-signed work). Interview/
|
|
3212
|
+
author sessions are NOT added (deferred to P3 — nothing to list at that stage yet). These
|
|
3213
|
+
functions (`appendRun`/`readRunList`/`backfillRuns`/`formatRunRow`, `src/runlist.js`) are
|
|
3214
|
+
NOT exported from the package root — the panel's HTTP server (P1's next task) will call
|
|
3215
|
+
them the same in-process way `src/cli.js` does, per the layering law
|
|
3216
|
+
(`docs/product/PANEL-BUILD.md` §2), not through `bareloop`'s public API.
|
|
3217
|
+
|
|
3218
|
+
- **`bareloop panel [--port N]`** (PANEL-BUILD.md P1) → a READ-ONLY `node:http` server
|
|
3219
|
+
(no new dependency), bound to `127.0.0.1` ONLY, default port `4700`. Serves the panel's
|
|
3220
|
+
look (Tokyo Night box-drawing TUI style) over the run list's real data: `GET /` the page;
|
|
3221
|
+
`GET /api/runs` (newest first, each row enriched with a glyph/check-type/spend/wall
|
|
3222
|
+
summary, `fileMissing:true` when the listed spine is gone — the client groups this SAME
|
|
3223
|
+
list by job for the left pane's "Workflows" view and renders it flat for "History", both
|
|
3224
|
+
behind one `Runs` tab toggle; there is no separate `/api/workflows` endpoint, deleted in
|
|
3225
|
+
the P1 Runs-tab merge); `GET /api/runs/:runid` (the full replay — parts/steps, counters,
|
|
3226
|
+
summary, model/provider/judge); `GET /api/runs/:runid/audit` (the gate-audit sidecar rows,
|
|
3227
|
+
ts-windowed to this run alone even when the sidecar file is shared with other runs, plus
|
|
3228
|
+
the matching raw-log text windowed the same way — when one resolves); `GET
|
|
3229
|
+
/api/runs/:runid/rounds?part=&attempt=&offset=&limit=` (one attempt's rounds, lazily
|
|
3230
|
+
paged, for the Audit tab's Grouped view); `GET /api/runs/:runid/job` (the signed spec's
|
|
3231
|
+
own fields, ONLY when a bundle-layout run's `spec.json` is reachable — a run-u run has
|
|
3232
|
+
none on disk, and this reads `resolved:false` with every spec-only field honestly
|
|
3233
|
+
`'unknown'`, never guessed from the spine's own narrower `job-start` record). Every
|
|
3234
|
+
endpoint is GET/HEAD only (anything else — including every write verb — is `405`); a URL
|
|
3235
|
+
never joins a path segment into a filesystem read — a runid is looked up in the run list
|
|
3236
|
+
first (`RUNID_RE`, `src/panel/server.js` — accepts a `~2`-style backfill-disambiguated
|
|
3237
|
+
suffix too, and every endpoint echoes back the LISTED runid, never a filename-derived one
|
|
3238
|
+
that can silently drop that suffix), and only the path THAT ROW stores is ever read. A
|
|
3239
|
+
taken port fails loudly (names the port, exit `1`) — this command never silently tries a
|
|
3240
|
+
different one. No interview, no author, no run trigger, no key/`.env` ever read on this
|
|
3241
|
+
path. One more caller of `src/replayio.js`/`src/runlist.js`, in-process, per the layering
|
|
3242
|
+
law (`docs/product/PANEL-BUILD.md` §2) — the panel's HTTP handler holds no flow logic of
|
|
3243
|
+
its own. Chat/Settings/authoring (P3/P4) are not built yet; the page says so rather than
|
|
3244
|
+
hiding the gap.
|
|
3245
|
+
|
|
3246
|
+
- **Panel P2 (live run view, `docs/product/PANEL-BUILD.md` Addendum 2026-09-27)** → the page
|
|
3247
|
+
polls, it does not push (no SSE/websocket). One `setInterval` (2s) drives two throttled
|
|
3248
|
+
fetches: the OPEN run's own detail (`GET /api/runs/:runid`) refetches every 2s while its
|
|
3249
|
+
glyph is live `▶`, and stops the instant it reaches a real `✓`/`✗` or dies `?`; the left
|
|
3250
|
+
Runs list refetches on a separate 10s throttle inside the same tick (`RUNS_LIST_POLL_MS`,
|
|
3251
|
+
lowered from the original 2s per F195 — `listRuns()` re-replays every archived spine on
|
|
3252
|
+
every call, ~457ms CPU at 250 rows, open/escalated), except the tick right after the open
|
|
3253
|
+
run stops being live, which forces one immediate list refresh so that row's glyph doesn't
|
|
3254
|
+
wait up to 10s. Both fetches skip re-rendering when the payload's JSON is byte-identical to
|
|
3255
|
+
the last one, restore each pane's scroll position across a poll-driven rebuild, and pause
|
|
3256
|
+
while the tab is hidden (`document.hidden`), catching up immediately on
|
|
3257
|
+
`visibilitychange`. The Audit tab does **not** join this loop — it refetches only when
|
|
3258
|
+
opened/switched to. A live run's gate-audit sidecar (before `run-u`/`bareloop run`'s
|
|
3259
|
+
end-of-run rename) is read from its DURING-RUN path (`<row.patient>/gate-audit.jsonl`) via
|
|
3260
|
+
`resolveAuditPathForRow` (`src/panel/server.js`), the one shared owner the Run tab's tools
|
|
3261
|
+
summary and the Audit tab both read through — tried only while the run's own spine has no
|
|
3262
|
+
`job-end` yet, so a finished run never risks reading a stale leftover file at that path.
|
|
3263
|
+
`scripts/replay-live.mjs <sourceSpine> <outDir> [--speed N]` is a dev-only, unshipped ($0,
|
|
3264
|
+
no provider call) instrument that paces a real archived spine's records back out under a
|
|
3265
|
+
new runid so the panel can be exercised against a "live" run without a paid run — it is not
|
|
3266
|
+
a substitute for watching a real run start-to-end, which stays a separately authorized step.
|
|
3267
|
+
|
|
3189
3268
|
- **`bareloop run-u <flags…>`** (PANEL-BUILD.md P0 task 2/4) → the person-path run flow
|
|
3190
3269
|
(the JOBS-table/`--spec` runner, resume, the review door — `docs/logs/FINDINGS.md`'s
|
|
3191
3270
|
U-mode). `src/cli.js`'s `run-u` dispatch hands `rest` straight to
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bareloop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Workflows that earn their own design, with receipts — agent-authored workflows for repeated, long, verifiable jobs that improve across runs under an un-gameable gate.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
package/src/authorrun.js
CHANGED
|
@@ -1277,7 +1277,15 @@ export async function main(argv, deps = {}) {
|
|
|
1277
1277
|
err(` AND THE SPINE COULD NOT BE WRITTEN: ${/** @type {any} */ (spineErr)?.message ?? spineErr}`);
|
|
1278
1278
|
err(' the error above is the only record of this run — copy it before it scrolls');
|
|
1279
1279
|
}
|
|
1280
|
-
err(
|
|
1280
|
+
// no `err(\` spine ${spineFile}\`)` here — this catch falls through to the
|
|
1281
|
+
// shared tail (`out(\`\nspine ${spineFile}\`)\`, below), which runs on
|
|
1282
|
+
// EVERY exit path including this one (nothing in finally/the tail skips it
|
|
1283
|
+
// when the catch already set exitCode). A second, catch-local print here
|
|
1284
|
+
// used to duplicate that line on every crash; ONE owner (the tail) now
|
|
1285
|
+
// prints it, same as the refusal/failed-gate/signed-readout paths always
|
|
1286
|
+
// did. (The `err(' spine …')` in the SIGINT/SIGTERM/SIGHUP handler above
|
|
1287
|
+
// is a different path — a real signal re-raises and never reaches the
|
|
1288
|
+
// tail, so that one stays as the sole print for a killed run.)
|
|
1281
1289
|
// 4, distinct from 1 (a refusal or a failed gate), 2 (operator/config) and 3 (a
|
|
1282
1290
|
// leak). A crash is none of those: it is the run failing to reach a verdict at
|
|
1283
1291
|
// all, and sharing an exit code with a refusal would file a bug as a result.
|
package/src/behaviour.js
CHANGED
|
@@ -126,11 +126,14 @@ export function runBehaviour(events, { runId } = {}) {
|
|
|
126
126
|
* Render a {@link runBehaviour} summary as the agreed printable block
|
|
127
127
|
* (2–4 short lines). A zero-call run formats plainly ("0 tool calls") rather
|
|
128
128
|
* than a NaN-laced percentage — an empty run is a complete, reportable
|
|
129
|
-
* answer, not a division error.
|
|
130
|
-
*
|
|
129
|
+
* answer, not a division error. `summary === null` means the caller never
|
|
130
|
+
* had a gate-audit sidecar to read at all (see `replayRun`'s `auditAvailable`
|
|
131
|
+
* option) — that reads as "unknown", never coerced to a 0-call summary.
|
|
132
|
+
* @param {ReturnType<typeof runBehaviour>|null} summary
|
|
131
133
|
* @returns {string}
|
|
132
134
|
*/
|
|
133
135
|
export function formatBehaviour(summary) {
|
|
136
|
+
if (summary === null) return 'unknown (no gate-audit sidecar found for this run)';
|
|
134
137
|
const { totalCalls, byTool, denied, repeats, repeatPct } = summary; // uniquePaths: data-only, not in the agreed printed shape
|
|
135
138
|
const lines = [];
|
|
136
139
|
|
package/src/cli.js
CHANGED
|
@@ -58,6 +58,17 @@ import {
|
|
|
58
58
|
parseJsonl, looksLikeSpine, replayOne, listSpines, readHistoryLog,
|
|
59
59
|
} from './replayio.js';
|
|
60
60
|
import { formatReplay, formatAllLines } from './replay.js';
|
|
61
|
+
// PANEL-BUILD.md P1 — the read-only panel HTTP server (`bareloop panel`).
|
|
62
|
+
// One more caller of the same `src/` library functions this file already
|
|
63
|
+
// calls (the layering law, PANEL-BUILD.md §2) — `src/panel/server.js` never
|
|
64
|
+
// re-implements the spine/run-list read side, it calls `src/replayio.js`/
|
|
65
|
+
// `src/runlist.js` directly, in-process.
|
|
66
|
+
import { panelMain } from './panel/server.js';
|
|
67
|
+
// PANEL-BUILD.md P1 (2026-09-24 rulings) — the one run list
|
|
68
|
+
// (`~/.config/bareloop/runs.jsonl`) and its backfill scan. `bareloop runs`
|
|
69
|
+
// (list) and `bareloop runs backfill <dir>` (reconstruct rows from archived
|
|
70
|
+
// spines already on disk) are this rung's only new commands.
|
|
71
|
+
import { appendRun, readRunList, backfillRuns, formatRunRow } from './runlist.js';
|
|
61
72
|
|
|
62
73
|
// The tier->model tables live in `src/providers.js` now (PRD item 28's
|
|
63
74
|
// factory) — one seam instead of a copy hardcoded in each runner. A
|
|
@@ -393,6 +404,23 @@ async function doRun(args, { out, err, cwd, env, now, deps }) {
|
|
|
393
404
|
const closeDir = join(runsDir, 'close');
|
|
394
405
|
mkdirSync(closeDir, { recursive: true });
|
|
395
406
|
|
|
407
|
+
// PANEL-BUILD.md P1 — one row in the run list, BEFORE the first paid call
|
|
408
|
+
// (runJob, right below). A list-append failure must never block a real,
|
|
409
|
+
// already-signed run: caught and named loudly, never rethrown.
|
|
410
|
+
// `deps.runlistHome` — the SAME test seam shape `deps.provider`/`deps.now`
|
|
411
|
+
// already are on this ctx (never a new env var, see src/runlist.js's own
|
|
412
|
+
// header comment): a caller that injects it (every test in
|
|
413
|
+
// tests/cli.test.js that reaches this line) writes its row under a temp
|
|
414
|
+
// home instead of the real `~/.config/bareloop`; production never sets it,
|
|
415
|
+
// so `appendRun` falls through to `runlistHome()`'s own `os.homedir()`.
|
|
416
|
+
try {
|
|
417
|
+
appendRun({
|
|
418
|
+
at: new Date(now()).toISOString(), runid, job: runSpec.job, spine: spineFile, patient: worktree, via: 'bundle',
|
|
419
|
+
}, { home: deps.runlistHome });
|
|
420
|
+
} catch (e) {
|
|
421
|
+
err(`WARNING: could not add this run to ~/.config/bareloop/runs.jsonl (${/** @type {Error} */ (e).message}) — the run continues; the panel's list will be missing this row.`);
|
|
422
|
+
}
|
|
423
|
+
|
|
396
424
|
let outcome;
|
|
397
425
|
try {
|
|
398
426
|
outcome = await runJob(runSpec, {
|
|
@@ -540,6 +568,38 @@ function doReplay(args, { out, err, cwd }) {
|
|
|
540
568
|
return 0;
|
|
541
569
|
}
|
|
542
570
|
|
|
571
|
+
/**
|
|
572
|
+
* `bareloop runs` — print the one run list, one line per row
|
|
573
|
+
* ({@link formatRunRow}). `bareloop runs backfill <dir>` — scan `dir` for
|
|
574
|
+
* archived spines and add a row per one not already listed
|
|
575
|
+
* ({@link backfillRuns}), printing the count added/already-listed/skipped
|
|
576
|
+
* (never silent). Read-only, $0: no interview, no author, no run trigger, no
|
|
577
|
+
* key ever read.
|
|
578
|
+
* @param {string[]} args @param {{ out: (s: string) => void, err: (s: string) => void, cwd: string }} ctx
|
|
579
|
+
*/
|
|
580
|
+
function doRuns(args, { out, err, cwd }) {
|
|
581
|
+
if (args[0] === 'backfill') {
|
|
582
|
+
const dirArg = args[1];
|
|
583
|
+
if (!dirArg) { err('usage: bareloop runs backfill <dir>'); return 1; }
|
|
584
|
+
const dir = resolve(cwd, dirArg);
|
|
585
|
+
if (!existsSync(dir)) { err(`no such directory: ${dir}`); return 1; }
|
|
586
|
+
let result;
|
|
587
|
+
try {
|
|
588
|
+
result = backfillRuns(dir);
|
|
589
|
+
} catch (e) {
|
|
590
|
+
err(`backfill failed: ${/** @type {Error} */ (e).message}`);
|
|
591
|
+
return 1;
|
|
592
|
+
}
|
|
593
|
+
out(`backfill ${dir}: added ${result.added}, already listed ${result.alreadyListed}, skipped ${result.skipped} (not a spine or unreadable)`);
|
|
594
|
+
return 0;
|
|
595
|
+
}
|
|
596
|
+
const { rows, skipped } = readRunList();
|
|
597
|
+
if (rows.length === 0) { out('runs: (none listed yet — see `bareloop runs backfill <dir>`)'); }
|
|
598
|
+
else for (const row of rows) out(formatRunRow(row));
|
|
599
|
+
if (skipped > 0) out(`(${skipped} malformed line${skipped === 1 ? '' : 's'} skipped)`);
|
|
600
|
+
return 0;
|
|
601
|
+
}
|
|
602
|
+
|
|
543
603
|
/**
|
|
544
604
|
* The bare `bareloop` menu: `1 export 2 run 3 history q quit`, then the
|
|
545
605
|
* SAME code path as the sub-command, asking its args line by line. `run-u`,
|
|
@@ -556,7 +616,7 @@ async function runMenu(deps, ctx) {
|
|
|
556
616
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
557
617
|
try {
|
|
558
618
|
ctx.out('1 export 2 run 3 history q quit');
|
|
559
|
-
ctx.out('(also on the command line, not wizarded here: run-u, interview, author, replay — run `bareloop <name>` with its own flags)');
|
|
619
|
+
ctx.out('(also on the command line, not wizarded here: run-u, interview, author, replay, runs, panel — run `bareloop <name>` with its own flags)');
|
|
560
620
|
const choice = (await rl.question('> ')).trim().toLowerCase();
|
|
561
621
|
if (choice === '' || choice === 'q' || choice === 'quit') return 0;
|
|
562
622
|
if (choice === '1') {
|
|
@@ -593,7 +653,7 @@ async function runMenu(deps, ctx) {
|
|
|
593
653
|
* code, so `bin/bareloop.mjs` can set `process.exitCode` and let node flush
|
|
594
654
|
* queued stdout on its own (F-something: `process.exit()` can discard it).
|
|
595
655
|
* @param {string[]} argv
|
|
596
|
-
* @param {{ env?: Record<string,string|undefined>, stdout?: any, stderr?: any, cwd?: string, provider?: any, providerFor?: any, judgeProvider?: any, judgeModel?: string|null, now?: () => number, stdin?: any }} deps
|
|
656
|
+
* @param {{ env?: Record<string,string|undefined>, stdout?: any, stderr?: any, cwd?: string, provider?: any, providerFor?: any, judgeProvider?: any, judgeModel?: string|null, now?: () => number, stdin?: any, runlistHome?: string }} deps
|
|
597
657
|
* @returns {Promise<number>}
|
|
598
658
|
*/
|
|
599
659
|
export async function main(argv, deps = {}) {
|
|
@@ -641,6 +701,15 @@ export async function main(argv, deps = {}) {
|
|
|
641
701
|
// seam — same shape as `doHistory`/`doExport`, not the argv-owning
|
|
642
702
|
// `main(argv, deps)` modules the flows above delegate to.
|
|
643
703
|
if (cmd === 'replay') return doReplay(rest, ctx);
|
|
644
|
-
|
|
704
|
+
// `bareloop runs` / `bareloop runs backfill <dir>` — PANEL-BUILD.md P1's
|
|
705
|
+
// one run list. Read-only ($0): no interview, no author, no run trigger,
|
|
706
|
+
// no key ever read.
|
|
707
|
+
if (cmd === 'runs') return doRuns(rest, ctx);
|
|
708
|
+
// `bareloop panel [--port N]` — PANEL-BUILD.md P1's read-only HTTP panel.
|
|
709
|
+
// Never runs a job, spends money, or reads a key: it serves the run list
|
|
710
|
+
// and spine/gate-audit reads over `127.0.0.1` only. `deps.runlistHome`
|
|
711
|
+
// rides through the same injectable seam `run`/`run-u` already use.
|
|
712
|
+
if (cmd === 'panel') return panelMain(rest, { out, err, runlistHome: deps.runlistHome });
|
|
713
|
+
err(`unknown command ${JSON.stringify(cmd)} — one of: export, run, history, run-u, interview, author, replay, runs, panel`);
|
|
645
714
|
return 1;
|
|
646
715
|
}
|