bareloop 0.28.0 → 0.29.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 +65 -0
- package/bareloop.context.md +57 -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 +1910 -0
- package/src/panel/server.js +1527 -0
- package/src/replay.js +632 -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,71 @@ 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.29.0] — 2026-09-26
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`bareloop` panel (N6/P0/P1)** — a read-only HTTP server (`bareloop panel`, bound to
|
|
13
|
+
`127.0.0.1:4700`) serving a single-page app built from one run list
|
|
14
|
+
(`~/.config/bareloop/runs.jsonl`, populated by `bareloop run`/`bareloop runs backfill`).
|
|
15
|
+
The page reads the library's own `replayRun`/spine/gate-audit functions directly, in-process
|
|
16
|
+
— the same read side `bareloop replay` uses, never a duplicated parser.
|
|
17
|
+
- **Run tab: step-map + per-part cards.** A plan-shape run renders one two-line card per PART
|
|
18
|
+
(scout, plan, each step, each replan, the post-step fix loop, judge when recorded), each
|
|
19
|
+
clickable through to its own slice of the Audit tab; an old-shape (iterations, no
|
|
20
|
+
step-start) run collapses to one "run" card.
|
|
21
|
+
- **Audit tab: Grouped (part → attempt → rounds table) and Flat views**, a search box
|
|
22
|
+
(matches job/runid/model), and All/Writes/Blocked filter chips that auto-open every
|
|
23
|
+
matching group.
|
|
24
|
+
- **Job tab** resolves the real spec (bundle `spec.json`, `jobs/<job>.json`, or the run's own
|
|
25
|
+
job-start record) and shows Success/Guardrails/Tools/Judge-examples fields plus the full
|
|
26
|
+
granted-tools "offered" list.
|
|
27
|
+
- **Run Summary** gained `model` (with provider) and `judge` lines, and a died glyph `[?]`
|
|
28
|
+
distinct from a real failed verdict `[✗]`.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **Workflows and History merged into one `Runs` tab**, toggled between a grouped-by-job
|
|
33
|
+
"Workflows" view (default) and a flat "History" view — both built from the one `/api/runs`
|
|
34
|
+
list; `/api/workflows` and the client's `listWorkflows` were deleted.
|
|
35
|
+
- Audit-tab paths shorten to the run's own resolved tree root (`pathShort`) when one can be
|
|
36
|
+
found; an older run with no resolvable root keeps its full absolute path.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- **`bareloop replay`'s tool-call count was contaminated by a shared gate-audit sidecar** — a
|
|
41
|
+
sidecar file can carry rows from several runs that happen to reuse the same filename;
|
|
42
|
+
`auditWindow` now scopes every reader (CLI and panel alike) to the run's own
|
|
43
|
+
`job-start`..`job-end` window, so the two can never disagree.
|
|
44
|
+
- **Panel tests bound a fixed port, flaking under a taken port** — every test now asks the OS
|
|
45
|
+
for an ephemeral port (`port: 0`) instead of guessing a free one.
|
|
46
|
+
- Audit tab's "Raw log" leaked an EARLIER unrelated run's rows when a gate-audit sidecar is
|
|
47
|
+
shared across runs; it now shows only this run's own ts-windowed lines, byte-for-byte.
|
|
48
|
+
- A `~2`-style backfill-collision runid (two archived spines sharing a basename in different
|
|
49
|
+
directories) echoed the wrong, unsuffixed runid in the run header and its sibling lookups.
|
|
50
|
+
- `toolCalls`/`behaviour` report `null` (unknown), never a fabricated `0`, when a run has no
|
|
51
|
+
gate-audit sidecar at all.
|
|
52
|
+
- Backfill scans recursively (bounded depth) instead of the top directory plus its immediate
|
|
53
|
+
subdirectories only, and dedups by resolved spine path rather than a filename-derived runid
|
|
54
|
+
(two same-basename spines in different directories are different runs, not duplicates).
|
|
55
|
+
- **Workflows job (parent) row is its latest run**: clicking it opens that run (previously a
|
|
56
|
+
dead click after the Runs-tab merge); the expanded child list shows only OLDER runs, never
|
|
57
|
+
repeating the latest as its own first child; the expand caret appears only when a matching
|
|
58
|
+
older run exists under the active filters/search (not just `runCount > 1`); and the row's
|
|
59
|
+
"selected" state and click target both follow whichever run is actually active
|
|
60
|
+
(`representedRun`) — so a search hit or a History pick on an older run correctly expands
|
|
61
|
+
and highlights that run's job, instead of always marking/opening the job's latest.
|
|
62
|
+
- Various Workflows/History/Audit rendering fixes: rows stay two lines and never wrap; the
|
|
63
|
+
meta-line separator carries a leading space; a died run's "why" line uses one plain
|
|
64
|
+
timestamp format; a run that died before its first step reads "0 of 0 done" (never "0 of
|
|
65
|
+
1"); the Audit tab distinguishes "no sidecar ever written" from "sidecar exists but empty";
|
|
66
|
+
History's filter chips no longer needed two clicks to register; the desktop list scrolls in
|
|
67
|
+
its own pane and mobile gets a "back to list" affordance; check-type labelling correctly
|
|
68
|
+
falls back to "deterministic" for runs that predate `verdictType` (2026-08-18); no-verdict
|
|
69
|
+
parts (scout/plan/replan/judge) render no result glyph rather than a guessed one; the Run
|
|
70
|
+
tab's step-map numbering and card order matched (F197/F198); Summary's "offered" line shows
|
|
71
|
+
the FULL granted-tools list, never truncated to the top few.
|
|
72
|
+
|
|
8
73
|
## [0.28.0] — 2026-09-24
|
|
9
74
|
|
|
10
75
|
### Added
|
package/bareloop.context.md
CHANGED
|
@@ -3186,6 +3186,63 @@ 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
|
+
|
|
3189
3246
|
- **`bareloop run-u <flags…>`** (PANEL-BUILD.md P0 task 2/4) → the person-path run flow
|
|
3190
3247
|
(the JOBS-table/`--spec` runner, resume, the review door — `docs/logs/FINDINGS.md`'s
|
|
3191
3248
|
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.29.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
|
}
|