bareloop 0.38.0 → 0.40.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 +59 -0
- package/bareloop.context.md +87 -24
- package/package.json +3 -3
- package/src/authorflow.js +335 -59
- package/src/authoring.js +125 -3
- package/src/authorjob.js +93 -32
- package/src/authorreadout.js +498 -3
- package/src/authorrun.js +26 -6
- package/src/authorscout.js +9 -6
- package/src/bridges.js +1 -1
- package/src/calibrate.js +85 -11
- package/src/cardauthor.js +8 -2
- package/src/declaredclose.js +12 -5
- package/src/draftspend.js +6 -1
- package/src/interviewrun.js +56 -27
- package/src/job.js +38 -5
- package/src/jobblock.js +140 -0
- package/src/judged.js +323 -29
- package/src/kinds.js +13 -1
- package/src/ledger.js +1 -1
- package/src/panel/authorroutes.js +17 -11
- package/src/panel/authorsession.js +202 -75
- package/src/panel/index.html +302 -312
- package/src/panel/jobplan.js +107 -0
- package/src/panel/server.js +67 -13
- package/src/planrun.js +6 -5
- package/src/promptregisters.js +1 -1
- package/src/providers.js +111 -2
- package/src/reuse.js +1 -1
- package/src/root.js +1 -1
- package/src/run.js +3 -3
- package/src/selection.js +1 -1
- package/src/source.js +64 -1
- package/src/text.js +13 -0
- package/src/transport.js +1 -1
- package/src/trend.js +1 -0
- package/src/userrun.js +3 -3
- package/types/authorflow.d.ts +258 -39
- package/types/authoring.d.ts +30 -1
- package/types/authorjob.d.ts +52 -13
- package/types/authorreadout.d.ts +123 -1
- package/types/authorscout.d.ts +5 -3
- package/types/calibrate.d.ts +4 -1
- package/types/declaredclose.d.ts +5 -1
- package/types/draftspend.d.ts +8 -2
- package/types/job.d.ts +1 -1
- package/types/jobblock.d.ts +102 -0
- package/types/judged.d.ts +21 -2
- package/types/panel/authorsession.d.ts +57 -7
- package/types/panel/jobplan.d.ts +58 -0
- package/types/panel/server.d.ts +20 -0
- package/types/planrun.d.ts +1 -1
- package/types/providers.d.ts +52 -1
- package/types/run.d.ts +1 -1
- package/types/source.d.ts +26 -0
- package/types/trend.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,65 @@ All notable changes to bareloop are documented here. Format:
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning:
|
|
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
|
+
|
|
8
|
+
## [0.40.0] - 2026-10-11
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **The job block (P7).** A job is now written as two numbered boxes, **The job** and **Inputs**, replacing Goal / Source / Success / Guardrails / Judge examples. A `~` line is a rule for the line above it; `~ PASS:` / `~ FAIL:` lines are a rubric's judge examples and keep their line number. Inputs are numbered `label: value`: line 1 is the repo, later lines are files inside it, proven at $0 before any session. One parser (`src/jobblock.js`) serves the panel and the CLI interview, which now asks The job and Inputs as multi-line answers.
|
|
13
|
+
- **Every job line is covered.** The signed spec carries `jobLines` and `inputs`. Each stage the drafter writes names the job line(s) it serves (`fromLine`, an array, so one check may serve several lines), or the line is listed in `closeDecl.refused` with a reason; an uncovered line is a $0 red fed back through the revise loop. Signed `jobLines` carry `rules[]` beside `rule`, so Reuse never cuts an example at its own `; `. Only new jobs' hashes move; Inputs is open on a Reuse.
|
|
14
|
+
- **Try-3 questions.** On the last drafting try, after two failed ones (validation red, seed-read failure or vacuous close, or an output cut-off), the drafter may ask up to 2 questions, each naming a job line. The panel asks each through the ordinary ask path (answer required, no Skip), adds the answer word for word as a `~` rule on that line, and drafts once more with no asking. Off unless the caller wires an asker (the CLI never does).
|
|
15
|
+
- **Thinking switch for drafting and the judge.** `thinkingMode` ('on' | 'off', anything else throws) is asked of bare-agent; `off` is sent only for models bare-agent's `thinkingSupport` says are switchable, and nothing is sent for a not-switchable or unverified model. The draft log records `thinking` (off / on / not-switchable / unverified) and bare-agent's `thinkingApplied` verdict per call.
|
|
16
|
+
- **Panel.**
|
|
17
|
+
- One shared plan renderer draws the chat bubble and the Job tab: numbered lines, rules, checks under the line they serve, with *Not checked* and *Always on* folds; an "AI's summary" row, and an "AI's open questions" row (each question its own item, then "Revise if you need to.") shown only when there are questions.
|
|
18
|
+
- The Job tab runs The job, Inputs, Destination, Tools, Cap, Signed, with the job at full height; older jobs show an older-job note and keep their goal as line 1.
|
|
19
|
+
- The job boxes grow to 8 lines then scroll, with the number gutter scrolling alongside; the left pane widens to 630px from 1200px wide.
|
|
20
|
+
- The drafting list opens with a code-owned *thinking* row; if the model reports it thought anyway after `off` was sent, it reads "thinking on (the model thought anyway)".
|
|
21
|
+
- Every drafting stop now says why in plain words (what happened, what it cost, what to do).
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **Thinking is OFF by default for drafting and the judge, for every switchable model, worker untouched.** The default was decided by a frozen, pre-registered 8-draft ON/OFF contrast measured on **deepseek-flash only** (rule said OFF; underpowered by declaration); extending it to every other switchable model is hamr's ruling of 2026-10-11, not a measurement. `on` sends nothing and is byte-identical to a call with no knob. The deepseek-flash request body is unchanged (thinking disabled, now produced by bare-agent); the per-model allow-lists in `src/providers.js` are deleted.
|
|
26
|
+
- A secret-shaped input file is masked, not refused (matching the worktree repo door); `.env` by name and symlinks are still refused.
|
|
27
|
+
- Dependencies: `bare-agent` ^0.49.0 to ^0.51.0.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **Output cut-off is a failed try, not provider-red.** A drafter answer stopped at the output cap (`truncated:max_tokens`) now goes through the same retry ladder as a malformed artifact, with the reason "the AI's answer was cut off at the output limit"; the cut-off reply is not replayed, `AUTHOR_MAX_TOKENS` is not raised, real transport failures stay provider-red, and spend stays booked. An all-cut-off try also counts for the try-3 question gate. Test-proven only, not exercised live.
|
|
32
|
+
- A dropped connection (ECONNRESET "socket hang up") is a call casualty like ETIMEDOUT, and the scout stop says in words that the first look did not complete (F217).
|
|
33
|
+
- `proveInputFiles` compares like-for-like, so a repo behind a symlinked parent proves its Inputs.
|
|
34
|
+
|
|
35
|
+
## [0.39.0] - 2026-10-10
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- **Rubric judge rulebook (F192).** `says-what`: the doc description must say more than the function's own name; name words match by prefix (init/initialize; a shorter word of 4+ letters), and with no doc block `has-doc` alone owns the red. `params` also reds an extra `@param` that names no parameter, and a missing field of a destructured parameter once the doc names any field. `has-doc` checks the artifact itself: the judge only points at the declaration, code finds the `/**` block above it. Quote matching uses bareguard `quoteIn` with `wholeLines`, so a quote that dropped a leading `* ` still matches. The injection facts check shares `has-doc`'s acceptance, and every rule carries a plain-words `means` for the case-proposal prompt.
|
|
40
|
+
- **A calibration case may not expect a red the rulebook cannot raise** (a `has-doc` red on a function that has a JSDoc block); it is refused before any locate call.
|
|
41
|
+
- **Calibration record.** A per-case `diag` (locate facts, full reds, per-attempt cause) is recorded beside the graded rows, and each failing case gets one plain why-line. Grading, hashes and verdicts are untouched.
|
|
42
|
+
- **Judged write fence.** A judged stage may only judge files inside the job's write fence; otherwise the red `judged-outside-write-scope` fires at draft time, before signing, on every door (authoring revise loop, signing, runner pre-flight, hand-written or reused specs).
|
|
43
|
+
- **Worker warning.** When a job's close carries the js no-suppressions check, the worker is told up front that it reads comment lines too.
|
|
44
|
+
- **Panel.**
|
|
45
|
+
- A refused rubric proposal and a refused signing each say why in plain words (panel and CLI share one owner); the full reds go to `draft-log.jsonl`.
|
|
46
|
+
- The calibrating line shows the gate's real score (8 of 10 / 3 of 5), not a stale check mark.
|
|
47
|
+
- The confirm plan reads as headed sections (#PLAN / #CHECKS / #NOT CHECKED, and #QUESTIONS when raised).
|
|
48
|
+
- The step map is fit-to-text chips with arrows, a line-start arrow on wrapped lines, and ↻N plus a dashed box for retries.
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
- **Strike governor.** A judged stage's trend number is its distinct red count, compared to best-so-far; the gap headline reads "N red(s) across X of Y artifact(s)". Identical, flip-flopping or growing judged reds now strike (they read as not comparable before).
|
|
53
|
+
- **Judge thinking is off** for the rubric judge on deepseek-flash, per call and allow-listed; workers and the drafter are unchanged and `JUDGE_MAX_TOKENS` stays 4000.
|
|
54
|
+
- **Revise reopens the job's boxes**, filled as drafted; Start drafting then drafts fresh. The typed change request is gone: `POST /api/author/:id/revise`, `session.revise`, the "N changes left" hint and `revisesLeft` are removed. The CLI confirm turn is unchanged.
|
|
55
|
+
- Dependencies: `bareguard` ^0.13.0 to ^0.21.0, `bare-agent` ^0.43.0 to ^0.49.0.
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
|
|
59
|
+
- **A passed calibration satisfies the seed-red gate for any close with a judged stage**, mixed or pure. The old test read a function's arity (always true), so signing refused a mixed rubric close after calibration passed 10/10 (live session smv0tasvb0u89).
|
|
60
|
+
|
|
61
|
+
### Known limits
|
|
62
|
+
|
|
63
|
+
- The judge's false-red classes seen on live run mv1j01sl are parked (FINDINGS F192 f), not fixed.
|
|
64
|
+
- The step-map chips and the new panel text were not checked at phone width by a person.
|
|
65
|
+
|
|
7
66
|
## [0.38.0] - 2026-10-08
|
|
8
67
|
|
|
9
68
|
### Added
|
package/bareloop.context.md
CHANGED
|
@@ -67,7 +67,7 @@ inherited rule carries the green that minted it and the contrast that attributed
|
|
|
67
67
|
an unknown provider name THROWS there, it never falls back to a default. Per-model
|
|
68
68
|
request-key gating lives in that table: `deepseek-flash` sets bare-agent's
|
|
69
69
|
`legacyMaxTokens`, because DeepSeek silently ignores `max_completion_tokens` and an
|
|
70
|
-
output cap that does not bind is a money hazard. `provider: 'gemini-api'` (PRD item 31.3)
|
|
70
|
+
output cap that does not bind is a money hazard. The judge's calls additionally carry the per-call `thinkingMode: 'off'` where bare-agent's `thinkingSupport` says the model is switchable (read through `judgeCallOptions`, applied by `defaultJudgeLoop`; F192) because deepseek-flash's default thinking ate the 4000-token locate budget; workers are untouched. `provider: 'gemini-api'` (PRD item 31.3)
|
|
71
71
|
takes the same `Loop` path, reads `GEMINI_API_KEY`, and maps two real tiers
|
|
72
72
|
(`sonnet` → `gemini-2.5-pro`, `haiku` → `gemini-2.5-flash`). It is
|
|
73
73
|
**ADMITTED-PENDING-PROBE**: it has ZERO runs, and the probe rule is not waived for it —
|
|
@@ -179,9 +179,10 @@ the step plan at run time (gated by `validatePlan`); the human signs only:
|
|
|
179
179
|
| field | shape | notes |
|
|
180
180
|
|---|---|---|
|
|
181
181
|
| `goal` | non-empty text | what the agent plans against |
|
|
182
|
+
| `jobLines`, `inputs` | optional (P7). `jobLines: [{ n, text, rule, rules? }]` — the person's own numbered job lines, `rule` = their `~` lines joined with `'; '` (`''` when none), `rules` = the same `~` lines as an array BESIDE `rule` (present only when the line has any; must join back to `rule` exactly; the Reuse card prefers it so an example's own `; ` is not cut; an older spec lacking it renders by splitting `rule`); `inputs: [{ n, label, value }]` — line 1 the repo (absolute path), lines 2+ tracked files inside it. Exact fields, numbered 1..n in order | what the Job tab and Reuse show back exactly as typed (the signed `goal` stays the model's confirm sentence, which the worker reads). **A spec without them keeps its `jobSpecHash`** (never rewritten); only NEW jobs carry them, so a new job's `workflowKey` differs from an old one's. `inputs` is OPEN on a reuse (`REUSE_OPEN_SPEC_FIELDS`), so `workflowKey` ignores it; `jobSpecHash` covers it. Parsed by `parseJobBlock` / `parseInputs` (`src/jobblock.js`, one owner for the panel and the CLI) |
|
|
182
183
|
| `verdictType` | `green` \| `soft-green` | declared radio, never inferred (`VERDICT_TYPES`, frozen). **AUTHORING offers both (`MENU_CLASSES`).** `soft-green` has its judged floor (`judged-floor`, signed rubric card + calibration gate) and is the forward path for a job needing judgement, paired with the end-of-run review door. Every `request-red` (e.g. for a locked TOOL verb, not a verdict class) also carries `lib` — the territory the demand lands against, stamped at the emit site (`verdictType` → `bareloop`, a locked tool verb → `bare-agent`): the ledger keys and its `suggestedAsk` seed on it, so a bareloop-catalogue refusal never files as an upstream ask |
|
|
183
184
|
| `close` | **an ORDERED LIST of named stages** `[{ name, cmd, expect, judged?, gapKeep?, offer?, needs?, direction? }, ...]` (PRD v1.28), or a close object (table below) | the destination, the only thing hand-authored; the check menu DERIVES from it (below). The plan flow executes a staged close directly and adapts a bare `predicate` object into a one-stage list; an object close names no command to run, so the plan flow refuses one at runtime as `close-unsupported`. The modern route to a judged or human-decided close is a staged `closeDecl` (`judged-floor` / `human-confirms` stage kinds), not this object form |
|
|
184
|
-
| `closeDecl` | **the AUTHORED close** `{ genre: "TYPES", lang: "js"\|"python", stages: [{ name, kind, params }], notes? }` | the ALTERNATIVE to `close`, and the point of the close-authoring rung: the user answers an interview and an LLM composes a DECLARATION over kinds whose implementations we own — never a script, never a shell fragment, never a new kind. **`close` and `closeDecl` are alternatives**: declaring both reds `close-duplicated` (two closes are two arbiters). It is HARD-class by construction (`CLASS_BY_CLOSE.declared`), so a locked verdict on one reds `close-hierarchy` as well as `request-red`. The declaration is validated by `validateCloseDecl`; the TREE-GROUNDED half of that gate (the path rule, and the scoped-job derivation that arms the F84 one-population law) is DEFERRED at spec-validation time — a job spec is validated with no repository in hand — and the runner re-runs it GROUNDED against the real seed before any stage and before any token. It stores the counting RULE and never a number (D12): there is no seed field, and `baseline: "seed"` is measured at each run's own HEAD |
|
|
185
|
+
| `closeDecl` | **the AUTHORED close** `{ genre: "TYPES", lang: "js"\|"python", stages: [{ name, kind, params, fromLine? }], notes?, refused? }` | the ALTERNATIVE to `close`, and the point of the close-authoring rung: the user answers an interview and an LLM composes a DECLARATION over kinds whose implementations we own — never a script, never a shell fragment, never a new kind. **`close` and `closeDecl` are alternatives**: declaring both reds `close-duplicated` (two closes are two arbiters). It is HARD-class by construction (`CLASS_BY_CLOSE.declared`), so a locked verdict on one reds `close-hierarchy` as well as `request-red`. The declaration is validated by `validateCloseDecl`; the TREE-GROUNDED half of that gate (the path rule, and the scoped-job derivation that arms the F84 one-population law) is DEFERRED at spec-validation time — a job spec is validated with no repository in hand — and the runner re-runs it GROUNDED against the real seed before any stage and before any token. **Job-line coverage (P7, `checkJobLines` in `validateDeclaration`, $0, fed back through the revise loop):** with `jobLines` handed in, every stage the drafter writes carries `fromLine: [N, ...]` — a non-empty, duplicate-free ARRAY of real job lines, so one check (e.g. the one judged stage) may serve several lines, the mandatory guards carry none, and every numbered line has at least one stage OR a `{line, reason}` in `closeDecl.refused` — a note in `notes` does NOT cover a line (`job-line-uncovered`). `validateCloseDecl`/`validateJob` run it when the spec carries `jobLines`. It stores the counting RULE and never a number (D12): there is no seed field, and `baseline: "seed"` is measured at each run's own HEAD |
|
|
185
186
|
| `checks` | **RETIRED** (PRD v1.28/v1.32) | hand-authored checks are gone, not merely discouraged: declaring `checks` reds `checks-derived` by name. The check menu is DERIVED from the close's own stages instead — see **Staged close** below. The hazard this removes is measured, not theoretical: job #5's three hand-written checks were re-implementations of three stages the close already ran, and a hand-carved copy can drift LENIENT (the worker passes the operator's ruler and fails the real inspection) |
|
|
186
187
|
| `tools` | optional unique subset of `TOOL_MENU` (14 verbs, below) | the CEILING every plan step's grant must fit inside (omitting it means the full menu — and the hash is taken over that RESOLVED form, so a `TOOL_MENU` widening flips an omitted-`tools` spec's hash and forces a re-sign; see `jobSpecHash`, MED-1); `run` is `LOCKED_TOOLS` and reds `request-red` — locked-but-listed, and the red IS the admission evidence the ledger tallies (a typo stays `invalid-value`). A ceiling of write-class and store-class verbs ONLY reds `invalid-value`: the scout surveys read-only, so it would be handed an empty menu and survey blind |
|
|
187
188
|
|
|
@@ -617,7 +618,7 @@ a step, or test it without a provider.
|
|
|
617
618
|
|
|
618
619
|
| step | call | what it is |
|
|
619
620
|
|---|---|---|
|
|
620
|
-
| interview | `runInterview({ verdictType, answers, repoPath })` → `{ ok, answers, verdictType, refusal, reds }` | PURE — no model, no repo, no clock. The interview ADMITS only what is on the menu (`MENU_CLASSES`): nothing is locked today (`LOCKED_CLASSES` is empty), and any pick off the menu returns a refusal rather than `ok:true` — ask `MENU_CLASSES` which classes can be authored, never this sentence. The interview asks NOTHING about a genre and NOTHING about the repository. **Ask the library for the set and its numbers — never hardcode a count or a slot number**: the green set has lost several slots since it was frozen (D13's genre confirm; the repo question, dropped 2026-08-15 because `repoPath` is mandatory structured input; and, at PRD item 33 M3 piece 3, the old "which files change/read" question, replaced by Source and Destination — MECHANICAL fields, proven against the machine, never a numbered answer), and it renumbers CONTIGUOUSLY from 1 each time rather than leaving a gap. Today the green set is
|
|
621
|
+
| interview | `runInterview({ verdictType, answers, repoPath })` → `{ ok, answers, verdictType, refusal, reds }` | PURE — no model, no repo, no clock. The interview ADMITS only what is on the menu (`MENU_CLASSES`): nothing is locked today (`LOCKED_CLASSES` is empty), and any pick off the menu returns a refusal rather than `ok:true` — ask `MENU_CLASSES` which classes can be authored, never this sentence. The interview asks NOTHING about a genre and NOTHING about the repository. **Ask the library for the set and its numbers — never hardcode a count or a slot number**: the green set has lost several slots since it was frozen (D13's genre confirm; the repo question, dropped 2026-08-15 because `repoPath` is mandatory structured input; and, at PRD item 33 M3 piece 3, the old "which files change/read" question, replaced by Source and Destination — MECHANICAL fields, proven against the machine, never a numbered answer), and it renumbers CONTIGUOUSLY from 1 each time rather than leaving a gap. Today (P7) the green set is ONE question — "The job", the person's numbered lines with their `~` rules, rendered by `renderJobLines` (`src/jobblock.js`); the `soft-green` set is that one plus Judge Examples, DERIVED from the `~ PASS:` / `~ FAIL:` lines of the same block (`renderExamples`, each example kept with its line number) — the judged floor's one required input, compiled into the signed rubric card and the frozen calibration set. Inputs (`label: value` lines) travel beside the answers as `inputs`, and `authorCloseForJob({ jobLines, inputs })` tells the drafter "the person pointed at these inputs: N label path" and orders `fromLine` tagging. `verdictType` and `repoPath` are STRUCTURED input, never parsed out of prose. Answers are scrubbed at INGEST |
|
|
621
622
|
| survey | `runAuthorScout({ workdir, provider, attempts?, ceilingUsd? })` → `{ state, facts, reason, meta, raw, raws, calls, budgetStop }` | a bounded READ-ONLY LLM survey. Read-only by MENU CONSTRUCTION (`AUTHOR_SCOUT_VERBS` = the full menu minus write-class and store-class verbs), 8 rounds, F59's reserved toolless final round. **`state: 'ABSENT'` means the scout did not complete — never "no special facts are needed"**, and a parsed `{}` is one of its five ABSENT routes. Up to `SCOUT_ATTEMPTS` (**3**, hardcoded — PRD v1.58) attempts: `attempts` is TIGHTEN-ONLY and CLAMPS rather than throws (floor **1**, because a scout that never ran is an ABSENT nobody can act on), the direction every cap in this system runs in. **A retry fires on the typed MALFORMED class alone** (`SCOUT_RETRY_CAUSES` = `empty` \| `unparseable`); what is excluded is excluded for a reason — `call-failed` covers transport and `truncated:max_tokens`, both provider-red with NO redraft; `short` is F59's cut-off population and already has its own instrument INSIDE the attempt; `non-object` and `empty-object` are valid JSON with wrong or vacuous content, which is a SEMANTIC failure, and F38/F39 measured what re-asking one buys (the same distribution, sampled twice) — that is the self-healing line, and it is not crossed here. The re-ask is TOOLLESS over the conversation the survey already produced (the repository was already read; only the emission was unreadable) and names the mechanical parse error and nothing else. **There is no JSON repair behind it and there never will be** — a repairer decides what the model MEANT and writes it down as though the model had said it, in the one artefact whose whole job is to be honest about what a repository contains. `meta.attempts` / `meta.attemptsAllowed` record what was paid for; every attempt is metered under its own label (`author-scout`, `author-scout#2`, …) and leaves its raw behind. **`ceilingUsd` is the operator's money ceiling** (PRD v1.62) — checked BETWEEN attempts and before F59's reserved round, so no paid call escapes it; `null`/absent is UNBOUNDED, and a non-null non-finite value THROWS (the shared `capStop` seam — see `authorClose` below). When it ends the ladder, `budgetStop` names which stop (`cap-halt` \| `pricing-red`) and a survey that was never asked for carries the typed cause `not-funded` — deliberately OUTSIDE `SCOUT_RETRY_CAUSES`, because a retry is precisely what the cap forbade |
|
|
622
623
|
| listing | `buildSeedListing({ workdir, seedRef, sourcePaths, testPaths })` | mechanical, `$0`, no model. `files` is the WHOLE tree (what the validator judges paths against); `block` is scoped to the survey's own paths and capped in ANNOUNCED tiers (what the prompt carries). Handing the validator the scoped half would make a job scoped to `src/` read as whole-tree and silently disarm the one-population law |
|
|
623
624
|
| authoring | `authorClose({ workdir, seedRef, lang, verdictType, answers, scout, listing, generate, ceilingUsd? })` | the grounded loop: author → validate → run EVERY stage at the seed → feed the MEASURED results back → revise, bounded at `MAX_REVISIONS` (2) — a passed `maxRevisions` is TIGHTEN-ONLY, so a caller may LOWER that ceiling and never buy more revise rounds than the constant (floor **0**: one authoring call with no revise round is a legal ask, and unlimited revising would launder thrash as adaptation) — early-stop on an unchanged declaration. The declaration is emitted through a SCHEMA-FORCED TOOL CALL (`declare_close`), never parsed out of prose; the feedback is EXECUTION OUTPUT only — no model ever reviews another model's close. The return carries **`raws`**: what every model call actually SAID, the scout's attempts absorbed together with the declaration's, in the cost book's own order and under the same labels it meters. Each is `{ label, attempt, bytes, trimmed, text, cause, reason }` through the ONE persist helper (`scrubRaw` — redacted over the ONE `SECRET_PATTERNS` inventory, since a raw is the record most likely in this system to carry a live credential; bounded at `RAW_PERSIST_MAX` (8000) with the trim ANNOUNCING its own full size, and the cut walked back off a continuation byte so a multi-byte character is never split). `raws` is present on EVERY path **including the `$0` preflight refusals** — the path that spends nothing more is exactly the one whose evidence used to vanish with the process. `iterations` records what each turn MEANT (declaration, validation, seed read); `raws` records what it SAID, and a malformation is only ever visible in the second. **`ceilingUsd` is the same operator ceiling the survey took** — the scout's spend is ABSORBED into this loop's cost book before its first call is weighed, so spend already incurred folds in and re-entering cannot silently widen it. It is checked immediately before EVERY paid call (the author call, each revise, and each malformed-emission retry). **A MALFORMED ceiling is an ERROR, not a silent unbounded run** (v1.64 §3): the seam every ceiling read goes through (`capStop`, `src/text.js`) THROWS on a non-null non-finite value — `'2.50'`, `NaN`, `Infinity`, `true`, `{}` — before the first paid call, and the throw propagates uncaught rather than being caught into an ABSENT survey, because a caught one would hand you the very silent-unbounded run it exists to prevent. `null`/omitted is the STATED operator choice for unbounded; `0` and negatives are finite, legal, and cap-halt immediately. The advertised ceiling and the enforced ceiling can therefore never be different numbers. A `cap-halt` here can arrive with `ok:true` — a close that was already validated and measured survives the money stop, exactly as a late provider casualty does. **F176 (fixed):** "kept the last accepted revision" is not the same claim as "kept the best revision" — a later revise round can VALIDATE fine yet MEASURE worse (every stage instrument-stops rather than reading a real red/green verdict). `declaration`/`seedRead`/`finalFrom`/the returned `genreEnv.dropped` are therefore the NEWEST SOUND measured iteration, not simply the last one, when the last one instrument-stopped and an earlier one did not; `stop` itself never changes. The swap is never silent: the return also carries **`fellBack`** — `null` on every run where it never fired, else `{from, to, brokenStages}` naming which iteration was dropped, which now stands, and which of the dropped one's stages instrument-stopped — reported through `onPhase('author-fallback', …)` as it happens and printed at the terminal readout (`fellBackLines`, `src/authorreadout.js` — lifted out of `scripts/author-readout.mjs` by PANEL-BUILD.md P0; called from `src/authorrun.js`). **F179/F180 (history):** bare-agent 0.42.0's OpenAIProvider used to THROW mid-call on malformed tool-call JSON (a raw SyntaxError, after the billed round), crashing past this function; a 2026-09-15 stopgap caught it as a `provider-red`/`ask.providerError` casualty (booked `costUsd: null`) so a sound prior declaration was kept via the fallback above rather than discarded. **F179 (bare-agent 0.43.0, BA-27 — the real mechanism):** the throw is fixed upstream. `OpenAIProvider`/`OllamaProvider.generate` now return a malformed round PRICED (real `usage`, never `costUsd: null`) with `toolCalls: []` plus their own `malformedToolCall: {name, error}` marker, which `Loop.run` surfaces unchanged on its return. For the authoring declaration and confirm-turn calls, `askStructured` reads that marker and RETRIES through the existing malformed-emission retry ladder (`MAX_STRUCTURE_RETRIES`) instead of stopping the run — nothing is ever repaired, and a malformed FIRST call no longer needs an earlier sound declaration to fall back to. The generic provider-casualty fallback above (idle-timeout, etc.) still applies for every other admitted class. **F184 (fixed, worker path):** the scout and the worker path never joined that retry ladder (still true — retrying is arbiter-adjacent, out of scope), but the worker path's `ask()` (`src/planrun.js`) now at least READS the marker where it already sees it and emits a distinct spine record, `worker-malformed-tool-call` (`{phase, iteration, name, error}`), before returning `r` unexamined exactly as before — no retry, no change to strikes/ladder/attempt counting or verdict routing, purely the same visibility bar this section's authoring-path fix already set |
|
|
@@ -625,6 +626,49 @@ a step, or test it without a provider.
|
|
|
625
626
|
| assembly | `assembleSpec(specDraft, { closeDecl, verdictType })` | folds the authored half into the OPERATOR's half. Budgets, the fence, cadence, escalation and the provider are never authored by anything here. The GOAL is passed through, not generated. **A draft that ALREADY carries the authored half is REFUSED — it THROWS, never merges and never overwrites.** `AUTHORED_SPEC_FIELDS` (`close`, `closeDecl`, `verdictType`) names the fields this fold WRITES, as data, so the refusal and the fold cannot disagree about which half of the spec is which; the rule is `job.js`'s one step earlier (two closes are two arbiters, and picking one silently is how a signed artefact stops meaning what the signer read). `src/authorrun.js` (lifted out of `scripts/run-author.mjs` by PANEL-BUILD.md P0) asks the same question at **$0 BEFORE the scout**, so the answer never arrives after the model has been paid |
|
|
626
627
|
| the three gates | `prepareSigning({ spec, workdir, seedRef? })` → `{ ok, specHash, gates, work, guards, refusal }` | D9, and it NEVER signs |
|
|
627
628
|
|
|
629
|
+
**Drafting stops and try-3 questions (P7, F215).** (1) A drafter round cut off at the output cap (bare-agent tags it
|
|
630
|
+
`error: 'truncated:max_tokens'`, neutral `stopReason: 'max_tokens'`) is a FAILED TRY on `askStructured`'s retry ladder with the
|
|
631
|
+
reason "the AI's answer was cut off at the output limit" (`axis: 'output-truncated'`) — NOT `provider-red`; all attempts stay
|
|
632
|
+
booked, `AUTHOR_MAX_TOKENS` is never raised, and only a real transport failure stays `provider-red`. `draftStopText`
|
|
633
|
+
(`src/authorreadout.js`) words any drafting stop that has no wording of its own; the panel puts it on the failed step. (2)
|
|
634
|
+
`authorClose({ offerQuestions })` (default `false`; the CLI never sets it) offers the optional `questions` property on the LAST
|
|
635
|
+
try of the full ladder only, and only when the two tries before it both failed — validation OR the seed read (hamr ruled 2026-10-10; this DIFFERS
|
|
636
|
+
from fwdloop's validation-only rule): a seed-read failure is a work stage that instrument-stopped or a close with no work stage
|
|
637
|
+
red at the seed (vacuous), and its lines are the `fromLine`s of the stages named, else every job line (`seedReadFailure`); a try
|
|
638
|
+
whose EVERY structure attempt was cut off at the output cap also counts as failed (hamr ruled 2026-10-10; a cut-off names no line, so
|
|
639
|
+
every job line counts), while a try cut off once but finished by a later attempt is judged by that finished attempt (the ladder carries
|
|
640
|
+
on to try 3 after an all-cut-off try 2 only when that gate can open, i.e. try 1 failed too);
|
|
641
|
+
the machine keeps at most
|
|
642
|
+
`MAX_QUESTIONS` (2) — each on a job line the previous try's reds named — and logs the rest in `iterations[].droppedQuestions`;
|
|
643
|
+
the drafter is never told a number. It then stops `questions-open` (`questions: [{line, question}]`, nothing validated).
|
|
644
|
+
`authorCloseForJob({ answerQuestions })` is the seam that asks the person (required answer, no skip), takes back the job lines
|
|
645
|
+
with each answer added word for word as a `~` rule, and runs the draft once more over the same survey, listing and confirmed
|
|
646
|
+
plan with the first leg's spend carried as prior spend and `offerQuestions: false`. The confirm turn's own `questions` (the
|
|
647
|
+
plan's open questions) are a separate, unchanged mechanism: they are asked before drafting, about the plan; try-3 questions
|
|
648
|
+
are asked after two failed drafts, about a job line.
|
|
649
|
+
|
|
650
|
+
**Drafter thinking knob (hamr 2026-10-10; DEFAULT OFF, hamr 2026-10-11; via bare-agent's switch since v0.51).** `draftThinking: 'on' | 'off'` (any other
|
|
651
|
+
value throws, an operator param, never read from env inside the library, not in the panel UI) is owned by `draftCallOptions(provider, mode)`
|
|
652
|
+
(`src/providers.js`). bareloop keeps NO per-model thinking list: bare-agent >= 0.51 owns the request shapes (`thinkingMode: 'on' | 'off'`, per provider
|
|
653
|
+
and model) and `thinkingSupport(kind, model)` (pure lookup, no network) says whether a model can be switched. The default is `'off'`. The frozen ON/OFF contrast
|
|
654
|
+
(`docs/logs/2026-10-10-drafter-thinking-contrast-prereg.md`) measured deepseek-flash only; the default applies to every switchable model, drafting and
|
|
655
|
+
judge alike, by hamr's 2026-10-11 ruling (no per-model exceptions), and is not measured live on other providers (Anthropic, Gemini, OpenAI). With mode `'off'`, a call carries the per-CALL option
|
|
656
|
+
`thinkingMode: 'off'` only when `thinkingSupport` answers `switchable: true` (e.g. deepseek-flash, claude-sonnet-5, gpt-5); a model measured NOT switchable
|
|
657
|
+
(claude-opus-5-5) gets nothing (asking would throw before send), and an `'unverified'` model (not measured by bare-agent) also gets nothing on this
|
|
658
|
+
default path, so an unknown or local model's drafting never breaks by default. `'on'` sends nothing (the model's own thinking, byte-identical to a call with
|
|
659
|
+
no knob). The raw `thinking` option is never sent. It is threaded explicitly: `makeLoopGenerate(provider, { draftThinking })` for the author, revise,
|
|
660
|
+
structure-retry and confirm calls; `authorCloseForJob({ draftThinking })` for the scout (it hands `runAuthorScout` a `callOptions`);
|
|
661
|
+
`createSession(card, { draftThinking })` in the panel does both. The CLI drafter (`src/authorrun.js`) passes no value, so it gets the same default
|
|
662
|
+
through the same two seams. The rubric judge uses the same rule (`judgeCallOptions`: `thinkingMode: 'off'` only where switchable; a judge on
|
|
663
|
+
claude-opus-5-5 does not throw). Worker calls are unchanged. Each panel `draft-log.jsonl` `call` entry records `thinking` = what was asked and sent
|
|
664
|
+
(`draftThinkingSent`): `'off'` (the switch was sent), `'on'` (mode on, nothing sent), `'not-switchable'` (measured not switchable, nothing sent) or
|
|
665
|
+
`'unverified'` (not measured, nothing sent); plus `thinkingApplied` (bare-agent's own verdict for that call's run: `true` / `false` = reasoning seen
|
|
666
|
+
although `'off'` was sent / `null` = unreadable) when thinkingMode was sent, absent otherwise. The panel's drafting list opens with a code-owned
|
|
667
|
+
`thinking` row (`thinkingRowText`, shown with a check, before "checking setup"): `thinking off`, `thinking on`, `thinking on (this model can't turn it off)`,
|
|
668
|
+
`thinking on (not verified for this model)`, or, once any call reports `thinkingApplied: false` after `'off'` was sent, `thinking on (the model thought
|
|
669
|
+
anyway)` (a `step-label` record in the draft log carries the change). A Reuse workflow session drafts nothing and has no row.
|
|
670
|
+
|
|
671
|
+
|
|
628
672
|
**D5 — the mandatory guards are SHOWN and FIXED, and the battery keys off the VERDICT
|
|
629
673
|
CLASS** (PRD v1.57 §2 — what a battery is FOR is the class of dishonesty that class of
|
|
630
674
|
verdict admits). `CLASS_BATTERIES` is the attachment point (structure, un-removability,
|
|
@@ -677,6 +721,16 @@ attempt PLUS its close. Unpriced is never free: a null cost is a `pricing-red` s
|
|
|
677
721
|
never retried, and the ladder is otherwise ONE retry (`JUDGE_ATTEMPTS`) — after that a broken
|
|
678
722
|
judge is an instrument stop, never a red about the tree.
|
|
679
723
|
|
|
724
|
+
**The rulebook, and what each rule can raise** (`JUDGE_RULES` in `src/judged.js`; every rule carries a plain-words `means` the case-proposal prompt renders, so a proposed practice case never expects a red the rule cannot raise). The judge only POINTS (quotes lines verbatim); code does every check; unsure is red; the judge never says pass/fail. Quotes are matched by bareguard's `quoteIn(..., {wholeLines:true})` (bareguard `^0.21.0`): a contiguous run of whole artifact lines, so a bare fragment cannot pass by hiding in a longer line, while a dropped leading `* ` of a JSDoc tag line still matches. `has-doc` checks only that a JSDoc block sits directly above the function (read off the artifact by `docBlockAbove`; the judge's quote is a pointer that must lie inside that block), so a documented function never breaks it. **`says-what`** (the doc's description must say more than the function's own name) reds when the described prose, minus a fixed list of function words and the name's own words (camel/snake/kebab, light inflections, and a prefix echo such as `init`/`initialize` when the shorter word is at least 4 letters), leaves nothing; no description, or an unlocatable quote, reds. It catches a pure name echo only ("Returns the total." for `getTotal` passes), and with NO doc block at all it stays silent and leaves the red to `has-doc` (the card is not required to carry both). `params` reds a declared parameter with no tag, an `@param` naming no parameter (each destructured slot absorbs one unmatched root tag), and, when the doc names any field of a simple destructured parameter, a missing field; `@param options` alone for `{ x, y }` passes. `returns` reds a returning function with no `@returns`. A practice case that expects a `has-doc` red on a function that has a block is refused as a legality red before any call (`validateCalibrationSet`).
|
|
725
|
+
|
|
726
|
+
**A judged stage may only judge files inside the job's write fence** (`judgedOutsideFence`, `src/authoring.js`, called from `validateDeclaration`, which every door reaches: the authoring revise loop, `validateCloseDecl` at signing and runner pre-flight, and `validateJob` for hand-written or reused specs). A path outside `writeScope` reds `judged-outside-write-scope` at draft time, because a finding on a file the run is fenced out of can never be fixed. With no fence to compare against (the plain-folder deferral) the rule is not run.
|
|
727
|
+
|
|
728
|
+
**One red count.** A judged stage's red COUNT is the number of distinct failing functions it itemizes (`redCount`/`redSet` on the stage detail, `src/kinds.js`; an unsure entry counts as one). The same number is the gap headline ("N red(s) across M of K artifact(s)") and the trend value the close-trend governor compares to best-so-far, direction always down (`trendValueOf`, `src/declaredclose.js`), so a repeated or growing judged failure strikes like any numeric stage instead of running to the wall. Spines written before this change carry the old headline (the card-item count).
|
|
729
|
+
|
|
730
|
+
**A refused draft or signing says WHY in plain words.** `proposalStopText` (a refused rubric proposal: `proposal-invalid` / `rubric-invalid`) and `signingStopText` (refused signing gates) in `src/authorreadout.js` are the one owner of the wording, printed by the panel's failed step and the CLI alike: code-written sentences per red kind naming the case and function, the real spend so far, and what to do; a practice-case/check mismatch is not claimed to be anyone's fault; an unknown red code is quoted, never dropped. The full scrubbed reds go into the session's `draft-log.jsonl` as a `step-reds` record, and the calibrating line shows the gate's real result (`calibrationSummary`: "N of 10 practice cases graded right, M of 5 attack tests resisted").
|
|
731
|
+
|
|
732
|
+
**The worker is warned about the `any` suppression guard.** A job whose actual close stages carry the js `any` suppression pattern gets one extra genre-owned line in the worker system prompt (`ANY_SUPPRESSION_WARNING`, `anySuppressionWarning`, `src/authoring.js`): the guard scans added comment lines too, so prose like `, any` trips it. The guard regex itself is unchanged.
|
|
733
|
+
|
|
680
734
|
**The two SIGNED artifacts of a judged close** (softgreen module 4, design §4.3/§4.4). The
|
|
681
735
|
softgreen interview's Judge Examples answer (PRD item 33 M3 piece 3 — one example to pass, one
|
|
682
736
|
to fail, and why; formerly two questions, Q6+Q7) compiles into the RUBRIC CARD and the FROZEN
|
|
@@ -745,12 +799,13 @@ signing record keeps WHAT was certified — `cardHash`, `casesHash`, `setHash` (
|
|
|
745
799
|
resolved judge identity), the graded rows and the battery — beside `judgeModel` itself.
|
|
746
800
|
`runCalibration` takes `judgeModel` as a REQUIRED argument and throws without one.
|
|
747
801
|
|
|
748
|
-
**D9.3 for a judged
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
802
|
+
**D9.3 for a close with a judged stage** (ruling 3, hamr: *"fix it now, we are delivering softgreen"*;
|
|
803
|
+
widened 2026-10-09: *"a rubric job should check if judge can judge (calibration) ... we can't close
|
|
804
|
+
everything upfront"*). A judged stage skips the seed read (ruling 8), so it can never be red at seed. For
|
|
805
|
+
ANY close carrying a judged stage, pure or mixed with mechanical stages, a PASSED calibration gate plays the
|
|
806
|
+
seed-red role (`seedProofIsCalibration`), and the record says so when no mechanical stage is red itself:
|
|
807
|
+
`gates.seedVerdict.satisfiedBy === 'calibration'`. A failed or missing calibration never satisfies it. A close
|
|
808
|
+
with NO judged stage is UNCHANGED and still needs a work stage red at the seed.
|
|
754
809
|
|
|
755
810
|
**THE JUDGE TIER IS INSIDE THE SIGNATURE, and the stage refuses a mismatch.** *"A judge-model
|
|
756
811
|
bump forces a full recalibration"* used to be an operator-side rule with nothing to fire on,
|
|
@@ -1296,7 +1351,7 @@ arm replaces the CLI-display cap rather than stacking on it; a non-capping arm l
|
|
|
1296
1351
|
installed *inside* the shim, so native never loses its bound. Default-flip pending a paid contrast;
|
|
1297
1352
|
see CHANGELOG for the replay numbers behind it.
|
|
1298
1353
|
|
|
1299
|
-
**`scout` (default `true`) — the operator-only off switch** (`docs/
|
|
1354
|
+
**`scout` (default `true`) — the operator-only off switch** (`docs/logs/SCOUT-CONTRAST.md`),
|
|
1300
1355
|
threaded verbatim from `runJob` to `runPlan`. `false` on a FRESH run (no `resumeSeed`) skips the
|
|
1301
1356
|
read-only survey entirely — `scout-start`/`scout-result`/`scout-empty` never fire, and a single
|
|
1302
1357
|
`scout-skipped { reason: 'operator-off', meaning }` records it (a RECORD, never silence — the
|
|
@@ -1863,7 +1918,7 @@ hand them to the one `Loop` that drives worker, scout and planner rounds; the ru
|
|
|
1863
1918
|
them once. No price on the row = no `rates` key anywhere = the guesstimate above, byte-identical to
|
|
1864
1919
|
before. bareloop keeps no price list of its own, ever.
|
|
1865
1920
|
|
|
1866
|
-
**The provenance is on the record, per round (`rateSource`).** The pinned bare-agent (`^0.
|
|
1921
|
+
**The provenance is on the record, per round (`rateSource`).** The pinned bare-agent (`^0.51.0`)
|
|
1867
1922
|
carries it on every metering payload, and bareloop forwards it VERBATIM (`rateSourceFields`,
|
|
1868
1923
|
`src/planrun.js`): every API `worker-round` (worker, scout, planner, fix loop) carries it beside
|
|
1869
1924
|
`pricing` today — `'caller'` when a customer price is on the key's row, else `'tier'`/`'default'`
|
|
@@ -3291,7 +3346,7 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3291
3346
|
close scripts need at run time. The correct flow is **`cd <bundle> && npm install`** (reads
|
|
3292
3347
|
the bundle's own `package.json`), then **`./node_modules/.bin/bareloop run . --repo <path>
|
|
3293
3348
|
…`**. `checkBundleDeps` (above) is the fail-safe that catches the wrong-shape install
|
|
3294
|
-
before any close ever runs; see `docs/
|
|
3349
|
+
before any close ever runs; see `docs/wiki/FINDINGS.md` F128 for the full account
|
|
3295
3350
|
(grounded in the real crashed spine) and `docs/product/EXPORT-BUILD.md`'s dated addendum.
|
|
3296
3351
|
|
|
3297
3352
|
**Minting-run line, corrected (F128).** The first-run notice used to print the shipped
|
|
@@ -3453,13 +3508,13 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3453
3508
|
re-attaches to it from the server's session map (nothing is stored page-side). A panel restart mints a new token and
|
|
3454
3509
|
loses the in-memory sessions: the page then shows one "reload this page" line and stops the Chat poll (no auto-reload;
|
|
3455
3510
|
drafting money already spent stays booked via `draft-spend.json`, below). `GET /api/author/:id` polls the session's state
|
|
3456
|
-
(phase, chat messages, cost, `
|
|
3511
|
+
(phase, chat messages, cost, `specHash` once prepared). `POST /api/author/
|
|
3457
3512
|
:id/send {text}` answers whatever the confirm turn is currently asking (the `language` pick or a plan's
|
|
3458
3513
|
own follow-up question — the old `worseThanBefore` ask is retired, 2026-09-28) — refused outright when the pending ask is the
|
|
3459
|
-
plan MENU itself (`{ok:false}`, no route from chat text to a plan decision, ever).
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3514
|
+
plan MENU itself (`{ok:false}`, no route from chat text to a plan decision, ever). There is no
|
|
3515
|
+
typed-revise route (2026-10-09): the panel's **Revise** button, shown only while a plan waits for the OK,
|
|
3516
|
+
abandons the draft (`/abandon`; spend already booked stays booked) and reopens the New job card with the same
|
|
3517
|
+
values in every box, editable, for a fresh Start drafting; no change request is ever sent to the model. `POST /api/author/:id/sign-prepare` is the menu's
|
|
3463
3518
|
own `confirm` pick — it runs gates 1–4 and reaches `phase:'prepared'` with a `specHash`; it
|
|
3464
3519
|
NEVER signs. `POST /api/author/:id/sign {specHash}` is the ONLY route that spawns a run —
|
|
3465
3520
|
it refuses unless `phase==='prepared'` and the posted hash matches the session's own
|
|
@@ -3471,7 +3526,7 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3471
3526
|
server's environment with `~/.config/bareloop/.env` merged in per request (a key is never read
|
|
3472
3527
|
into or sent to the page — a missing one refuses the session at $0, naming only the env var). `src/panel/authorsession.js` takes
|
|
3473
3528
|
test-only DI seams (`scout`/`generate`/`confirmGenerate`/`authorFn`/`prepareSigningFn`) so
|
|
3474
|
-
a test can drive the real ask()-channel/
|
|
3529
|
+
a test can drive the real ask()-channel/hash wiring without a live provider call;
|
|
3475
3530
|
none of them are reachable from `authorroutes.js`'s real construction path. Sessions live
|
|
3476
3531
|
under `~/.config/bareloop/panel-sessions/<id>/` (each one's own `resolved-spec.json` and
|
|
3477
3532
|
`signing.json`, the two files `bareloop author` itself already writes). Edit/re-sign, the
|
|
@@ -3516,7 +3571,7 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3516
3571
|
died, else failed. A run that DIED (no `job-end`, runner gone) leaves its last part in flight: when that part has no outcome
|
|
3517
3572
|
and no cut-off, the server marks it `died` (`[?] died`), never the "happened, so passed" default — replay alone cannot tell
|
|
3518
3573
|
died from still running, so `getRunDetail` decides it from the same `death` verdict as the run's status. A live run's open
|
|
3519
|
-
part stays running; a part that finished before the death keeps its own status. The page's map
|
|
3574
|
+
part stays running; a part that finished before the death keeps its own status. The page's step map is a row of fit-to-text chips that flow and wrap (no truncation, no horizontal scroll), `[sign title]` each, in plan order, with an arrow between neighbours and a leading arrow on a wrapped line; the status word (from the same table) rides in the chip's tooltip, a retried step has a dashed border and a `↻N` tries mark, a leg boundary a dotted right edge, and a click jumps to that part's Audit row (the live part reads `[▶]`); the step cards and Audit rows
|
|
3520
3575
|
show the word only; no surface says "done".
|
|
3521
3576
|
**Resume (item 2)** → `POST /api/runs/:runid/resume` (`src/panel/runroutes.js`), behind
|
|
3522
3577
|
`checkHumanGuard` (token + own address, like `/api/author/*`); body `{budgetUsd?, maxWallMin?}`
|
|
@@ -3574,8 +3629,7 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3574
3629
|
fills the card through this same path (a pick, then `change` returns to the empty search; Deterministic or Rubric leaves reuse).
|
|
3575
3630
|
It opens the Chat tab's New job card
|
|
3576
3631
|
filled from the SIGNED job: a NEW run (new runid), never a rerun, nothing drafted ($0). **Only five boxes are
|
|
3577
|
-
open —
|
|
3578
|
-
guardrails, judge examples) is the signed workflow, greyed, and the SERVER refuses a start that changed one
|
|
3632
|
+
open — Inputs, Destination, Model, $ cap, Time cap**; every other box (check type, job name, The job) is the signed workflow, greyed, and the SERVER refuses a start that changed one
|
|
3579
3633
|
(`400 "<Field> is locked on a reused workflow — Clear the card to change it"`; the page is never trusted).
|
|
3580
3634
|
Changing a locked box is Clear (the Chat card's top-right button), which drafts. Routes, behind `checkHumanGuard`:
|
|
3581
3635
|
`GET /api/author/start-from?runid=<id>` or `?import=<id>` → `{ok, origin, card, from:'signed job'|'imported job',
|
|
@@ -3666,7 +3720,7 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3666
3720
|
live card reads "$X so far · running Ym" exactly like the right pane.
|
|
3667
3721
|
|
|
3668
3722
|
- **`bareloop run-u <flags…>`** (PANEL-BUILD.md P0 task 2/4) → the person-path run flow
|
|
3669
|
-
(the JOBS-table/`--spec` runner, resume, the review door — `docs/
|
|
3723
|
+
(the JOBS-table/`--spec` runner, resume, the review door — `docs/wiki/FINDINGS.md`'s
|
|
3670
3724
|
U-mode). `src/cli.js`'s `run-u` dispatch hands `rest` straight to
|
|
3671
3725
|
**`src/userrun.js`'s own `main(argv, deps)`**, unparsed — that function is the flag
|
|
3672
3726
|
grammar's ONE owner, and `scripts/run-u.mjs` calls the exact same function directly, so
|
|
@@ -3688,7 +3742,7 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3688
3742
|
fed by each flow's `deps.invokedAs`.
|
|
3689
3743
|
|
|
3690
3744
|
- **`bareloop interview <flags…>`** (PANEL-BUILD.md P0 task 3/4) → the close-authoring
|
|
3691
|
-
interview, at the terminal, one question at a time (D10; `docs/
|
|
3745
|
+
interview, at the terminal, one question at a time (D10; `docs/wiki/FINDINGS.md`'s
|
|
3692
3746
|
interview surface). `src/cli.js`'s `interview` dispatch hands `rest` straight to
|
|
3693
3747
|
**`src/interviewrun.js`'s own `main(argv, deps)`**, unparsed — the same one-owner shape
|
|
3694
3748
|
`run-u` uses, and `scripts/run-interview.mjs` calls the exact same function directly.
|
|
@@ -4277,7 +4331,7 @@ blast radius.
|
|
|
4277
4331
|
|
|
4278
4332
|
## Gotchas
|
|
4279
4333
|
|
|
4280
|
-
*Recorded here as they're found (repo-side friction goes to `docs/
|
|
4334
|
+
*Recorded here as they're found (repo-side friction goes to `docs/wiki/FINDINGS.md`).*
|
|
4281
4335
|
|
|
4282
4336
|
- **`non-green-verdict` is a misleading refusal verb, and it is kept on purpose.** When
|
|
4283
4337
|
`runInterview` refuses a job for having no git repository (`repoPath` absent), the refusal
|
|
@@ -4298,3 +4352,12 @@ blast radius.
|
|
|
4298
4352
|
an append-only record that captures a key captures it forever.
|
|
4299
4353
|
- **The spine is append-only** and the single source of truth; every UI (panel included)
|
|
4300
4354
|
is a pure observer of it.
|
|
4355
|
+
|
|
4356
|
+
## The job block (P7) — "The job" + "Inputs"
|
|
4357
|
+
|
|
4358
|
+
Goal, Success, Guardrails and Judge examples are one box, **The job**, and Source is **Inputs** (CLI interview and panel card alike).
|
|
4359
|
+
|
|
4360
|
+
- **The job:** each plain line is numbered 1, 2, 3 (the number sits in a gutter outside the box). A `~` line has no number and belongs to the numbered line above; consecutive `~` lines join into ONE rule with `'; '`. A `~` line starting `PASS:` / `FAIL:` is a judge example (a rubric check needs at least one of each; a deterministic check refuses them); any other `~` line is a guardrail. A `~` before line 1 is a $0 red. Empty lines are ignored. `parseJobBlock(text)` → `{ ok, lines: [{ n, text, rule, examples }] }` or `{ ok:false, error }`; `jobBlockFitsCheck(lines, rubric)` is the Check-type cross-check.
|
|
4361
|
+
- **Inputs:** one `label: value` per line, numbered. Line 1 = the repo (absolute path). Lines 2+ = files or folders inside that repo, repo-relative; `proveInputFiles(inputs)` (`src/source.js`) proves them at $0 before any token — exists, inside the repo, tracked, and (files) not an environment file by name, not a symlink, text, under the size ceiling, a known secret shape in a file's content is MASKED wherever bareloop records it, never refused (the P6 worktree door ruling; `.env` by name and symlinks stay refused). A miss is `Inputs, line N: ...`. Search lines and files outside the repo are `only files inside the repo for now`. The panel proves them in `POST /api/author/start` (400 naming the line); the CLI re-asks.
|
|
4362
|
+
- **Drafter:** every stage carries `fromLine`, or the line is listed in `closeDecl.refused` with a reason (see `closeDecl` above); the confirm turn's checks are `{ text, fromLine: [N, ...] }`; the plan draws a multi-line check under EACH line it serves.
|
|
4363
|
+
- **Panel:** the card is `{ jobName, checkType, model, jobText, inputs, destination, capUsd, maxWallMs }`. The confirm plan is a structured chat message `{ role: 'bot', kind: 'plan', plan }` and the Job tab reads the SIGNED spec through the same model (`src/panel/jobplan.js`: `planFromConfirm`, `planFromSpec`); the page draws both with one `renderJobPlan()`. `GET /api/runs/:id/job` returns `plan`, `jobLines`, `inputs`, `specHash`, `signedAt` (the run's FIRST `job-start`) and `signed` (`YYYY-MM-DD HH:MM · <first 6 of specHash>`, local time). An older spec (no `jobLines`) renders the signed goal as line 1 with its stage names as checks and the note `older job — numbered lines were not saved`; Reuse / Edit / Resume prefill the same fallback.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bareloop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.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",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"check:lockfile": "node scripts/check-lockfile.mjs"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"bare-agent": "^0.
|
|
31
|
-
"bareguard": "^0.
|
|
30
|
+
"bare-agent": "^0.51.0",
|
|
31
|
+
"bareguard": "^0.21.0",
|
|
32
32
|
"litectx": "^0.32.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|