bareloop 0.20.0 → 0.22.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 +234 -0
- package/README.md +14 -0
- package/bareloop.context.md +331 -10
- package/bin/bareloop.mjs +24 -0
- package/package.json +7 -3
- package/src/authorjob.js +15 -1
- package/src/bundle.js +713 -0
- package/src/cli.js +472 -0
- package/src/close-integrity.js +325 -0
- package/src/closetimeout.js +165 -0
- package/src/index.js +31 -1
- package/src/job.js +48 -4
- package/src/plan.js +5 -1
- package/src/planrun.js +212 -7
- package/src/ralph.js +40 -7
- package/src/reuse.js +4 -2
- package/src/reviewdoor.js +26 -6
- package/src/run.js +24 -3
- package/src/validate.js +59 -0
- package/types/bundle.d.ts +199 -0
- package/types/cli.d.ts +19 -0
- package/types/close-integrity.d.ts +196 -0
- package/types/closetimeout.d.ts +126 -0
- package/types/index.d.ts +4 -1
- package/types/planrun.d.ts +17 -2
- package/types/ralph.d.ts +19 -5
- package/types/reuse.d.ts +16 -0
- package/types/reviewdoor.d.ts +3 -1
- package/types/run.d.ts +25 -2
- package/types/validate.d.ts +48 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,240 @@ 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.22.0] — 2026-09-07
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Close integrity M1** (`docs/product/CLOSE-INTEGRITY-BUILD.md`, PRD item 27, F129/F131).
|
|
13
|
+
- `src/close-integrity.js`: the ONE `close-absolute-path` detector, shared by
|
|
14
|
+
`src/bundle.js` (export time) and a new $0 run-start precheck in `runPlan`
|
|
15
|
+
(`src/planrun.js`) — every job, not only exported bundles, now refuses at $0
|
|
16
|
+
(before the close-first precheck, before any provider call) when a close
|
|
17
|
+
script's CONTENT bakes in an absolute path that exists on disk. Exports
|
|
18
|
+
`readCloseScripts`, `checkCloseAbsolutePaths`, `absolutePathLiteralsOf` from
|
|
19
|
+
`src/index.js` — `readCloseScripts` is the pure reader M2's sha256 fingerprint
|
|
20
|
+
will reuse.
|
|
21
|
+
- `runJob`/`runPlan` gain an optional `resumable` flag (default `true`,
|
|
22
|
+
`run-u.mjs` unchanged); the bundle CLI (`src/cli.js`) passes `resumable: false`
|
|
23
|
+
so its escalation tail says "resume is `run-u`-only in v1" instead of naming a
|
|
24
|
+
`--resume` flag it does not implement (F130).
|
|
25
|
+
|
|
26
|
+
- **Hermetic test runner** (F136, closed at the runner). `scripts/hermetic.mjs`
|
|
27
|
+
runs `node --test` with `HOME` redirected to a fresh empty `mkdtemp` dir and
|
|
28
|
+
`GIT_CONFIG_GLOBAL`/`GIT_CONFIG_SYSTEM`/`GIT_CONFIG_NOSYSTEM` set to neutralise git
|
|
29
|
+
config, plus any inherited `GIT_AUTHOR_*`/`GIT_COMMITTER_*`/`EMAIL` deleted — the
|
|
30
|
+
local suite is now blind to this machine's identity the same way CI's runner already
|
|
31
|
+
is, so a test leaning on either fails here first. `npm test` now runs it in place of
|
|
32
|
+
a bare `node --test`; not a sandbox (PATH, `node_modules`, shelled-out binaries
|
|
33
|
+
untouched). See `docs/logs/FINDINGS.md` F136.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- **F129, remaining 8 close scripts**: `scripts/testgen-cold-check-close.mjs`,
|
|
38
|
+
`testgen-close.mjs`, `types-close.mjs`, `l2poc-check-close.mjs`,
|
|
39
|
+
`u-bareagent-close.mjs`, `u-bareguard-close.mjs`, `u-litectx-close.mjs`,
|
|
40
|
+
`u-baremobile-close.mjs` now read `WORKDIR = process.cwd()` instead of a
|
|
41
|
+
hardcoded absolute patient path, following `u-spawner-close.mjs`'s fix
|
|
42
|
+
(`3b987d4`). Behaviour-preserving: `run-u` always passes `cwd` = the patient.
|
|
43
|
+
|
|
44
|
+
- **Close integrity M2 — close-bytes signature** (`docs/product/CLOSE-INTEGRITY-BUILD.md`,
|
|
45
|
+
PRD item 27, N4). A job's signature now covers its close scripts' CONTENT, not only
|
|
46
|
+
the path naming them.
|
|
47
|
+
- New signed spec field `close[].sha256`: hex sha256 of the script file's bytes, for
|
|
48
|
+
every stage whose `cmd` names an addressable script (an interpreter cmd —
|
|
49
|
+
`node`/`sh`/`bash`/`python`/`python3`/`npx` — or a bare directly-executable absolute
|
|
50
|
+
path, e.g. a `.sh` wrapper). It sits inside the signed spec, so `jobSpecHash` covers
|
|
51
|
+
it with no hash-function change. `validateJob` (`src/job.js`) demands it
|
|
52
|
+
(`missing-required`) on every addressable stage and shape-checks it
|
|
53
|
+
(`invalid-value`, 64 lowercase hex chars).
|
|
54
|
+
- `src/close-integrity.js` gains `hashCloseScriptBytes`, `checkCloseByteSignature`,
|
|
55
|
+
`checkStageByteSignature`, `signCloseScripts` (all exported from `src/index.js`).
|
|
56
|
+
`runPlan` verifies the signature at run start (alongside `close-absolute-path`, $0,
|
|
57
|
+
before the close-first precheck and before any provider call) AND before EVERY
|
|
58
|
+
close run thereafter (the `runCloseStages` seam the precheck/preflight/check-passes/
|
|
59
|
+
close-fix-loop all share) — a mismatch reds the new typed fault `close-tampered`
|
|
60
|
+
(`src/ralph.js`'s `CLOSE_FAULTS`), distinct from `close-red`/`close-crashed`.
|
|
61
|
+
- `src/bundle.js`'s `exportBundle` reds `close-sha-mismatch` when a spec's signed
|
|
62
|
+
sha256 disagrees with the script bytes actually being packed — the manifest hash
|
|
63
|
+
and the spec's own signature must never drift apart.
|
|
64
|
+
- `scripts/sign-close.mjs`: a thin CLI wrapper around `signCloseScripts` that fills
|
|
65
|
+
`close[].sha256` from disk and prints the old→new `jobSpecHash`; never writes
|
|
66
|
+
unless `--write`. The agent never writes this field — `src/authorjob.js`'s
|
|
67
|
+
`assembleSpec` refuses a draft carrying `sha256` the same way it refuses
|
|
68
|
+
`close`/`closeDecl`/`verdictType`.
|
|
69
|
+
- **Widened scope (orchestrator audit, folded into this build):** the addressable-script
|
|
70
|
+
shape test (`closeScriptCandidateToken`, `src/validate.js`) now also recognizes a
|
|
71
|
+
bare directly-executable absolute-path cmd (no interpreter prefix) — previously
|
|
72
|
+
invisible to BOTH `close-absolute-path` and this fingerprint. Every `jobs/*.json`
|
|
73
|
+
whose close names a script was re-signed with `scripts/sign-close.mjs --all --write`.
|
|
74
|
+
|
|
75
|
+
- **Close integrity M3 — close timeout: autoset + signed override** (PRD item 27,
|
|
76
|
+
hamr 2026-09-06: "both … autoset and can be override, same like api pricing"; arbiter
|
|
77
|
+
constants set 2026-09-07: floor 120,000ms, K = 5).
|
|
78
|
+
- New `src/closetimeout.js`: `CLOSE_TIMEOUT_FLOOR_MS`, `CLOSE_TIMEOUT_K`,
|
|
79
|
+
`TIMING_PREFLIGHT_CEILING_MS`, `timeCloseStages`, `computeCloseTimeoutCeiling`,
|
|
80
|
+
`resolveCloseTimeoutMs`, `closeTimeoutBanner` (all exported from `src/index.js`). A
|
|
81
|
+
$0 timing preflight runs every close stage once, ignoring verdicts, before the
|
|
82
|
+
close-first precheck and before any provider call; the per-stage ceiling every close
|
|
83
|
+
call in the run then uses is `max(FLOOR, K × slowest measured stage)`, printed on
|
|
84
|
+
every run (`close timeout: <n>s per stage (estimated from seed timing: …)`).
|
|
85
|
+
- New OPTIONAL signed spec field `closeTimeoutMs` (`>= CLOSE_TIMEOUT_FLOOR_MS`,
|
|
86
|
+
`src/job.js`): the operator's own override, which wins outright over the estimate and
|
|
87
|
+
may legally sit above OR below it (the rates-passthrough shape, F113 — not
|
|
88
|
+
tighten-only). Skipped when set: the timing pass never runs. Arbiter territory —
|
|
89
|
+
`src/authorjob.js`'s `assembleSpec` refuses a draft carrying it.
|
|
90
|
+
- New `runPlan` run-start check `wall-under-close-timeout`: refuses at $0 when
|
|
91
|
+
`job.maxWallMs` is under the just-resolved EFFECTIVE close timeout — the honest shape
|
|
92
|
+
once the real (autoset or signed) ceiling is known, never a silent clamp.
|
|
93
|
+
- `scripts/run-u.mjs`'s hardcoded `CLOSE_TIMEOUT_MS = 900_000` retires; the per-stage
|
|
94
|
+
ceiling is resolved once (via the same `resolveCloseTimeoutMs`) before the outside
|
|
95
|
+
watchdog (F67) is even spawned, so its stale/grace windows size off the real number.
|
|
96
|
+
`src/cli.js`'s bundle runner passes nothing — both runners now go through the
|
|
97
|
+
identical autoset/override path.
|
|
98
|
+
- **Part B — `BARELOOP_CLOSE_DIR`.** `runClose` (`src/ralph.js`) accepts an optional
|
|
99
|
+
`closeDir` and sets it as `BARELOOP_CLOSE_DIR` in the close's (already-stripped) env —
|
|
100
|
+
a `BARELOOP_` name matches none of `CLOSE_ENV_DENY`'s rules. New
|
|
101
|
+
`checkCloseDirRequired`/`CLOSE_DIR_ENV_VAR` (`src/close-integrity.js`): a close script
|
|
102
|
+
whose source mentions the variable and gets no `closeDir` refuses at $0
|
|
103
|
+
(`close-dir-required`), the same seam as `close-absolute-path`/`close-tampered`. The
|
|
104
|
+
four hand-authored scripts that hardcoded an external `SPINE_DIR`
|
|
105
|
+
(`testgen-cold-check-close.mjs`, `testgen-close.mjs`, `l2poc-check-close.mjs`,
|
|
106
|
+
`types-close.mjs`) now read `process.env.BARELOOP_CLOSE_DIR` and instrument-stop
|
|
107
|
+
(exit 97) when it is absent — no baked fallback. `scripts/u-pulselog-close.mjs`'s
|
|
108
|
+
hardcoded `--workdir` DEFAULT (a separate F129 cwd-class hazard — that literal existed
|
|
109
|
+
on disk and already tripped the shipped `close-absolute-path` guard for
|
|
110
|
+
`jobs/pulselog-u-types.json`) now falls back to `process.cwd()`, the same M1 template
|
|
111
|
+
every other close script uses. `src/cli.js` sets `closeDir` to
|
|
112
|
+
`<bundleDir>/runs/<runid>/close/`; `scripts/run-u.mjs` sets it to its existing
|
|
113
|
+
`spineDir`. Every `jobs/*.json` whose close bytes changed was re-signed
|
|
114
|
+
(`scripts/sign-close.mjs --all --write`).
|
|
115
|
+
|
|
116
|
+
- **F132 — a real paid fire of an exported bundle refused itself `close-tampered`
|
|
117
|
+
at $0** (run `mtqwmb9l` → export → `mtqwydl4`). `exportBundle`'s import rewrite
|
|
118
|
+
(`../src/x.js` → `'bareloop'`) changes a close script's bytes, but the written
|
|
119
|
+
bundle's `spec.json` kept the SOURCE spec's `close[].sha256` unchanged — two
|
|
120
|
+
signatures over two different byte strings, so the run-start
|
|
121
|
+
`checkCloseByteSignature` correctly refused a bundle that was never actually
|
|
122
|
+
tampered with. `exportBundle` (`src/bundle.js`) now re-signs `close[].sha256`
|
|
123
|
+
in the written spec over the RELOCATED bytes it actually packs, for every
|
|
124
|
+
stage that already carries a signature — the export-time `close-sha-mismatch`
|
|
125
|
+
check against the SOURCE spec/bytes is unchanged (it still guards a tampered
|
|
126
|
+
source).
|
|
127
|
+
|
|
128
|
+
- **F133 — double/mislabelled close-timeout banner** (run `mtqwmb9l`). `runPlan`
|
|
129
|
+
printed/emitted "estimated from seed timing" (`scripts/run-u.mjs`'s own
|
|
130
|
+
resolution, needed early to size the outside watchdog, F67) AND, on the same
|
|
131
|
+
run, "explicit runner override" (the library's own print, because run-u also
|
|
132
|
+
fed the resolved number back in as a bare `closeTimeoutMs`) — two lines for
|
|
133
|
+
one number, the second lying about its source. `scripts/run-u.mjs` no longer
|
|
134
|
+
passes `closeTimeoutMs` into `runJob` at all; `runJob`/`runPlan` resolve
|
|
135
|
+
(autoset, or the spec's own signed field) and announce the ceiling themselves
|
|
136
|
+
— the ONE banner/spine record per run. The runtime `closeTimeoutMs` option on
|
|
137
|
+
`runJob`/`runPlan` stays, test/backward-compat only. Same fire, second (real)
|
|
138
|
+
catch: run-u's own pre-fix early `emit('close-timing', …)` landed BEFORE
|
|
139
|
+
`job-start` on the spine (`runJob` is what emits `job-start`, and run-u called
|
|
140
|
+
it before ever calling `runJob`) — every spine reader assumes `job-start`
|
|
141
|
+
opens the file. Fixed by the same change (run-u no longer emits it early);
|
|
142
|
+
`tests/readshim-battery.test.js`'s archive check now tolerates `job-start`
|
|
143
|
+
within the first 3 records (the archived `u-mtqwmb9l.jsonl` is real and never
|
|
144
|
+
rewritten), and a new test pins `job-start` as literally the first record on
|
|
145
|
+
a fresh scripted run.
|
|
146
|
+
|
|
147
|
+
- **F135 — review-door accept re-run bypassed the close byte signature**. Every
|
|
148
|
+
other close execution goes through `checkStageByteSignature` before running
|
|
149
|
+
a stage (`runCloseStages`, `src/planrun.js`); the review door's `accept`
|
|
150
|
+
re-proof (`proveMechanically`, `src/reviewdoor.js`) called
|
|
151
|
+
`runStages`/`runDeclaredStages` directly and skipped it — a close script
|
|
152
|
+
swapped on disk after the run ended but before the signer's accept ran
|
|
153
|
+
unchecked and could mint a false accept. Fixed: `proveMechanically` now
|
|
154
|
+
verifies the mechanical stages' bytes against their signed `sha256` first,
|
|
155
|
+
refusing through the existing `door-accept-red` path on a mismatch
|
|
156
|
+
(`tests/reviewdoor-tamper.test.js`).
|
|
157
|
+
|
|
158
|
+
- **F134 — live re-fire validation, no code change**. Two real paid fires of the
|
|
159
|
+
same exported bundle (`mtr0icky` cap-halt, `mtr4t1u1` green re-fire) confirmed
|
|
160
|
+
M2's relocated-bytes signature and M3's close-timing banner both hold under a
|
|
161
|
+
second real fire, not just the one that motivated each fix; the cap-halt
|
|
162
|
+
itself read as a cap-shaped negative (an `Any`-suppression cheat correctly
|
|
163
|
+
caught by `no-suppressions`, not an instrument defect). See
|
|
164
|
+
`docs/logs/FINDINGS.md` F134.
|
|
165
|
+
|
|
166
|
+
- **F136 — CI-only red: patient test fixtures committed without the neutralised
|
|
167
|
+
git identity**. `tests/close-integrity.test.js` and `tests/close-timeout.test.js`
|
|
168
|
+
each added a second commit on top of `initPatientRepo`'s seed via a raw
|
|
169
|
+
`execFileSync('git', ...)` with no env, so it depended on the host's own git
|
|
170
|
+
identity — green on hamr's machine (a global config papers over it), red on
|
|
171
|
+
CI (none). `tests/helpers.js` now exports `patientGitEnv`/`gitInPatient`
|
|
172
|
+
reusing `initPatientRepo`'s identity env; both files' `makePatient` go through
|
|
173
|
+
it. See `docs/logs/FINDINGS.md` F136.
|
|
174
|
+
|
|
175
|
+
## [0.21.0] — 2026-09-06
|
|
176
|
+
|
|
177
|
+
### Added
|
|
178
|
+
|
|
179
|
+
- **Export/run a bundle** (`docs/product/EXPORT-BUILD.md`, frozen 2026-09-05). New
|
|
180
|
+
`src/bundle.js`: `exportBundle` mints a plain directory bundle (signed spec with
|
|
181
|
+
`close[].cmd` rewritten under `$BARELOOP_BUNDLE`, the close scripts, and the job's
|
|
182
|
+
registry history) from a job that already carries a real green at its current spec
|
|
183
|
+
hash — refuses (typed reds, nothing written) on an unrelocatable close cmd, a script
|
|
184
|
+
collision, an import this module cannot verify or that bareloop's own `src/index.js`
|
|
185
|
+
does not export, or no bridge at the spec's hash. `bundleHash(dir)` / `readBundle(dir)`
|
|
186
|
+
recompute the signature from disk and red `bundle-tampered` on any mismatch — the load-
|
|
187
|
+
bearing check that stands between a swapped close script and a fake green (N4).
|
|
188
|
+
`resolveBundleSpec`, `checkEnvelope` (tighten-only budget/wall), `bless`/`verifyBlessing`
|
|
189
|
+
(first-run approval, no-resign on later runs), and `appendHistory` round out the module.
|
|
190
|
+
- **`bareloop` CLI and `bin/`.** New `bin/bareloop.mjs` (the package's first `bin` entry)
|
|
191
|
+
and `src/cli.js`'s `main(argv, deps)` — exported from the package root as `cliMain` —
|
|
192
|
+
implement `bareloop export|run|history` plus a bare numbered menu. `run` orders its
|
|
193
|
+
steps as: tamper check, tighten-only envelope check, provider key (or the injected
|
|
194
|
+
test-seam provider), blessing/approval, a fresh `git worktree add --detach` per run,
|
|
195
|
+
then `runJob` itself; the tail prints the outcome, spend, and the `git merge` command —
|
|
196
|
+
merge stays human, the CLI never merges. `bin/` added to `package.json`'s `files`.
|
|
197
|
+
- **`checkBundleDeps(bundleDir)`** (`src/bundle.js`, F128): resolves `bareloop` from the
|
|
198
|
+
bundle's own `close/` directory (`createRequire(...).resolve('bareloop')`, walking the
|
|
199
|
+
package's own `exports` map) and reds typed `bundle-deps-missing` — with the exact cure
|
|
200
|
+
line `cd <bundleDir> && npm install` — when it cannot. `bareloop run` calls it as literal
|
|
201
|
+
step 1b, right after the tamper check and before the envelope check, the key, and any
|
|
202
|
+
worktree, so a bundle installed the wrong way (as someone else's dependency, never `npm
|
|
203
|
+
install`ed inside its own directory) fails fast with a fix instead of crashing deep
|
|
204
|
+
inside a close stage with a bare `ERR_MODULE_NOT_FOUND`.
|
|
205
|
+
- **`close-absolute-path` export guard** (`src/bundle.js`, F129): `exportBundle` now refuses
|
|
206
|
+
a close script whose source bakes in a string literal that is an absolute POSIX path
|
|
207
|
+
which exists on the exporting machine and is outside an allow-listed system prefix — a
|
|
208
|
+
close judges the `cwd` the runner gives it, never a path baked into its own source. Pins
|
|
209
|
+
the live defect where a hardcoded `WORKDIR` made the close judge the original patient
|
|
210
|
+
checkout instead of a fresh worktree, minting `already-green` at $0.
|
|
211
|
+
- **The aurora close now reads `process.cwd()`** (`scripts/u-spawner-close.mjs`) instead of a
|
|
212
|
+
hardcoded absolute `WORKDIR`, so it judges the tree the runner actually hands it rather than
|
|
213
|
+
the original patient checkout — the fix behind F129, validated live by a fourth paid fire
|
|
214
|
+
that went green from a fresh detached worktree (`docs/logs/FINDINGS.md`, F130's closing
|
|
215
|
+
pointer).
|
|
216
|
+
|
|
217
|
+
### Fixed
|
|
218
|
+
|
|
219
|
+
- **F128 — `bareloop run` exit code now reflects the run outcome.** Previously every
|
|
220
|
+
invocation that reached the tail exited `0` regardless of outcome — a close-red, plan-
|
|
221
|
+
red, escalated, cap/wall-halted, or crashed run all read as CLI success. `run` now
|
|
222
|
+
returns `0` only for `green`/`already-green`; every other outcome exits `1`, so a caller
|
|
223
|
+
scripting off the exit code can no longer mistake a red run for a green one. The tail's
|
|
224
|
+
printed lines are unchanged.
|
|
225
|
+
- **F128 — the "minting run" line named the wrong run.** On a bundle's first (unblessed)
|
|
226
|
+
run, the notice printed the bridge's FIRST history row's `runid`, not the run that
|
|
227
|
+
actually minted the bundle's own signed spec. It now searches every bridge version
|
|
228
|
+
across ALL shipped bridges (base + shape fork) for the one whose `specHash` equals the
|
|
229
|
+
resolved `$BARELOOP_BUNDLE` spec's `jobSpecHash`, and prints "no version at this hash"
|
|
230
|
+
(honestly, never a guess) when none matches — which cross-machine is the common case,
|
|
231
|
+
since the historic mint's real script path and the importer's own bundle path rarely
|
|
232
|
+
coincide.
|
|
233
|
+
- **`bin/bareloop.mjs`: a closed stdout reader (`| head`) no longer crashes with an
|
|
234
|
+
unhandled `EPIPE`.** `process.stdout` now has an `'error'` listener that exits `0` on
|
|
235
|
+
`EPIPE` (the one legitimate `process.exit()` in this file — the reader is gone, so
|
|
236
|
+
there is no queued output left to lose) and re-throws anything else.
|
|
237
|
+
- **`scripts/replay-row.mjs`** — a $0 registry-row replay from an archived green spine: mints
|
|
238
|
+
the row a real run would have written via `writeRunGreenRow` had it been launched with
|
|
239
|
+
`--registry`, using the same seam and the same input derivation `scripts/run-u.mjs` uses.
|
|
240
|
+
Given it a permanent home this session (was scratchpad-only).
|
|
241
|
+
|
|
8
242
|
## [0.20.0] — 2026-09-05
|
|
9
243
|
|
|
10
244
|
### Added
|
package/README.md
CHANGED
|
@@ -133,6 +133,20 @@ works, **export the whole workflow** — with its self-healing harness — and r
|
|
|
133
133
|
anywhere. The point is to stop trial-and-erroring your way to the workflow that fits a job.
|
|
134
134
|
Find it once, keep it, reuse it. It is a seed of something bigger over time.
|
|
135
135
|
|
|
136
|
+
### Export a job
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
bareloop export jobs/my-maintainer.json --registry ./bridges --out ./my-maintainer.bareloop
|
|
140
|
+
bareloop run ./my-maintainer.bareloop --repo /path/to/checkout --approve <bundleHash printed above>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`export` only ships a job that has already earned a real green — it refuses to bundle an
|
|
144
|
+
unproven draft. The first `run` on a new machine asks you to approve the bundle's printed
|
|
145
|
+
hash; once that run goes green, later runs need no re-approval as long as the bundle stays
|
|
146
|
+
unchanged. Every run works in its own fresh git worktree and leaves the merge to you. Full
|
|
147
|
+
contract — flags, approval/blessing rules, exit codes — in
|
|
148
|
+
[`bareloop.context.md`](bareloop.context.md).
|
|
149
|
+
|
|
136
150
|
## Running it
|
|
137
151
|
|
|
138
152
|
bareloop runs locally, as you. Secrets load from the environment and never enter the tree or
|