bareloop 0.42.0 → 0.44.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 +88 -0
- package/bareloop.context.md +103 -41
- package/package.json +2 -2
- package/src/authorflow.js +26 -11
- package/src/authoring.js +1 -1
- package/src/authorjob.js +33 -15
- package/src/authorreadout.js +7 -6
- package/src/declaredclose.js +6 -3
- package/src/draftspend.js +20 -2
- package/src/facts.js +54 -9
- package/src/factwords.js +58 -6
- package/src/inputlook.js +38 -3
- package/src/interviewrun.js +0 -1
- package/src/job.js +39 -12
- package/src/kinds.js +5 -0
- package/src/pagedropwords.js +64 -0
- package/src/pagemap.js +264 -0
- package/src/pagerules.js +319 -0
- package/src/panel/authorroutes.js +138 -60
- package/src/panel/authorsession.js +88 -32
- package/src/panel/index.html +1251 -522
- package/src/panel/jobplan.js +4 -2
- package/src/panel/runroutes.js +188 -6
- package/src/panel/scheduler.js +156 -0
- package/src/panel/schedules.js +667 -0
- package/src/panel/schedulestart.js +243 -0
- package/src/panel/server.js +1234 -68
- package/src/panel/status.js +33 -0
- package/src/panelmoney.js +29 -0
- package/src/planrun.js +92 -11
- package/src/promptregisters.js +4 -0
- package/src/providerrows.js +3 -3
- package/src/replay.js +118 -7
- package/src/reviewdoor.js +37 -0
- package/src/rubricstep.js +123 -11
- package/src/runlist.js +37 -1
- package/src/schedulewords.js +299 -0
- package/src/source.js +18 -0
- package/src/thinking.js +63 -2
- package/src/trigger.js +48 -0
- package/src/triggerclock.js +127 -0
- package/src/triggerfiles.js +215 -0
- package/src/triggerledger.js +79 -0
- package/src/userrun.js +34 -21
- package/types/authorflow.d.ts +42 -6
- package/types/authorjob.d.ts +10 -2
- package/types/declaredclose.d.ts +3 -1
- package/types/draftspend.d.ts +27 -1
- package/types/facts.d.ts +15 -0
- package/types/factwords.d.ts +26 -3
- package/types/inputlook.d.ts +15 -0
- package/types/job.d.ts +8 -1
- package/types/kinds.d.ts +5 -0
- package/types/pagedropwords.d.ts +26 -0
- package/types/pagemap.d.ts +154 -0
- package/types/pagerules.d.ts +86 -0
- package/types/panel/authorroutes.d.ts +50 -14
- package/types/panel/authorsession.d.ts +37 -12
- package/types/panel/runroutes.d.ts +32 -1
- package/types/panel/scheduler.d.ts +17 -0
- package/types/panel/schedules.d.ts +412 -0
- package/types/panel/schedulestart.d.ts +106 -0
- package/types/panel/server.d.ts +297 -15
- package/types/panel/status.d.ts +51 -0
- package/types/panelmoney.d.ts +5 -0
- package/types/planrun.d.ts +2 -0
- package/types/providerrows.d.ts +2 -2
- package/types/replay.d.ts +21 -1
- package/types/reviewdoor.d.ts +27 -0
- package/types/rubricstep.d.ts +57 -1
- package/types/runlist.d.ts +24 -1
- package/types/schedulewords.d.ts +137 -0
- package/types/source.d.ts +11 -0
- package/types/thinking.d.ts +39 -0
- package/types/trigger.d.ts +37 -0
- package/types/triggerclock.d.ts +52 -0
- package/types/triggerfiles.d.ts +93 -0
- package/types/triggerledger.d.ts +35 -0
- package/types/userrun.d.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,94 @@ 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.44.0] - 2026-10-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **bareguard ^0.25.1.** Gap words now JSON-escape the extra-keys keys and end with " and N more" when more items exist than are shown; evidence and `cited` results are unchanged, and bareloop's own behaviour is unchanged.
|
|
13
|
+
- **Result tab shows a money fact as the page wrote it.** The price box reads the value with its currency or unit exactly as the check located them on the page ("144 US dollars"), replacing the interim "$144"; with no evidence (an old run) the fact's own spelling stays.
|
|
14
|
+
- **Result tab marks the words each check read.** Each result's proof fold marks, in the page's own quote, every word the check located (the anchors, the values, the unit and the phrases), using the offsets bareguard returned and the run's `rubric-evidence` record, never a search in the panel. A membership token that is not the result's own is not marked. Escaped throughout; an old run or a quote the check did not read shows the plain quote as before.
|
|
15
|
+
- **`rubric-evidence` spine record (report-only).** A rubric step's `cited` checks now save what they read (bareguard 0.25 `full.evidence`: the quote and the spans of the words each check located) as `{step, iteration?, evidence}`, masked like every spine write; nothing in the verdict, governor or money paths reads it. The Result tab's highlights and money-as-written build on it.
|
|
16
|
+
- **bareguard `^0.25.0` (ruling #66 evidence spans; dependency bump only, `full.evidence` not used yet).** Checked on the saved real Google Flights page: the dump is byte-identical to the 0.24-era evidence and the page check reads 27 blocks, 0 bad.
|
|
17
|
+
|
|
18
|
+
- **Result tab links to the page each result read.** One web page read shows one link at the top of the Result tab; two or more pages show each result box's own page link, with "The page may have changed since this run." beside it. The address is the run's own `source.json` input url (http and https only, opens in a new tab with `rel="noopener noreferrer"`); file and repo inputs get no link. `/result` now carries `pages`, `pageNote` and each line's `page`.
|
|
19
|
+
- **The page-word map replaces the search-and-read look (idea A core, items 1-6; `src/pagemap.js`, `src/pagerules.js`, ITEM33-BUILD.md "Addendum 2026-10-18").** For a rubric job on a page, ONE AI call per page input names the page's own words for the job; plain code keeps only words found on the page (found-on-page), drops a label written 2+ times inside one page line (N2), keeps a second label only if it reads the same values as the first (R1), turns a marker the page writes after the number ("EUR 490 total") into a unit and drops a label that reads no number (U1), compares unit and currency forms by the numbers they read (R1L), and refuses a fact named for a position or two facts with the same shape and label (S1). The AI never grades. A malformed or truncated answer is retried once, then a plain stop; the scrubbed answer is kept in `draft-log.jsonl`.
|
|
20
|
+
- **Currency and unit lists on a money fact (idea A core, item 7; bareguard ruling #65).** A money fact may carry both `unit` and `currency`, each one value or a list (at most 3 currencies and 10 units). Old single-value specs keep their bytes and hash.
|
|
21
|
+
- **No silent drop (idea A core, item 8; F238).** Every word, fact or `~` rule the page rules drop appears as Not checked with a plain reason from one table (`src/pagedropwords.js`), on the confirm card and on the signed plan; a dropped word cannot be signed by the drafter (`rubric-fact-word-dropped`).
|
|
22
|
+
|
|
23
|
+
- **Part 5 of PRD v1.106: each try, its checks and the files judged (Audit), two Run summary rows, a line on each step card.** A plan step's Audit now lists every try with its checks in the order they ran (result, a count or the folded reasons, the job line each serves), the first reason and `(+N more)` on the try line, one Round table per step with a Try column and round numbers from 1 (20 rows a page; the flat view keeps run-wide numbers and says so), a `[model]` marker and a tools tally; a try the wall cut off before its checks ran is listed as such. A run that reached its review door gets a door part (`[human · ask]`). The Run summary gains `calls:` and `checks:` (counts of what ran, never a fraction), and each step card a `N tries · last try: ...` line. New spine record `output-judged {step, iteration, path, bytes, sha256}` (never content) written before each `exit-eval`. Fixes the false "not run" on every check of a multi-try step (D1).
|
|
24
|
+
- **Answered questions are recorded and shown (Part 5 Q1).** Every question the person answers (the confirm card's, the early look ask, the last-try ask) is a `human` row in the session's `draft-log.jsonl` (answer secret-masked), drawn under its step in the Audit's drafting table as `you were asked` / `you said`.
|
|
25
|
+
- **Code forces the early ask for a `~` rule the page map could not tie (Part 5 Q3, T2).** `askableFromLook` (src/pagemap.js) asks, before any paid drafting, one question per such rule (cap 3 since Part 5b); the answer becomes a `~` rule, or "drop it" puts the rule on the look's Not checked list.
|
|
26
|
+
- **The look reads, code asks (PRD v1.107 Part 5b).** The page map may carry a `reading` (a limit such as `between 06:00 and 21:00`) on a time or money fact; when its bound is not in the person's own words, code asks `I read it as: ... Agree?` before any paid drafting (`agree` writes the reading as a `~` rule in plain words, so the fact signs plain; a typed answer is the rule instead), and a rule the page cannot check asks `Drop it?`. The cap is 3 asks (`MAX_QUESTIONS`); more stops the draft with a code-written list of every ask and its reading, at no extra cost. Replaces the unbuilt Part 5 T1.
|
|
27
|
+
- **"fetching the page…" after Sign & run (Part 5 Q2).** The progress list shows the step while the run's own refetch of a web input is running and settles it when it lands.
|
|
28
|
+
|
|
29
|
+
- **The Result tab (PRD v1.107 Part 6, rubric jobs only).** A fourth right tab after Job, shown only when the run's check type is rubric and its signed close has a `rubric-step` stage. Per job line: the line and its `~` rules, then for a facts line one box per result in the delivered step file, with one small box per signed fact name (each value as the quote writes it, `5 of 5 ✓` when the run's own `check-run` for that line was satisfied and the run ended green; never a ✓ otherwise), a `proof - the page's own words` fold with the quote (the words each check read are marked, see the highlight line above), the rules in `factSentence` words, the Not checked list (the Job tab's own, `planFromSpec(spec).notChecked`), the door (`Accept` / `Rerun with my words` / `Pause`, only while waiting), `saved to:` and `Download <file>`. The Run tab says `the result is on the Result tab` while the door is open. New: `GET /api/runs/:runid/result`, `GET /api/runs/:runid/download?i=<n>` (read only paths in the run's own `destination-written` records and re-check their sha256; a changed or missing file says `the saved file changed after the run` / `the saved file is gone`), `POST /api/runs/:runid/rerun {text}` and `POST /api/runs/:runid/pause` (the engine's own `run-u --door --decide`, like Accept; empty rerun words refused in the page and the route, the engine refuses them too). The run detail gains `hasResult` and `delivered`.
|
|
30
|
+
- **Result tab fixes (PRD v1.107 Part 6).** A red run now shows the rows of the last file its check judged (the run's own `output-judged` record, resolved in the run's own tree and sha256-checked: `the file changed after the check` / `the file is gone`; no ✓ ever, the check's gap words sit under the table; an old run with no record still says no result was saved). `(you confirmed)` marks a fact whose limit came from a Part 5b `agree` answer (read from the drafting log's `human` rows; shown only when the written `~` rule and the signed fact match; nothing when the log is gone). Money cells use the fact's own signed currency or unit (`$144` / `144 US dollars`, `moneyAs` in `src/facts.js`, shared with the rule words). Not checked items tied to a job line by the signed plan show under that line; the rest stay at the bottom (in total the Job tab's items). The tab now has the signed look (variation B, `design/result-tab-mock-v2.html`): Result / Line N / Your call / Saved cards in the panel's own `.job-card` boxes (no table), one box per result with one small box per fact, the line numbered `1. text`, Not checked as lists, and a gone or changed delivered file said once per file in Saved (the line falls back to the rows of the file the check read, else `no saved result to show`).
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **A currency sign listed in both unit and currency is used once, as the currency.** When the AI listed `$` under both `unit` and `currency` (fwd F71 drafts), the unit copy was checked as a phrase and the person read `"$" is not written on the page.` (false). The unit copy is now dropped with "it is used once, as the currency". A sign listed only under unit is unchanged.
|
|
35
|
+
- **A word the AI listed as both unit and currency no longer reads as "does not show the same amounts as" itself.** On a Google Flights page the page-word map listed `US dollars` under both `unit` and `currency` of the price; R1L compared the currency form with the same word and Not checked showed a nonsense sentence. The word is now never compared with itself: the unit (listed first) stays and the line says it was listed twice and is used once. The kept check ("144 US dollars") is unchanged.
|
|
36
|
+
- **Reuse of a rubric job on a URL or file is no longer refused at $0 (run mvez8s54).** Reuse workflow -> Sign & run (and a scheduled fire) proved Inputs line 1 as a repo and refused a URL with "the repo must be an absolute path". `startSession` now decides rubric-on-files from the reused signed spec's verdict type, in one place for the click and the schedule; a repo job still proves line 1.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **A fresh run lists itself before it fetches the page (run mvez8s54).** The run-start refetch of a web input (about 18 s on a flights page) used to finish before the run appeared in the Runs list, so Sign & run looked dead and the run then showed up already 10 s old. The run now lists its row first (pid and cap, the monthly claim), then fetches; the Run tab says "fetching the page… the run has not started yet" meanwhile (words from the server). A refused page settles the row at $0 (`failed`, plain words, monthly room freed) instead of leaving no trace. A resume, door or decision does not refetch, as before.
|
|
41
|
+
- **Panel money keeps its decimals under a cent (PRD Part 6 rulings 2026-10-19, (3)).** An amount under $0.01 now reads with 4 decimals ("$0.0054 run + $0.0035 drafting = $0.0089 of $1.00") instead of "<$0.01"; $0.01 and up stay 2 decimals, zero stays "$0.00", "at least" floors keep "at least". One owner in Node, `src/panelmoney.js` (`panelMoney2`, re-exported by `src/panel/authorroutes.js`), replaces the second Node spelling in `src/authorreadout.js` and a raw `toFixed(2)` in the resume-cap refusal; the page keeps one browser copy (`panelMoney`) pinned to the Node one by a parity test. The CLI's 4-decimal money and the caps' display are unchanged.
|
|
42
|
+
|
|
43
|
+
- **Money reads `run + drafting = total` (Part 5 Q4).** `$0.0054 run + $0.0035 drafting = $0.0089 of $1.00` in the run.log and the panel (from a cent up 2 decimals, under a cent 4 decimals, as the line above); a floor keeps `at least` on all three parts.
|
|
44
|
+
- **Thinking on retries, for every job (PRD v1.106 Part 1; F230-F233).** The worker's per-call thinking mode is now decided in code from the phase and attempt (`workerThinkingMode`, `src/thinking.js`): planner (draft, redraft, replan) on, run scout off, step attempt 1 off, step attempt 2 and later on, close-fix on. The job's "Thinking on" tick makes every worker call on (as it already did for drafting and the rubric judge). It is sent as bare-agent's per-call `thinkingMode` through `thinkingCallOptions` (nothing is sent to a model that cannot switch). New spine records: `thinking-decision {phase, iteration, mode}` before every worker call and `thinking-status {phase, iteration, mode, thinkingStatus}` after it. A step or fix iteration that is not a whole number throws. Measured on DeepSeek deepseek-flash only; on the short changelog job it was slower (1.76 vs 1.46 min), by hamr's one-rule ruling. Budgets, caps and the close are untouched.
|
|
45
|
+
- **The Runs list searches Scheduled, plus a `◷` chip (PRD v1.106 Part 2; fwdloop M11 am17-am20).** Scheduled now sits BELOW the search box (only never-filtered Running now and Needs you stay above it). The one search box narrows Scheduled cards by job name, along with Jobs; a `run-N` or date word narrows Jobs only, and the result and time chips never touch Scheduled. The new `◷` chip (`scheduled jobs only`) shows only Scheduled: Jobs and the quiet fold hide, the count line reads `showing N scheduled job(s)`, and with no match the list says `no scheduled job matches`. The faint line reads `search also finds Scheduled; ◷ shows only Scheduled`. Page only; `GET /api/runs` is unchanged.
|
|
46
|
+
- **Thinking shown in the Audit tab and the Job tab (PRD v1.106 Part 3).** Each Audit attempt line says the thinking its call was sent (`attempt 2 [✗] · thinking on`), and a part with one call (plan, scout, replan, a one-attempt step) says it on its header line, read from the run's `thinking-decision` / `thinking-status` records in drafting's own words (`thinkingRowText`). The Job tab gets a `Thinking` row (`on retries`, or `always on`), always shown, on one row with Model like the caps; a run from before those records reads `—` there (never a guess from the job's tick). A run from before those records shows no thinking word in the Audit tab. `GET /api/runs/:runid` parts and attempts carry `thinking`; `GET /api/runs/:runid/job` carries `thinking`.
|
|
47
|
+
- **The Job tab's AI's summary shows only the sentence the person confirmed (PRD v1.106).** A rubric job's signed goal is the whole worker brief; the Job tab used to show all of it. It now shows the one confirmed sentence (`confirmedGoalOf`, `src/rubricstep.js`), or no summary row when the brief carries none; a code job is unchanged.
|
|
48
|
+
- **The Audit schedule header matches the other part headers (PRD v1.106 Part 4).** `▾ schedule [✗] when a new file arrives ...` is now `▾ [✗] failed schedule · when a new file arrives ...`: sign, word, name, then the `·` parts, with the sign and word both from the status table (`scheduleSection` gains `word`).
|
|
49
|
+
|
|
50
|
+
## [0.43.0] - 2026-10-16
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- **A job can be scheduled from the panel (T, Part 3 of 8; panel-only, nothing fires yet).** A signed spec may carry a `trigger` (`daily`, `weekdays`, `weekly`, `new-file` folder, `file-updated` file), set only by a two-click, $0 door (`POST /api/author/schedule-prepare`, then `POST /api/author/schedule` with the hash it showed; `when: null` cancels and is never refused). It needs a real green run of the job's current workflow (a rubric job also needs an honoured Accept). Files live under `~/.config/bareloop/triggers/<job>/` (`schedule.json`, one `ledger.jsonl` whose rows all carry the signature). The agent never writes a trigger: a draft that carries one is refused by name. `workflowKey` ignores `trigger`; `cadence` is no longer required (an old spec keeps it, still valid, same hash) and new specs no longer write it.
|
|
55
|
+
- **One starter for a scheduled run (T, Part 4 of 8; still no timer).** A scheduled fire goes through the same session start and the same `signRun` as a Reuse the person clicked (headless sessions kept apart from the person's chat session; `--approve` is the hash derived by code; a scheduled spawn passes `--review-door`). Fire order: busy, monthly, `wouldAccept`, started; each outcome is one row, told once per slot-or-file, outcome and reason. A busy, monthly or refused fire consumes nothing, so it retries when things are back up; the `fire`/`ran` row is written only after every check said yes, and a launch error after it writes a `released` row (at most 3 times).
|
|
56
|
+
- **The panel's scheduler (T, Part 5 of 8).** While the panel is up a 500 ms timer (unref'd, stopped when the server closes) arms each signed schedule, fires a due clock slot or a queued file through the one starter, and writes the `arrived` rows; poll only, never `fs.watch`. A run of the job is busy only while its pid is alive AND the process started no later than 5 s after its run row was written (a recycled pid is not that run). One job's failure is written once and never stops the others. Missed slots still collapse to one catch-up.
|
|
57
|
+
- **The words of a schedule (T, Part 6 of 8).** `src/schedulewords.js` is the one owner of every schedule sentence: the next time (`today 15:00`, `tomorrow 09:00`), `next in 20m`, the NEXT line (a run waiting for your Accept does not hold the next one; the month used up reads as a red `Paused: ...` with two-decimal money and a typed red flag), the catch-up note and the edit-clears sentence. The page paints strings and works out nothing.
|
|
58
|
+
- **Schedule run, from the panel (T, Part 7 of 8).** The Run tab's last button is `Schedule run` (a job with a good run) or `Cancel / modify schedule` (a scheduled job, on every run of it, also while one is live); a job with no good run has neither. It starts and signs nothing: it opens the Chat card in schedule mode with a `When` field (By hand / Every day / Weekdays / Every <day> / When <file> is updated / When a new file arrives in <folder>). One main button: `Sign & run` while When is unchanged, `Sign schedule` / `Cancel schedule` when changed, `Confirm: <words>` after click 1 ($0, signs nothing), and after click 2 the card clears to the empty New job card with the button dead for 1.5 s (no Run invitation after a sign). Runs list gets a `Scheduled` section after Needs you (soonest first, a click opens the job's newest run). ENDED / NEXT moved from the Audit tab to the Run tab, with a SCHEDULED / NEXT twin directly below it (NEXT red when the month is used up) and a note on why a run started and when it was a catch-up. Editing a job never clears its schedule (see Changed: one-door Reuse). Every schedule word is one table (`SCHEDULE_WORDS`) the server sends.
|
|
59
|
+
- **A run waiting at its review door shows `Accept` in Needs you** (never ages out).
|
|
60
|
+
- **Accept, from the panel (T, Part 8 of 8).** A run waiting at its review door gets an `Accept` button on the Run tab (right after Resume, before Edit in chat), shown only when the server says a door is open. It runs the engine's own door path (`run-u --spec --door --decide accept --approve`): the mechanical stages re-run, a red is `door-accept-red`, the verdict is untouched; the engine's own words are shown on a refusal. A scheduled run's green is held (not offered by Reuse, `green-held`) until it is accepted.
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
|
|
64
|
+
- **The Runs list search and run cards follow fwdloop M11 am5 and am6 (PANEL-BUILD.md P9 v2 section O; replaces the "one newest older match past the 7" rule).** The search splits at spaces and every word must match; `run-2` matches only run-2 (never run-20); `MM/DD` or `YYYY-MM-DD` matches the runs that started that local day (a part date such as `10/1` matches nothing); while a search or chip is on, a job's older-runs fold shows only matching runs, newest first, at most 7, plus the selected run. The line after the 7 reads `(and k older · <oldest> to <newest> of those k · search above)` or, while searching, `(and k more matches · add a day or a run)`; the search box reads `job, run-23, or a day 10/12`. Line 1 of every run card, and of the Running now and Scheduled cards, now sets the date, model, time, cost and next-run text in the same size and baseline as the job and run name (`run-2 · 10/12 13:46 · deepseek-flash`), not in the small raised faint span.
|
|
65
|
+
- **The Runs search takes any day form, ignores brackets and says it plainer (fwdloop M11 am7; PANEL-BUILD.md section O am7).** `10/13`, `10-13` and `2026-10-13` mean the same local day; `aging-live-4 (run-2)` finds what `aging-live-4 run-2` finds; the line after the 7 gives the hidden runs' real oldest-to-newest days (`4 more older runs, 10/11 to 10/13. Search to find one.`, or `6 more runs on 10/11. ...`, or `3 more match. Add a date or run number to narrow it.` while searching); the search box reads `search: job name, run-23, or a date 10/13`. Replaces am5 item 5's words.
|
|
66
|
+
- **A scheduled fire refused with `install-needed` backs off 1, 2, 3, 5, 8, 13, 21 minutes, then stops and waits for you (PANEL-BUILD.md T addendum item 11, RULED 2026-10-15).** It used to redo the copy and `npm ci` on every 500 ms tick; the first fix was a flat 1 minute. Now each consecutive refusal of a job waits the next Fibonacci step (the count is in memory and starts over after a start, a cancel or a re-sign); the next step that would pass 60 minutes in total is not taken: ONE `gave-up` row is written and the schedule pauses (read from the ledger, so a restart does not resume it). No clock slot fires and no queued file runs while paused; new files still queue. The job shows in Runs list Needs you with the pill `Check` (reword is one edit in `SCHEDULE_WORDS`), and the run page's NEXT is red: `Stopped trying after 60 minutes: <words>. Fix it, then Cancel / modify schedule to start again.` Re-signing through Cancel / modify schedule, or cancelling, clears it. During a wait NEXT reads `Trying again in N minutes.`
|
|
67
|
+
- **The Runs search box reads `job, run-23, or date 10/13` (fwdloop M11 am8; PANEL-BUILD.md section O am8).** am7's longer words clipped at 320 px; the new ones fit, and a test pins the words and a 32-character limit.
|
|
68
|
+
- **The Runs search line after the 7 shows the hidden matches' real days (fwdloop M11 am9; PANEL-BUILD.md section O am9).** While searching: `5 more matches, 10/11 to 10/12. Add a date or run number to narrow it.`, or `50 more matches on 10/09. Add a run number to narrow it.`; a hidden match with no date drops the date part. Replaces am7 item 4's `k more match.` words.
|
|
69
|
+
- **Runs-list card money names the drafting share.** The short form reads `$0.22 (drafting <$0.01)` (was `$0.22 (<$0.01)`, the bracket's meaning unclear); a floor reads `(drafting at least $X)`. The long form `($X drafting)` is unchanged.
|
|
70
|
+
- **The panel's Runs list is rebuilt in fwdloop's shape (P9 v2).** One list, top to bottom: Running now (live runs, the wall counting up in place), Needs you (a stopped run the engine would resume, with a `Resume` label, for 7 days), the filters, Jobs (run in the last 30 days, newest activity first) and a folded `quiet jobs`. A job card shows `job · k runs · check type` and its newest run; its runs sit under it, with the older ones behind `older runs (k)` (7 shown, then "use search"). The Workflows / History switch and the flat History list are gone.
|
|
71
|
+
- **Search reads the whole visible row** (job, run-N, run id, model, status word, reason, date, cost, wall, check type); a matching run is marked with a blue bar, never selected, and the fold opens on it. The search box placeholder is shorter.
|
|
72
|
+
- **`GET /api/runs` now returns `{runs, jobs, limits}`.** The server decides live, needs-you, each job's newest run, the quiet split and the limits; the page works out nothing.
|
|
73
|
+
|
|
74
|
+
- **Reuse workflow is ONE door on every run (RULED 2026-10-15).** Every box opens; the card's job boxes pick the road. A green or already-green run with no job box changed signs its plan verbatim under its own name with no drafting; a changed job drafts a NEW workflow under `<base>-N` (a name already used by another job is refused in plain words). Edit in chat and the locked-box refusal are gone, and an already-green run now counts as green for Reuse and the picker.
|
|
75
|
+
- **Remove from Needs you (`POST /api/runs/:runid/needs-remove`).** A stopped run's card leaves Needs you with one `needs-removed` row in `runs.jsonl`; its status, list row, Resume and money stay, and a later stop brings it back.
|
|
76
|
+
- **The SCHEDULE line says why, and only real problems are red.** Line 2 says a run is waiting for your Accept, and `busy` reads "still working"; before the first fire it says no run yet, and an unarmed schedule ends `next: not armed yet`. Only a refused try, the give-up and a used-up month are red, and the month-used-up red shows only in the month it happened; busy and the install back-off are not red.
|
|
77
|
+
- **A run's name reads `job · run-N` in Running now, Needs you and Scheduled,** and the Scheduled and gave-up cards carry the run's number from the server.
|
|
78
|
+
- **A stopped run is grey, not red.** The `[■]` sign, the step-map chip and the Run tab frame all use the same grey as `[·]` (you stopped it, it did not fail); a schedule problem on the run still wins red.
|
|
79
|
+
- **A sign blinks only while the run is running or waiting for your Accept;** every other sign rests (reduced motion keeps the sign).
|
|
80
|
+
- **The Audit tab of a scheduled job has its own folded Schedule section at the top:** this run's start, and the tries that started nothing (all signings, newest 7), in the same rounds-table shape as the drafting and step sections.
|
|
81
|
+
- **The Run tab has one summary block (ENDED dated, NEXT, SCHEDULE with last and next fire; red on a problem), and the Job tab has Started and Schedule rows.** Editing a job never clears a schedule. Runs-list rows say `step N of M` while running.
|
|
82
|
+
- **Resume and Accept answer in plain words, never raw engine output (RULED 2026-10-15/16).** A known engine sentence or a fixed plain sentence with the log path replaces raw output (`engineRefusalWords`); Resume and Accept on a run whose saved job fails today's checks say so in plain words and refuse at $0 before spawning anything. An Accept refused by its final checks names each failed check by its plain question, never the stage id.
|
|
83
|
+
|
|
84
|
+
### Fixed
|
|
85
|
+
|
|
86
|
+
- **Schedule run and Cancel / modify schedule never start a run (live defect, run mv9euoam).** When only Destination, Inputs or a cap changed (or nothing changed), the card's main button stayed the Reuse card's `Sign & run` and started a hand run without touching the schedule. The schedule card now has no run path: a change in When, Inputs, Destination or a cap makes the button `Sign schedule` (then `Confirm`, the $0 two-click door); unchanged is a disabled `Sign schedule` with the "nothing changed" words.
|
|
87
|
+
- **A refused scheduled fire now shows on the open run page.** The page stopped refetching a run once it was no longer live, so the SCHEDULED / NEXT block of a finished run stayed as first painted and the red NEXT ("The last try did not start: ...") the server already sent never appeared. A finished run of a scheduled job is now refetched on the runs-list cadence (10 s).
|
|
88
|
+
- **A scheduled start that is refused leaves no session folder behind, and a refusing child cannot loop.** `wouldAccept` now runs the session's own Destination check and the good-run check before any session exists; any session the starter made for a start that is then refused (the copy refused a symlink, packages missing, the saved copy no longer hashing to what was signed, a failed spawn) is removed by the starter, and a child that exits non-zero within 1.5 s with no row in the run list counts as a release toward `MAX_RELEASES`, so the retries and the rows stay bounded. The live failure wrote 237 folders at 500 ms.
|
|
89
|
+
- **A plain-files job can be scheduled and reused with a real Destination (rubric-2 schedule refused at its first fire).** The Destination of a plain-files rubric job is an absolute folder that the signed spec never holds (its fence is `output/` inside the run's tree), but the Reuse and Schedule cards opened with that fence and `buildReuseSpec` wrote whatever was typed into it, so a schedule signed fine and its fire was refused ("Destination must be the full path of a folder"). The card now opens with the folder the origin run delivered to, an absolute Destination stays out of the spec (it rides on the schedule and in the hash both clicks bind), and ONE check (`startDestinationProblem`) runs at both schedule clicks, at the HTTP start (before any session folder exists) and in a scheduled fire's `wouldAccept`.
|
|
90
|
+
- **A rubric job signed without an explicit `judge` can be scheduled and reused.** `workflowKey` now hashes the RESOLVED judge of a soft-green spec (the runner's `resolveJobJudge`), so a missing judge and its pinned default (what Schedule run and Reuse sign) are one workflow; an explicitly different judge is another, and deterministic keys are unchanged. Before, Schedule run (and Reuse) of such a job was refused with "not the one that had the good run" after an honoured Accept (mv76m82c). Soft-green keys differ from before, so a schedule saved by an earlier build for a judge-less rubric job needs re-signing.
|
|
91
|
+
- **A replan answer cut off at its length limit is no longer reported as "The model provider failed" (RULED 2026-10-16, F229, run mv9gm796).** It is a bounded failed attempt: the cut round is booked, the half-answer is never validated and is handed to the one redraft as a plain-words red (a cut redraft, even after a bad first answer, ends the run), and the run ends with the plain sentence "The replan answer was cut off at its length limit, so no new plan came back." It is no longer in the resumable provider-red set. Scout and the first draft are unchanged. Test-proven only.
|
|
92
|
+
- **Reuse workflow on a saved job that makes the validator throw answers the plain "made by an older bareloop" words** instead of a raw `internal error` 500.
|
|
93
|
+
- **A scheduled start's `lateMs` and `startedAt` use the same clock the scheduler uses** (they read the wall clock beside an injected scheduler clock).
|
|
94
|
+
- **The `signed` schedule-ledger row no longer carries the unread `originRunid` field** (the Audit reads `schedule.json` and `trigger.json`).
|
|
95
|
+
|
|
8
96
|
## [0.42.0] - 2026-10-14
|
|
9
97
|
|
|
10
98
|
### Added
|
package/bareloop.context.md
CHANGED
|
@@ -64,7 +64,7 @@ inherited rule carries the green that minted it and the contrast that attributed
|
|
|
64
64
|
back to a default. The request-key spelling is the ROW's tick, never a model-id table: a server that ignores `max_completion_tokens` (F149) does not bind
|
|
65
65
|
the output cap until the "old max_tokens" box is ticked, and an output cap that does not bind is a money hazard, so tick it on that row before a paid run
|
|
66
66
|
(`makeProvider(shape, { legacyMaxTokens })`, forwarded for `openai-api` only). The judge's calls carry the per-call `thinkingMode` the job's signed "Thinking on" tick asks for
|
|
67
|
-
(`src/thinking.js`, applied by `defaultJudgeLoop`; F192: unticked = off, because a model's default thinking can eat the 4000-token locate budget);
|
|
67
|
+
(`src/thinking.js`, applied by `defaultJudgeLoop`; F192: unticked = off, because a model's default thinking can eat the 4000-token locate budget); the WORKER's per-call mode is decided in code from the phase and attempt (see "Thinking on" below). A plan step's
|
|
68
68
|
`model: "sonnet"` is a documented schema token meaning "this job's worker model" (`STEP_MODELS`), not a model id. `gemini-api` (PRD item 31.3) is
|
|
69
69
|
**ADMITTED-PENDING-PROBE**: it has ZERO runs, and the probe rule is not waived for it —
|
|
70
70
|
`PROBE_STATUS` (`src/providers.js`) is the machine-readable form of that debt, and
|
|
@@ -153,12 +153,12 @@ minting claim, or the shell-owned retry cap — all unknown-field reds.
|
|
|
153
153
|
| `description` | non-empty string | |
|
|
154
154
|
| `provider` | `anthropic-api` \| `openai-api` \| `gemini-api` \| `clipipe-subscription` | menu (`PROVIDERS`): an API SHAPE (a wire protocol), part of the lineage key by definition. Only `anthropic-api` is guaranteed (F48); the shapes go through the provider factory (`src/providers.js`) — see "Worker surface" above for the `baseUrl` field and the row's old-max_tokens tick; `gemini-api` (PRD item 31.3) is ADMITTED-PENDING-PROBE — zero runs, and every launch says so (`PROBE_STATUS`/`probeWarningLines`); `clipipe-subscription` drives the worker natively and needs `opts.nativeProvider` |
|
|
155
155
|
| `conditions` | `{ providerPath?, closeVerbosity?, taskFraming?, scaffold? }` | declared keys only, string values — the environment label (consumed by the N3 lineage key; recorded on spines from run one) |
|
|
156
|
-
| `cadence` | `{ unit: hour\|day\|week, every: 1..30 }` | validated
|
|
156
|
+
| `cadence` | `{ unit: hour\|day\|week, every: 1..30 }` | still validated if present but IGNORED: it starts nothing (an old signed spec keeps it, never rewritten). Scheduling is the `trigger` field, panel-only (see "Triggers" below) |
|
|
157
157
|
| `budgetUsd` | finite `0 < n <= shell cap` | ceiling chain: workflow ≤ job ≤ shell — each layer may tighten, never exceed |
|
|
158
158
|
| `maxWallMs` | optional integer ms `>= MIN_WALL_MS` (one close timeout) | the run's wall clock. **NO DEFAULT, by ruling** — absent means time-unbounded *by explicit operator choice*, never by fallback (F45: a defaulted cap is a silent second ceiling). Enforcement is a BETWEEN-ROUND deadline, so the honest worst case is `maxWallMs + closeStages × closeTimeoutMs` — every stage of a staged close gets the FULL timeout — and all three numbers are reported (`loop.stop()` cannot cut an in-flight call — F61 measured 500ms→4,018ms). The `MIN_WALL_MS` floor is a ONE-stage, un-autoset-default number, so a spec with many stages OR a raised/autoset `closeTimeoutMs` can validate while its overshoot dwarfs its cap: `runPlan` adds a RUN-START check (PRD item 27/M3) that refuses `wall-under-close-timeout` at $0 once the EFFECTIVE per-stage ceiling is known (autoset or signed), rather than a silent clamp. Operator-only, tighten-only; adding or changing it changes the spec hash |
|
|
159
159
|
| `closeTimeoutMs` | optional integer ms `>= CLOSE_TIMEOUT_FLOOR_MS` (120,000 — hamr's arbiter floor, 2026-09-07) | PRD item 27/M3's SIGNED override for the close's own per-stage wall-clock cap. Absent (the normal case): the run autosets it from a $0 seed timing pass — `max(FLOOR, K × slowest measured stage)`, `K = 5` (hamr, 2026-09-07: "2 and 5 are fine"). Present: the operator's own number wins outright and may legally sit ABOVE *or below* the autoset estimate (the rates-passthrough shape, F113 — never tighten-only). Arbiter territory exactly like `budgetUsd`/`maxWallMs`/`close[].sha256`: the authoring pipeline's `assembleSpec` refuses a draft carrying it |
|
|
160
160
|
| `model` | optional non-empty string, the exact model id of a line ticked in Settings > Providers | the WORKER's model. A job carries its own model (P8: bareloop has no default); a spec signed before that (no `model`, perhaps no `baseUrl`) runs on the line `--model <id>` names (plus `--provider <KEY>` when two keys tick it) — the signed hash is untouched. Present, it wins over a `--model` flag outright, and a flag naming a different id is refused (`resolveWorkerModel`, `src/job.js`) rather than silently overridden. Part of the signed hash like every other field. Worker only — the JUDGE's model comes from the `judge` row below, which DEFAULTS to this same worker model |
|
|
161
|
-
| `thinking` | optional boolean | the job's "Thinking on" tick (P8): absent = off, and an old spec keeps the hash it always had; present it is signed like `model`. Covers DRAFTING and the rubric JUDGE
|
|
161
|
+
| `thinking` | optional boolean | the job's "Thinking on" tick (P8): absent = off, and an old spec keeps the hash it always had; present it is signed like `model`. Covers DRAFTING and the rubric JUDGE, and makes every WORKER call think (see "Thinking on" below) |
|
|
162
162
|
| `judge` | optional `{provider, model}`, both non-empty; `provider` from the `PROVIDERS` menu | the signed JUDGE override (PRD item 32). Absent, the judge resolves to this job's own `provider`/`model` (`resolveJudge`) — which is what lets a non-Anthropic job judge without a second account. Present, it pins a judge deliberately different from the worker (an independent reader). Both halves are required together: a judge with a provider and no model is an identity nobody signed. Part of the signed hash, so changing it kills the signature and forces recalibration. Unknown nested keys red — no smuggling level exists in a signed spec. The drafting agent cannot express it: naming your own examiner is arbiter territory |
|
|
163
163
|
| `writeScope` | array of contained globs | the operator's outer fence; the plan's own scopes must fit inside it, same containment code |
|
|
164
164
|
| `steps` | RETIRED | operator-authored `steps[]` was deleted (PRD v1.32); a spec carrying it reds `shape-retired:steps` by name rather than half-running |
|
|
@@ -636,7 +636,7 @@ whose EVERY structure attempt was cut off at the output cap also counts as faile
|
|
|
636
636
|
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
|
|
637
637
|
on to try 3 after an all-cut-off try 2 only when that gate can open, i.e. try 1 failed too);
|
|
638
638
|
the machine keeps at most
|
|
639
|
-
`MAX_QUESTIONS` (
|
|
639
|
+
`MAX_QUESTIONS` (3) — each on a job line the previous try's reds named — and logs the rest in `iterations[].droppedQuestions`;
|
|
640
640
|
the drafter is never told a number. It then stops `questions-open` (`questions: [{line, question}]`, nothing validated).
|
|
641
641
|
`authorCloseForJob({ answerQuestions })` is the seam that asks the person (required answer, no skip), takes back the job lines
|
|
642
642
|
with each answer added word for word as a `~` rule, and runs the draft once more over the same survey, listing and confirmed
|
|
@@ -646,7 +646,7 @@ are asked after two failed drafts, about a job line.
|
|
|
646
646
|
|
|
647
647
|
**Thinking on (per job; hamr 2026-10-11).** The New job card has a "Thinking on" tick under Model, unticked by default, saved in the SIGNED job as the optional
|
|
648
648
|
boolean `thinking` (absent = off; an old spec and any unticked job keep the hash they would have had; a ticked job's `jobSpecHash` and `workflowKey` differ). It
|
|
649
|
-
covers DRAFTING (scout, confirm, author, revise, structure retries) AND the rubric JUDGE
|
|
649
|
+
covers DRAFTING (scout, confirm, author, revise, structure retries) AND the rubric JUDGE, and as the think-everywhere override it makes every WORKER call think too (below). A Reuse card does not
|
|
650
650
|
offer the tick: it never drafts and the judge follows the SIGNED value, so the 10/10 calibration gate runs with the same tick as the later grading
|
|
651
651
|
(`defaultJudgeLoop({ ..., thinking })` takes it in all three constructors: the run, `run-author`, the panel's gate). bareloop keeps NO per-model thinking list and no
|
|
652
652
|
vendor regex: bare-agent (>= 0.52) owns the request shapes, and `thinkingSupport(kind, model, { baseUrl })` (a pure lookup, no network) answers `true` (measured
|
|
@@ -668,6 +668,23 @@ with a code-owned `thinking` row (`thinkingRowText`, before "checking setup"): `
|
|
|
668
668
|
`thinking on (this model can't change it)`; and, once a call reports a status, `thinking on (the model thought anyway)` (`'ignored'`), `thinking on (this model can't change it)`
|
|
669
669
|
(`'unsupported'`) or `thinking off (not confirmed)` (`'unknown'`) — a `step-label` record in the draft log carries the change. A Reuse workflow session drafts nothing and has no row.
|
|
670
670
|
|
|
671
|
+
**Thinking on retries (the worker; PRD v1.106 Part 1, hamr 2026-10-17, every job).** Without the tick, each worker model call's mode is decided in code, never by the model,
|
|
672
|
+
by `workerThinkingMode({ phase, iteration, tick })` in `src/thinking.js` (the one owner) at the `mkWorker` ask/askFrom call sites in `src/planrun.js`:
|
|
673
|
+
planner (draft, redraft, replan; phase `plan`) = on; run scout = off; step attempt 1 = off; step attempt 2 and later = on; close-fix (`fix`) = on. The job's tick makes
|
|
674
|
+
every one of those on. The mode goes out as bare-agent's per-call `thinkingMode` through `thinkingCallOptions` (the same support-aware mechanism drafting uses: a
|
|
675
|
+
model bare-agent measured as not switchable, a test double or a clipipe/native session is sent nothing). A step or fix iteration that is not a whole number >= 1
|
|
676
|
+
throws (operator error, never a guess). Each call writes the spine record `thinking-decision {phase, iteration, mode}` BEFORE it is made (one per call, a transport
|
|
677
|
+
or rate-limit retry inside it carries the same options), and, once the call returns, `thinking-status {phase, iteration, mode, thinkingStatus}` with bare-agent's own
|
|
678
|
+
verdict (`applied` / `unsupported` / `ignored` / `unknown`; absent when nothing was sent). Measured only on DeepSeek deepseek-flash (n=4 per arm); other shapes report
|
|
679
|
+
through `thinkingStatus`, and are not claimed. The close, budgets and caps are untouched.
|
|
680
|
+
|
|
681
|
+
**Thinking shown in the panel (PRD v1.106 Part 3).** The Audit tab says the thinking each worker call was SENT, read by `replayRun` from the run's own `thinking-decision` / `thinking-status`
|
|
682
|
+
records (`thinkingWordsFrom`, `src/thinking.js`), never guessed: each attempt line reads `attempt 2 [glyph] · thinking on` (the decision whose seq lies in that attempt's window), and a part with one
|
|
683
|
+
call (plan, scout, replan, a one-attempt step) carries it on its header line (`part.thinking`). The words are drafting's own (`thinkingRowText`): `thinking on` / `thinking off`, and
|
|
684
|
+
`thinking on (this model can't change it)` / `thinking on (the model thought anyway)` / `thinking off (not confirmed)` when bare-agent was not sure. An attempt whose own call recorded nothing (the fix loop's
|
|
685
|
+
opening precheck) and every part of a run from before the records show no thinking word. `GET /api/runs/:runid/job` adds `thinking` (`thinkingJobText(tick, recorded)`: `on retries`, or `always on` when
|
|
686
|
+
the signed spec carries the tick (an unreadable tick counts as not ticked), but `—` for a run whose spine holds no `thinking-decision`, never a guess from the tick) for EVERY run; the Job tab draws it as the `Thinking` row, beside Model on one row like the caps. The page types none of these words. The Job tab's "AI's summary" (`GET /api/runs/:id/job` plan `goal`) is only the sentence the person confirmed: for a rubric job (`RUBRIC_GOAL_HEAD`) `confirmedGoalOf` reads it out of the worker brief, none when the brief has no such line; a code job's goal is unchanged.
|
|
687
|
+
|
|
671
688
|
|
|
672
689
|
**D5 — the mandatory guards are SHOWN and FIXED, and the battery keys off the VERDICT
|
|
673
690
|
CLASS** (PRD v1.57 §2 — what a battery is FOR is the class of dishonesty that class of
|
|
@@ -721,7 +738,7 @@ attempt PLUS its close. Unpriced is never free: a null cost is a `pricing-red` s
|
|
|
721
738
|
never retried, and the ladder is otherwise ONE retry (`JUDGE_ATTEMPTS`) — after that a broken
|
|
722
739
|
judge is an instrument stop, never a red about the tree.
|
|
723
740
|
|
|
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})` (`wholeLines` arrived in bareguard 0.21; bareloop now needs `^0.
|
|
741
|
+
**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})` (`wholeLines` arrived in bareguard 0.21; bareloop now needs `^0.25.1`): 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
742
|
|
|
726
743
|
**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
744
|
|
|
@@ -1437,9 +1454,13 @@ ambiguity, not a merge. `resolveHumanRuling(fresh, held)` is the exported seam t
|
|
|
1437
1454
|
`branch-red` is the WORK BRANCH refusing (below): the patient is not a git checkout, its
|
|
1438
1455
|
branch namespace has no free name, or a resume's recorded branch is gone. Zero tokens, and
|
|
1439
1456
|
never a fallback to working on the branch the run was handed. `provider-red` is a
|
|
1440
|
-
transport throw, or a scout
|
|
1457
|
+
transport throw, or a scout or first-draft round the API cut off mid-generation
|
|
1441
1458
|
(`truncated:max_tokens`, BA-6 — before which it laundered into a clean finish, F25): no
|
|
1442
|
-
verdict exists and the failed round's spend is only partly known (F6). A
|
|
1459
|
+
verdict exists and the failed round's spend is only partly known (F6). A REPLAN answer cut
|
|
1460
|
+
off at `max_tokens` is NOT provider-red (hamr 2026-10-16, F229): the half answer is never
|
|
1461
|
+
validated, the redraft is told in plain words to answer shorter, and a second cut ends as
|
|
1462
|
+
`plan-red` with an escalation `cause: 'truncated'` and the code-owned sentence "The replan
|
|
1463
|
+
answer was cut off at its length limit, so no new plan came back." A STEP or FIX-loop
|
|
1443
1464
|
worker round cut at `max_tokens` is NOT provider-red (F122 "2A" half b, hamr 2026-10-05, fix
|
|
1444
1465
|
phase added by ruling A): it is a failed attempt — the round is booked, its output discarded,
|
|
1445
1466
|
an `attempt-bounded` record carries the reason `output cut at the token limit (max_tokens)`,
|
|
@@ -3411,11 +3432,12 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3411
3432
|
- **`bareloop panel [--port N]`** (PANEL-BUILD.md P1) → a READ-ONLY `node:http` server
|
|
3412
3433
|
(no new dependency), bound to `127.0.0.1` ONLY, default port `4700`. Serves the panel's
|
|
3413
3434
|
look (Tokyo Night box-drawing TUI style) over the run list's real data: `GET /` the page;
|
|
3414
|
-
`GET /api/runs` (newest first, each row enriched with a glyph/check-type/spend/wall
|
|
3415
|
-
summary, `fileMissing:true` when the listed spine is gone
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
the
|
|
3435
|
+
`GET /api/runs` (P9 v2: `{runs, jobs, scheduled, gaveUp, limits}`; `runs` newest first, each row enriched with a glyph/check-type/spend/wall
|
|
3436
|
+
summary, `fileMissing:true` when the listed spine is gone, and the states the left list draws, all decided by the server:
|
|
3437
|
+
`live`, `liveWhy` (`step N of M`), `liveSince`/`priorWallMs`, `needs` (`'Resume'` while the engine would accept a resume and
|
|
3438
|
+
the run stopped under 7 days ago and was not removed; `'Accept'` while the run waits at its review door, `doorWaiting`), `stoppedAt`; `jobs` = `[{job, headRunid, quiet, activityMs}]`
|
|
3439
|
+
newest activity first; `scheduled` = the Scheduled section's cards (v1.106 Part 2: drawn BELOW the search box, narrowed by the one search box on JOB NAME only: a `run-N` or a date word narrows Jobs only, the result and time chips never touch it; the `◷` chip, `scheduled jobs only`, shows only Scheduled with the count line `showing N scheduled job(s)`, and `no scheduled job matches` when none does; Running now and Needs you are never filtered) and `gaveUp` = the Needs-you cards of schedules that gave up (both built by the schedule door, `listScheduled` / `listGaveUp`); `limits` = `{quietDays:30, olderRuns:7, needsYouDays:7}`. The page groups `runs` by `jobs` into Running now,
|
|
3440
|
+
Needs you, Jobs and a quiet fold; there is no Workflows / History switch and no separate `/api/workflows` endpoint); `GET /api/runs/:runid` (the full replay — parts/steps, counters,
|
|
3419
3441
|
summary, model/provider/judge). Every `GET /api/runs` row and the detail carry `runNo` (P6 item 7): runs of one job are numbered
|
|
3420
3442
|
oldest `at` first by ONE server function (`runNumbers`), and the page names a run `job (run-N)`; the run id stays in the Audit
|
|
3421
3443
|
tab and every command line. A run that came out of a panel drafting session also carries the DRAFTING part as the FIRST element of
|
|
@@ -3425,7 +3447,10 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3425
3447
|
ts-windowed to this run alone even when the sidecar file is shared with other runs, plus
|
|
3426
3448
|
the matching raw-log text windowed the same way — when one resolves); `GET
|
|
3427
3449
|
/api/runs/:runid/rounds?part=&attempt=&offset=&limit=` (one attempt's rounds, lazily
|
|
3428
|
-
paged, for the Audit tab's Grouped view); `GET /api/runs/:runid/
|
|
3450
|
+
paged, for the Audit tab's Grouped view); `GET /api/runs/:runid/result` and `GET /api/runs/:runid/download?i=<n>` (PRD v1.107 Part 6, the Result tab of a rubric run: `result`
|
|
3451
|
+
is the signed lines and fact sentences plus the rows of each delivered step file (a row carries `proof: {text, marks: [[start, end]]}` when the run's `rubric-evidence` record holds what the check read for that result: the words to mark, never found by searching; absent for an old run or a quote the check did not read; a money cell then reads as the page wrote it, "144 US dollars", else the fact's own spelling "$144"); both read ONLY the paths of the run's own `destination-written` records, re-check their
|
|
3452
|
+
sha256, and answer in words when the file changed or is gone; the detail carries `hasResult` and `delivered`); `POST /api/runs/:runid/rerun {text}` and `/pause` (human-click
|
|
3453
|
+
guard, the engine's own `run-u --door --decide`, only while the run waits at its review door; empty rerun words are refused); `GET /api/runs/:runid/job` (the signed spec's
|
|
3429
3454
|
own fields, ONLY when a bundle-layout run's `spec.json` is reachable — a run-u run has
|
|
3430
3455
|
none on disk, and this reads `resolved:false` with every spec-only field honestly
|
|
3431
3456
|
`'unknown'`, never guessed from the spine's own narrower `job-start` record). The run
|
|
@@ -3590,6 +3615,7 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3590
3615
|
Chat card in RESUME mode (title `RESUME <job> (run-N)`, filled from `GET /api/author/start-from`, every box locked
|
|
3591
3616
|
except the two caps, "spent so far" shown, Clear/Start drafting hidden) and `Sign & resume` on that card is the human
|
|
3592
3617
|
click, posting this same route; the run's Job tab is read-only on every run.
|
|
3618
|
+
**Remove from Needs you** → `POST /api/runs/:runid/needs-remove` (`src/panel/runroutes.js`, same `checkHumanGuard`, no body): appends ONE `{runid, type:'needs-removed', at}` row to `runs.jsonl` (`appendNeedsRemoved`; `readNeedsRemovals` reads it) and changes nothing else; 409 in plain words when the run is not in Needs you or waits for Accept. `readRunList` ignores the row type, so monthly accounting, the run counter and the scheduler never see it; a later stop (a newer `job-end`) puts the run back in Needs you. The run detail carries `canRemoveFromNeeds` and `needsAudit`.
|
|
3593
3619
|
**Stop (item 5)** → `POST /api/runs/:runid/stop` (`src/panel/runroutes.js`, `checkHumanGuard`, no body).
|
|
3594
3620
|
It writes the run's STOP REQUEST, a file `<spine>.stop` (`stopFilePath(spine)`, `src/legs.js` — the one
|
|
3595
3621
|
spelling), and signals nothing. `404` unknown run; `409` when the run's LATEST leg is not live
|
|
@@ -3613,34 +3639,38 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3613
3639
|
scout, plan or close-fix phase is honoured at that phase's next round boundary, the same as in a step (it used to be read in
|
|
3614
3640
|
step workers only). A stop in the close-fix loop reads "You stopped the run during the fix phase … a resume continues the
|
|
3615
3641
|
run" (`ralph`'s `phase` option; the step loop keeps "a resume re-enters this step").
|
|
3616
|
-
**
|
|
3617
|
-
|
|
3618
|
-
after `resume` when both show
|
|
3619
|
-
|
|
3620
|
-
code-owned origin line ("Copied from <job> (run-N) — <status word>. …").
|
|
3621
|
-
**Reuse workflow (replaces P5 item 3's Start from this, 2026-10-03)** — a button on runs that EARNED their green only
|
|
3622
|
-
(the Run tab's action row and the Ended block; `ended.actions` carries `{id:'reuse', label:'Reuse workflow'}` on `green`
|
|
3623
|
-
and green-with-destination-refused `green` rows, and on no other: an `already-green` run (the check passed before any
|
|
3624
|
-
work) offers Edit in chat instead, and a red row's next line is "Change the job: press Edit in chat."; stopped/capped/died
|
|
3625
|
-
rows offer `resume` and `edit`) and on every imported job; the Chat card's Check type is one radio group of three
|
|
3642
|
+
**Reuse workflow — ONE door on every run (2026-10-15, PRD v1.101; it replaced the green-only button and the old edit door)** —
|
|
3643
|
+
`ended.actions` carries `{id:'reuse', label:'Reuse workflow'}` on EVERY ending (green, failed, capped, stopped, died, every
|
|
3644
|
+
other red, already-green), after `resume` when both show, and on every imported job; a red row's next line is "Change the
|
|
3645
|
+
job: press Reuse workflow." The Chat card's Check type is one radio group of three
|
|
3626
3646
|
(Deterministic · Rubric · Reuse workflow), and the third opens a search picker over `GET /api/author/reuse-jobs` (below) that
|
|
3627
|
-
fills the card through this same path (a pick
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3647
|
+
fills the card through this same path (a pick puts the radio on the job's own check type). The button opens the Chat tab's New
|
|
3648
|
+
job card filled from the SIGNED job with EVERY box open (job lines and `~` rules, check type, judge examples, thinking, Inputs,
|
|
3649
|
+
Destination, Model, $ cap, Time cap) under a code-owned origin line "Reuse: <job> (run-N)"; nothing is locked and the server
|
|
3650
|
+
no longer refuses a changed job box. **The path is decided by the CARD, not the drafted spec** (`jobBoxesChanged(card, originCard)`
|
|
3651
|
+
in `src/panel/authorsession.js`): the JOB boxes are the job text, the check type and the thinking tick; the RUN boxes (Inputs,
|
|
3652
|
+
Destination, Model, $ cap, Time cap) never decide it. A GREEN origin (an imported job counts as green) with no job box changed is
|
|
3653
|
+
today's reuse — the signed plan verbatim, nothing drafted ($0), a new run of the same workflow signed under the ORIGIN job name.
|
|
3654
|
+
Any job box changed (green or red origin) drafts a NEW workflow named by the Job name box. A non-green origin with no job box
|
|
3655
|
+
changed also drafts (an unproven plan is never reused), signed under the ORIGIN name (another try of the same job). The name is
|
|
3656
|
+
fixed before drafting, so the hash the person signs already covers it. The Job name box shows the origin name until a job box
|
|
3657
|
+
changes, then the next free `<base>-N` (`GET start-from` returns it as `nextName`; renamable; base = the part before a trailing
|
|
3658
|
+
`-<digits>` when that part is itself a run-list or jobs/ job, so `x-2` suggests `x-3`); changing the boxes back restores the origin name. A name owned by a
|
|
3659
|
+
different job, or the origin's own name on a changed job, is refused at $0 ("A job named "x" already exists and is a different job.
|
|
3660
|
+
Pick another name."); a name typed while nothing is changed is ignored (the origin's is forced). The main button reads Sign & run on
|
|
3661
|
+
the reuse path and Start drafting otherwise, flipping as the person types. A new workflow has no schedule until its own first green;
|
|
3662
|
+
nothing here ever clears a schedule. Only the schedule card keeps its job boxes locked (it signs the green run's plan as it is).
|
|
3663
|
+
Routes, behind `checkHumanGuard`:
|
|
3633
3664
|
`GET /api/author/start-from?runid=<id>` or `?import=<id>` → `{ok, origin, card, from:'signed job'|'imported job',
|
|
3634
|
-
note,
|
|
3665
|
+
note, green, nextName, specHash, workflowKey, trackRecord:{runs,
|
|
3635
3666
|
green, notGreen, live, finished, avgSpendUsd, avgWallMs}, line}` (`404` unknown, `409` with an `error` when
|
|
3636
3667
|
there is nothing to copy: no log, the run's signed job is not on disk, or an imported job that changed, lost its
|
|
3637
3668
|
dependency or no longer matches its signed bytes); `POST /api/author/start` accepts `startFrom: <runid>` or
|
|
3638
|
-
`startFrom: {importId}` beside the card and answers `{ok, sessionId, reuse, state}
|
|
3639
|
-
|
|
3640
|
-
be reused — this one did not"` — the server owns the rule, not just the pickers. The old
|
|
3669
|
+
`startFrom: {importId}` beside the card and answers `{ok, sessionId, reuse, state}` (`reuse` false = drafting); the server
|
|
3670
|
+
makes the card-compare decision above, so a hand-made POST cannot reuse a plan that did not earn its green. The old
|
|
3641
3671
|
`POST /api/author/start-from-check` and the "Changed — new job" mode are gone. `GET /api/author/reuse-jobs[?model=<Name>]` →
|
|
3642
3672
|
`{ok, jobs:[{job, runid, checkType:'deterministic'|'rubric', line}]}` is the Chat card's Reuse workflow picker list: one entry per
|
|
3643
|
-
job (same `workflowKey`) with at least one
|
|
3673
|
+
job (same `workflowKey`) with at least one `green` or `already-green` local run (RULED 2026-10-15: an already-green run is a reuse source), `runid` = its newest such run (hand it to `start-from`), `line` =
|
|
3644
3674
|
start-from's own estimate line, newest green first; imported jobs are not listed. **Identity, two keys:** the
|
|
3645
3675
|
signature hash `jobSpecHash` is unchanged (it covers the caps and the write fence — what the person signs);
|
|
3646
3676
|
`workflowKey(spec)` (`src/job.js`, not exported from the package root) is a sha256 over the signed spec WITHOUT
|
|
@@ -3651,7 +3681,7 @@ key you want first (the panel picks the row by the Model menu).
|
|
|
3651
3681
|
when the Name is not the provider's default tier — spelled as authoring spells it; a RUBRIC origin with no `judge`
|
|
3652
3682
|
first gets `judge` = its own resolved judge identity so the judge never follows the worker; an explicit judge is
|
|
3653
3683
|
kept; deterministic jobs get none), and signs under a
|
|
3654
|
-
NEW `jobSpecHash` with the SAME `workflowKey` (the session refuses a spec whose key moved).
|
|
3684
|
+
NEW `jobSpecHash` with the SAME `workflowKey` (the session refuses a spec whose key moved). The job boxes carry the
|
|
3655
3685
|
signed values read by the Job tab's own readers. **Estimate line** above the card: "Same job — G green · N not
|
|
3656
3686
|
green · about $X and M min a run", from every LISTED run whose signed spec has the same `workflowKey` AND ran the same worker (provider + baseUrl + model) as the Model box now names — `GET /api/author/start-from?…&model=<Name>` recomputes it when the box changes; none = "Same job — no runs yet on this model" (one run =
|
|
3657
3687
|
one entry via `legsOf`; the averages read FINISHED runs with exact figures — spend from `chainSpend`, working time
|
|
@@ -4358,19 +4388,51 @@ A **Rubric** job whose first Input is the full path of a FILE (not a repo folder
|
|
|
4358
4388
|
|
|
4359
4389
|
### Required facts — a line that finds, lists or looks something up (Part 1b, `src/facts.js`, `docs/product/ITEM33-BUILD.md` "Addendum 2026-10-13" and "Part 1c", PRD item 33 RF1–RF22)
|
|
4360
4390
|
|
|
4361
|
-
Check what the person asked for, not what the worker chose to write. bareloop needs bareguard `^0.
|
|
4391
|
+
Check what the person asked for, not what the worker chose to write. bareloop needs bareguard `^0.25.1` (`cited` + `decides`, bind-only and `text` decides, word-edge anchors; 0.24 lets money currency/unit be lists, ruling #65; bareloop facts now carry them, see "Currency and unit lists" below; 0.25 returns `full.evidence`, the spans the Result tab marks, see `rubric-evidence`).
|
|
4362
4392
|
|
|
4363
|
-
- **The part.** A `rubric-step` stage's `params.parts` may hold ONE part of `class: "fact"` (a line that finds, lists or looks up): `{ text, class: "fact", source: <input label>, facts: [{ name, shape, unit? | currency?, phrase?, anchor?, until?, rule?, aiProposal? }], count?: { min?, max? }, rules?: [n], covers?: [n] }`. `shape` is `money | number | date | time | phrase | text`; `rule` is `{ op: lt | lte | gt | gte, value }` or `{ op: "between", lo, hi }` (not on `text` or `phrase`); `money` names
|
|
4393
|
+
- **The part.** A `rubric-step` stage's `params.parts` may hold ONE part of `class: "fact"` (a line that finds, lists or looks up): `{ text, class: "fact", source: <input label>, facts: [{ name, shape, unit? | currency?, phrase?, anchor?, until?, rule?, aiProposal? }], count?: { min?, max? }, rules?: [n], covers?: [n] }`. `shape` is `money | number | date | time | phrase | text`; `rule` is `{ op: lt | lte | gt | gte, value }` or `{ op: "between", lo, hi }` (not on `text` or `phrase`); `money` names at least one of `unit` (the input's own words, e.g. `"US dollars"`) or `currency` (`$ € £`), and may name both, each one value or a list (below); `phrase` carries the fixed `phrase` the quote must contain; `anchor` / `until` are labels copied VERBATIM from the input. A fact name is letters, spaces, `_` or `-` only (it is written into the claim text bareguard scans for numbers). A line with a fact part has no word, section or claim-count parts (a drafting red). Code fills `tested` (the early-test evidence) and `sample` (what the drafter saw of a big input) into the signed part; a model-written one is replaced.
|
|
4364
4394
|
- **The step file.** `output/steps/<n>.json` of a facts line is `{ "results": [ { "quote": "...", "values": { "<fact name>": "<value as the quote writes it>" } } ] }` — no free text, no `cites`. ONE quote per result holds all its facts, so a worker cannot mix a price from one result with a time from another. `buildStepSpec` makes one bareguard `cited` check with `decides` (the fact's index is its `d<j>` key; a phrase fact is a presence decide and takes no value). The claim text is written by CODE from the values (`name: value; ...`); a `count` becomes `min` / `max` on the number of results. Every value is compared by shape and exactly (`8:15 AM` equals `08:15`, `389` equals `389.00`); a red names the result and the fact in plain words (`plainFactItem`). A JSON number in `values` is read as the string it prints as.
|
|
4365
4395
|
- **No record cutting (Part 1c).** A fact part carries no `record`, `tested` or `sample` (a `record` field is an unknown-field red). The worker's brief says "one quote per result, copied from that result's own text" (bareguard's quote cap of 4000 characters stays its own). Drafting does not grade: it only proves each fact CAN be checked (below).
|
|
4366
4396
|
- **Repeat rule (Part 1c M1, M4).** On a fact line asking for N results (`count.min`), `checkFactWords` (`src/factwords.js`, the one owner) refuses at $0 an anchor label the label list counts fewer than N times (`rubric-fact-label-rare`) and a `phrase` fact whose words `countPhrase` finds in the input fewer than N times (`rubric-fact-phrase-rare`); count 1 or none keeps a once-only one legal. `RUBRIC_RULES` states both.
|
|
4367
|
-
- **The look, then the draft (Part 1c).** For a rubric job on files, `authorCloseForJob` runs `runInputLook`
|
|
4397
|
+
- **The look, then the draft (Part 1c; the default look is now the page-word map, next bullet).** For a rubric job on files, `authorCloseForJob` runs the look (`lookFn`, default `runPageMap`; the older search-and-read `runInputLook` in `src/authorscout.js`, pure parts in `src/inputlook.js`, is unwired from the default path) on the frozen input text BEFORE the confirm turn and the draft, when a `provider` is present (`lookFn: null` = no look; a panel test that replaces `generate` gets none unless it passes `lookFn`). Read-only by construction: tools `search_input` / `read_input` over in-memory text, `LOOK_ROUNDS` 8, a reserved final round whose only tool is the strict answer form `report_look`, spend metered into the drafting book (`calls` absorbed like the repo scout's). Its report (facts with a label picked from the list, prose lines, not-found entries) is verified by code (`verifyLookReport`: a label off the list, an unknown shape/line/input, or a phrase not in the source is moved to not-found, named) and rendered into the drafter's prompt (`renderLookReport`); a failed or reportless look is an ABSENT survey and stops drafting at $0 (`scout-absent`, "the first look at your repo or input did not complete"). Every place, date or name in the job line is a fact too, bound to the label before it (a direction = phrase facts `Leaves <place>` / `arrives at <place>`); prose lines and their `~` limits are not looked up.
|
|
4398
|
+
- **The page-word map (idea A core, `src/pagemap.js`, `src/pagerules.js`, `docs/product/ITEM33-BUILD.md` "Addendum 2026-10-18 — Idea A core").** The default `lookFn` is `runPageMap`: ONE AI call per page input (the job's drafter identity, thinking off, booked per call into `draft-spend.json`, cap checked before each call) maps the job to the page's OWN words as `{"facts":[{name, from, shape: place|money|time|phrase, words, currency?, unit?}]}`; it takes the old look's arguments and returns the same envelope. Plain code then keeps or drops each word (`applyPageRules(text, mapping, jobLines)` -> `{ facts, dropped }`, pure, ported from the frozen POC): found-on-page (count >= 1), N2 (a label written 2+ times inside one page line is unusable), R1 (a second label stays only if it reads the same values as the first), U1 (a label the page writes AFTER the number, "EUR 490 total", becomes a unit; a label with no number either side is dropped), R1L (a unit/currency form stays only if it reads the same numbers as the primary form; units first), S1 (a fact name that counts results is refused; two facts may not share shape AND label). The AI never grades; the answer is read strictly (no JSON repair): a malformed or truncated answer is retried ONCE, twice is the plain stop "The AI's answer about the page was not readable twice. Nothing was signed." (an ABSENT survey; a draft is not a run, so no Resume); one retry on a transport error (booked unpriced, so `spendComplete=false`) or a 429 (the vendor's stated wait). The scrubbed raw answer rides each call into `draft-log.jsonl` (`call.raw`), unsigned, so a run can be replayed at $0. A drafted fact may use only words the map KEPT: a signed anchor, closing label, unit or currency that was dropped is the red `rubric-fact-word-dropped` with the drop's reason, and an anchor must be a kept label (`rubric-fact-label-off-list`, now against the kept set).
|
|
4399
|
+
- **Currency and unit lists (idea A core, `src/facts.js`).** A money fact's `unit` and `currency` may each be one string (every spec signed before lists) or a non-empty list: at most 3 currencies (`$ € £` only; a code like `EUR` is a unit) and 10 units, none repeated, a unit at most 100 characters, enforced by our own plain reds before bareguard is asked. A string reads exactly as a one-element list and is never rewritten, so an old signed spec keeps its bytes and hash. The first listed form is the primary one in the plan row and the worker brief; others read "or". `checkFactWords` requires EVERY listed unit and sign to be on the page.
|
|
4400
|
+
- **No silent drop (idea A core, `src/pagedropwords.js`, `withDropNotes` in `src/rubricstep.js`).** Every word, fact or `~` rule the page rules drop is shown as Not checked with a plain sentence from ONE table (no other file writes a drop reason). Before validation `withDropNotes` appends to the declaration: `closeDecl.refused` `{ line, rule, reason }` for a dropped fact whose `~` rule no part carries (never a line-level refusal; a rule the drafter already listed is not listed twice), and one line in `closeDecl.notes` for any other drop, including a word cut from a fact that is still checked. The confirm card gets the same lines (`plan.notChecked`) before drafting is paid for, and the drafter's look block says `NOT CHECKED (rule n)` / `LEFT OUT`. Nothing about this changes a signed spec's verdict rules; it is the Not checked list, in the person's words.
|
|
4368
4401
|
- **Labels are picked, never typed (`src/labelscan.js`).** `scanLabels(text)` lists, per shape (money, number, date, time, text), the words right before each value with counts (top 40 per shape; one generic scan, no per-site table; a page reader's `[ref=N]` tags are ignored). `labelOnList(scan, shape, idOrText)` is the one test (id or exact words, white-space blind). The look and the drafter pick labels only from the list shown in the prompt (`renderLabelList`). Fact-line reds are plain sentences; the confirm step and the drafter share one rule text (`RUBRIC_RULES`), and the drafter's per-shape field guide is generated from bareguard's `rubricVocabulary`.
|
|
4369
4402
|
- **Found-once check (`src/factwords.js`, `checkDeclarationWords`, run after validation in `authorClose`).** Every anchor must be on the input's label list for its shape, and every `until`, phrase and unit must be in the source once (white-space blind: U+202F, U+00A0; phrases whole-word, case-blind). A miss is a red naming the word and the input: `rubric-fact-label-off-list`, `rubric-fact-word-missing`, `rubric-fact-unread` (an input that cannot be read is a red, never a skipped check). Nothing is graded; finding and grading are the run's and the close's job.
|
|
4370
4403
|
- **No silently dropped rule (RF16).** When a close holds a fact part or any part with `rules`, every `~` rule of every job line must be carried out by a part (its number in the part's `rules`, 1 = the first `~` rule) or listed in `refused` as `{ line, rule, reason }`, or its whole line refused; a rule that is none is a $0 red (`rubric-rule-dropped`). `refused` entries may now carry `rule`. A number the person did not write (a bound not found in the line's own words) must be marked `aiProposal: true` (the card says "AI's proposal — check it"), and an AI-proposed time limit must be `between`, never one-sided (RF17).
|
|
4371
4404
|
- **The prose step.** A line that writes keeps its word / section / line counts. When any fact line comes BEFORE it, code puts one `numbersFromFacts` part on it (`withFactBindings`; the model never writes it, and a missing or wrong one is a drafting red): every number in `text` must appear in a quote of those fact lines' step files (bareguard's `numbersInQuote`: token match, not canonical, so `v0.41.0` is one token). The card says, under Not checked, `line N: what the text says — you judge it at Accept`. At the close, a tied earlier line whose step file does not exist yet (the seed read, before any worker wrote) is a normal red ("line N's answer is not there yet"), never an instrument stop; only an unreadable file is a stop.
|
|
4372
4405
|
- **The plan card.** One code-worded row per fact under its line, read by the one reader (`checksOfClose` / `factRows`) the confirm card, the Job tab (`planFromSpec`) and the sign-time `planMatchesClose` share, e.g. `fact · price — money in US dollars, at most 450 US dollars, after "Price", quoted from "Flights"`, then the count row. A `~` rule the drafter listed as not checked reads `line N: "<rule as written>" — <reason>`.
|
|
4373
4406
|
- **The worker brief.** `rubricGoal({ ..., closeDecl })` adds a code-written brief per line (`lineBriefs`): a facts line gets its facts and its count; a text line that follows gets "every number must be copied exactly as written in a quote you wrote for job line N". `stepFileContract()` names both shapes.
|
|
4374
|
-
- **Web addresses as inputs (Part 1c, `src/pagefetch.js`, `src/source.js`).** An Inputs line may be `label: https://...` (also a rubric card's FIRST line; `looksLikeRubricInput`). `fetchPageText(url)` calls barebrowse `connect({ incognito: true, blockPrivateNetwork: true })` (`CONNECT_OPTIONS`) → `goto` → `waitForNetworkIdle({ idle: 1500, timeout: 20000 })` (bounded; a page that never settles is read as it stands) → `snapshot({ mode: 'act' })`, always closing the browser, with our own 90 s deadline (L1: a load-time `browse()` snapshot gave 77,951, 8,801 or 719 characters of the same results page; L2: a page whose text outside the banner / navigation / contentinfo landmarks is under `MIN_CONTENT_CHARS` = 100 (`pageContentChars`) is a stop too, "only the site's own header and menus", and an input with no content never reaches the look or an early ask: the survey is ABSENT, "holds nothing to read", nothing spent); an empty or under-200-character page (`MIN_PAGE_CHARS`), a bot challenge, an unreachable address, a credentialed or secret-shaped address, or a page carrying a secret shape is a plain-words $0 stop on its line. `proveRubricInputs({ ..., fetchUrl })` freezes the page text under `input/<label>.txt` and records `url` on the manifest input row. A FRESH `run-u --spec` start (not `--resume` / `--door` / `--decide`) calls `refreezeUrlInputs`: it fetches every recorded address again, writes that text into the prepared tree, commits it as the new seed and rewrites `source.json` (new `sha256` and `seed`, `draftSha256` keeps what the look saw) BEFORE the worker starts, so quotes are checked against the run's copy and the worker reads it, never the live page. A failed or empty fetch stops the run, "The run did not start. Nothing spent." Plan card (L3): `settlePersonsWords` (called in `authorClose` before validation) unmarks AI's proposal on a limit made of the person's own numbers, swaps in the one explicit positive time range the person wrote, and drops a `refused` entry for a `~` rule a part of its line carries; the look's fact may carry `forRule` (a rule it numbered from its own knowledge, marked AI's proposal), which removes that rule from the look's not-found list so it is not asked. Test seams: `fetchPageText(url, { connect })`, `deps.fetchUrl` (`run-u`), `deps.fetchUrl` / `deps.lookFn` (`createSession`).
|
|
4375
|
-
- **Ask at once, only about intent (I1).** A look
|
|
4407
|
+
- **Web addresses as inputs (Part 1c, `src/pagefetch.js`, `src/source.js`).** An Inputs line may be `label: https://...` (also a rubric card's FIRST line; `looksLikeRubricInput`). `fetchPageText(url)` calls barebrowse `connect({ incognito: true, blockPrivateNetwork: true })` (`CONNECT_OPTIONS`) → `goto` → `waitForNetworkIdle({ idle: 1500, timeout: 20000 })` (bounded; a page that never settles is read as it stands) → `snapshot({ mode: 'act' })`, always closing the browser, with our own 90 s deadline (L1: a load-time `browse()` snapshot gave 77,951, 8,801 or 719 characters of the same results page; L2: a page whose text outside the banner / navigation / contentinfo landmarks is under `MIN_CONTENT_CHARS` = 100 (`pageContentChars`) is a stop too, "only the site's own header and menus", and an input with no content never reaches the look or an early ask: the survey is ABSENT, "holds nothing to read", nothing spent); an empty or under-200-character page (`MIN_PAGE_CHARS`), a bot challenge, an unreachable address, a credentialed or secret-shaped address, or a page carrying a secret shape is a plain-words $0 stop on its line. `proveRubricInputs({ ..., fetchUrl })` freezes the page text under `input/<label>.txt` and records `url` on the manifest input row. A FRESH `run-u --spec` start (not `--resume` / `--door` / `--decide`) calls `refreezeUrlInputs`: it fetches every recorded address again, writes that text into the prepared tree, commits it as the new seed and rewrites `source.json` (new `sha256` and `seed`, `draftSha256` keeps what the look saw) BEFORE the worker starts (inside `execute`, AFTER the run's row is listed with its pid and cap, so the Runs list shows it while the page loads; the Run tab reads "fetching the page…" until `source.json` carries `refrozenAt`), so quotes are checked against the run's copy and the worker reads it, never the live page. A failed or empty fetch stops the run (the row is settled at $0 as `failed` with the plain reason, `refused before start`, and the monthly claim is freed), "The run did not start. Nothing spent." A rubric job on a URL or file is recognised by the signed spec's verdict type on a Reuse and on a scheduled fire (`startSession`, `src/panel/authorroutes.js`), so line 1 is not proved as a repo path; a fresh job reads it from the card. Plan card (L3): `settlePersonsWords` (called in `authorClose` before validation) unmarks AI's proposal on a limit made of the person's own numbers, swaps in the one explicit positive time range the person wrote, and drops a `refused` entry for a `~` rule a part of its line carries; the look's fact may carry `forRule` (a rule it numbered from its own knowledge, marked AI's proposal), which removes that rule from the look's not-found list so it is not asked. Test seams: `fetchPageText(url, { connect })`, `deps.fetchUrl` (`run-u`), `deps.fetchUrl` / `deps.lookFn` (`createSession`).
|
|
4408
|
+
- **Ask at once, only about intent (I1; the asks are now code-forced from the page map, see "The look reads, code asks" below).** A `~` rule the look cannot tie to a kept page word becomes a question worded by code (`askableFromLook`, `src/pagemap.js`: line and rule named; the older `ruleQuestions` in `src/inputlook.js` is no longer called by the draft path), asked before the confirm turn through the existing question gate (`answerQuestions`; `normalizeQuestions` caps it at `MAX_QUESTIONS`); the answer is a new `~` rule on that line. A rubric job on files never offers the piecemeal try-3 question; its failed-draft stop (`draftStopText({ wholeJob: true })`) says to review the whole job and submit it again.
|
|
4376
4409
|
- **Known limits (documented, not built around).** Airport codes like SFO have no stable label on a flight page: direction is checkable only through the full airport names the page uses. An unanchored `text` fact cannot tell which field a value sits in. `am` / `pm` in lower case and `9:27AM` are malformed times; `v0.41.0` is one token. A fact step's quote proves the value is in a real record, not that it is the RIGHT record: "the cheapest" and "the last 3" are Not checked. Verified on real files only; the live proof (DeepSeek, both jobs through the panel) has not been run.
|
|
4410
|
+
|
|
4411
|
+
## Triggers — a job that starts by itself (panel-only; `src/trigger*.js`, `src/panel/schedules.js`, `schedulestart.js`, `scheduler.js`, `src/schedulewords.js`)
|
|
4412
|
+
|
|
4413
|
+
- **The `trigger` field of a signed spec** is a typed object, never cron text: `{kind:'daily'|'weekdays', hour, minute}`, `{kind:'weekly', day:'mon'..'sun', hour, minute}`, `{kind:'new-file', folder}` or `{kind:'file-updated', path}` (absolute paths). By hand = the key is ABSENT, so a by-hand spec hashes as before. `workflowKey` ignores it. The agent never sets it: `assembleSpec` refuses a draft that carries it.
|
|
4414
|
+
- **Only the panel fires.** `createPanelServer` runs one 500 ms unref'd scheduler (poll only, never `fs.watch`); the CLI never fires. Panel off = nothing fires; slots missed collapse to ONE catch-up (marked when more than 2 minutes late). `createPanelServer` test seams: `scheduleNow`, `schedulerIntervalMs` (0 = no timer), `sessionDeps`, `acceptWaitMs`.
|
|
4415
|
+
- **Files live under `<config home>/triggers/<job>/`**: `schedule.json` (the current signed schedule) and ONE append-only `ledger.jsonl` (every row carries `sig`, the spec hash it was written under). A run that a schedule started carries `trigger.json` beside its `resolved-spec.json`.
|
|
4416
|
+
- **Setting one is two clicks, both $0** (`POST /api/author/schedule-prepare`, then `/api/author/schedule` with the hash). It needs a real green run of the job's CURRENT workflow (a rubric job also an honoured Accept). A file trigger is for plain-files jobs only. Cancel is never refused. A reuse never clears a schedule: Reuse workflow (the one door, v1.101) keeps the origin's name while no job box is changed and switches the Job name to the next free `<base>-N` once one is; a changed job is a new workflow that needs its own green before it can be scheduled.
|
|
4417
|
+
- **A fire goes busy, then monthly limit, then `wouldAccept`, then the one starter** (the same session start and `signRun` as a Reuse the person clicked, with `--review-door`). Busy, monthly and refused fires consume nothing and are told once per `key|outcome|why`, so they retry. A run is busy while its pid is alive AND its process started no later than 5 s after its run row was written (a recycled pid is not that run). A run waiting at its review door is not busy.
|
|
4418
|
+
- **An install-needed refusal backs off, then gives up.** When a fire's copy needs packages, that job's next attempt waits 1, 2, 3, 5, 8, 13, then 21 minutes (in memory: a restart begins at the first wait); a wait that would pass 60 minutes in total is not taken: the schedule writes ONE `gave-up` row and is paused (nothing fires; files still queue as `arrived`), and the job shows in Needs you with the pill `Check` (`listGaveUp`). Signing the schedule again writes `resumed` and clears it. Constants: `INSTALL_RETRY_STEPS_MS`, `INSTALL_GIVE_UP_MS` (`src/panel/schedules.js`).
|
|
4419
|
+
- **A scheduled green is held until accepted**: `getStartFrom` reads its outcome as `green-held` and the Reuse list skips it, until the spine holds an honoured accept.
|
|
4420
|
+
- **Accept** (`POST /api/runs/<id>/accept`) runs the engine's own door path (`run-u --spec <signed spec> --door <id> --decide accept --approve <hash>`): the mechanical stages re-run, a red is `door-accept-red`, the run's verdict is untouched. It shows only while `doorWaiting(events)` (`src/reviewdoor.js`) is true; the same answer fills the runs list's Needs-you `Accept`.
|
|
4421
|
+
- **Every word the page shows** comes from `src/schedulewords.js` (`SCHEDULE_WORDS` is the one button table the server sends).
|
|
4422
|
+
- **The schedule is shown on three tabs** (v1.102): the Run tab's one summary block (ENDED dated, NEXT, SCHEDULE with `last`/`next`; row and frame red when the last fire started nothing), the Job tab's Started and Schedule rows (`getRunJob`), and the Audit's own folded `schedule` section on every run of a scheduled job (v1.104: `detail.scheduleSection` `{title, sign, word, head, rows}`, drawn `[sign] word schedule · head` with the sign and word both from the status table, v1.106 Part 4; a run a cancelled schedule started keeps `detail.startedAudit`). `/api/runs` `scheduled[]` and `gaveUp[]` carry `runNo` so the small lists read `job · run-N`. `trigger.json` carries `triggerKind`, `sessionId`, `originRunid`, `sig`, `lateMs`; each signing appends a `signed` ledger row naming its origin run.
|
|
4423
|
+
|
|
4424
|
+
|
|
4425
|
+
## The Audit's tries, checks and answered questions (PRD v1.106 Part 5)
|
|
4426
|
+
|
|
4427
|
+
For an integrating agent reading a run's record:
|
|
4428
|
+
|
|
4429
|
+
- **`output-judged` spine record** `{step, iteration, path, bytes, sha256}`: written by `runPlan`'s step `judge()` right before the `exit-eval`, once per step file a `rubric-step` check reads (`outputJudgedRecords`, `src/rubricstep.js`, from the signed `closeDecl`). The file is overwritten by the next try, so this is the only trace; content is never written. Old runs have none, and show no judged-file line.
|
|
4430
|
+
- **`rubric-evidence` spine record** `{step, iteration?, evidence, evidenceTotal?}`: report-only. Written by `runPlan` (`onRubricEvidence`, fed by the `rubric-step` kind) each time a step's `cited` checks read a step file; `evidence` is bareguard 0.25's `full.evidence` (`{check, claim, text?, spans, spansTotal?}` per claim; span offsets are UTF-16 into that claim's normalized `text`). `iteration` is present for a step try's own check and absent for the outer close. The `text` is masked like every spine write; a claim whose text changed by masking keeps `spans: []`. No code in the verdict, governor or money paths reads it; the Result tab reads it to mark the words each check read. Nothing is written when the check read no claim. Old runs have none.
|
|
4431
|
+
- **Per-try checks** come from `exit-eval {step, iteration, results}`: a `check-passes` result is named by the `check-run` record just before it, a `tree-changed` result is the declared `files-changed` stage. A try that began (`iteration-start`) in a step that has ended and has no `exit-eval` was cut off (`part.cutOffTry`). `replayRun` step attempts carry `checks` and `outputJudged`; the panel server adds `tries[]`, `triesLine`, `tally` and `marker`.
|
|
4432
|
+
- **Run summary** words `callsLine` / `checksLine` (`callsLineOf`, `checksLineOf`, `src/panel/server.js`): counts from the raw spine, never a fraction.
|
|
4433
|
+
- **Review door part** (`kind: 'door'`) when the spine has `review-door`; words in `DOOR_WORDS` (`src/panel/status.js`).
|
|
4434
|
+
- **`GET /api/runs/:id/rounds?part=N&all=1`** returns a step's whole Round table as rows (`kind: model | tool | check`), 20 per page (`offset`).
|
|
4435
|
+
- **`draft-log.jsonl` `human` rows** `{no, source: confirm | look | try-3, question, answer, line?, at}`; the answer is secret-masked before it is written and the row exists only when the question was answered.
|
|
4436
|
+
- **Money line** `moneyWithDraft(spendCore, draftUsd, budgetUsd, draftComplete, runUsd)`: `$run run + $draft drafting = $total of $cap`; without `runUsd` the sum is omitted. The panel's own spelling is `panelMoney2` (`src/panelmoney.js`, one Node owner; `src/panel/index.html` carries a pinned browser copy): 2 decimals from a cent up, 4 decimals for a real amount under a cent (`$0.0054`), `<$0.0001` below that, exact zero `$0.00`, null `unknown`; the CLI's `money()` keeps its own 4 decimals.
|
|
4437
|
+
- Not built from the signed spec (see the Part 5 commits): the early ask about a number the AI made (T1), because the data it needs does not exist at that point; T1 is replaced by Part 5b below. The "fetching the page…" state (Q2) was built after the run-start refetch moved to after the run is listed: the sign progress list shows the step (`settleFetchingStep`, `src/panel/authorroutes.js`) and a listed run with no log yet reads "fetching the page… the run has not started yet" (`startingStub`, `webFetchState`), both read from `source.json`'s `refrozenAt`.
|
|
4438
|
+
- **The look reads, code asks (PRD v1.107 Part 5b; `src/pagemap.js`).** A map fact on a `time` or `money` shape may carry an optional `reading: {op, value | lo, hi}` (the signed-rule `op` set; a time bound is a string, a money bound a number); any other `reading` makes the whole map answer malformed (no repair). `askableFromLook(look, jobLines)` sorts every `~` rule: a kept page word carries it (no ask); a `reading` whose bound is NOT in the person's own words (`boundInWords`, the same test as `rubric-fact-unmarked`) asks `Your line N says "<rule>". I read it as: <fact> <ruleWords>. Agree?`; a rule no kept fact carries asks `... I can't check this on the page. Drop it?`. Readings come first, then can't-check asks. `agree` (or `yes`) writes the reading as a `~` rule in plain words, so the bound is the person's own and the fact signs plain; a typed answer is the `~` rule instead; `drop it` on a can't-check ask adds no rule and leaves it Not checked. `MAX_QUESTIONS` is 3: more asks than that stops the draft before the confirm turn (stop `look-asks-too-many`, no extra spend) with `tooManyAsksText`, a code-written list of every ask and its reading. Asks carry `ask: 'reading' | 'cant-check'` (and `reading` for the agree text) through `normalizeQuestions`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bareloop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.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",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"bare-agent": "^0.52.0",
|
|
31
31
|
"barebrowse": "^0.21.0",
|
|
32
|
-
"bareguard": "^0.
|
|
32
|
+
"bareguard": "^0.25.1",
|
|
33
33
|
"litectx": "^0.32.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|