bareloop 0.45.0 → 0.46.1

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/bareloop.context.md +34 -7
  3. package/package.json +1 -1
  4. package/src/attemptdiff.js +116 -0
  5. package/src/authorreadout.js +55 -14
  6. package/src/authorscout.js +1 -1
  7. package/src/bundle.js +90 -6
  8. package/src/close-integrity.js +39 -5
  9. package/src/config.js +5 -2
  10. package/src/inputlook.js +1 -47
  11. package/src/judged.js +3 -3
  12. package/src/labelscan.js +13 -4
  13. package/src/ladder.js +2 -2
  14. package/src/learn.js +943 -0
  15. package/src/learnstore.js +154 -0
  16. package/src/modellist.js +1 -1
  17. package/src/pagedry.js +2 -3
  18. package/src/pagefetch.js +2 -1
  19. package/src/pagerules.js +27 -4
  20. package/src/panel/authorroutes.js +4 -3
  21. package/src/panel/authorsession.js +5 -7
  22. package/src/panel/index.html +108 -0
  23. package/src/panel/schedulestart.js +5 -0
  24. package/src/panel/server.js +16 -134
  25. package/src/panel/status.js +11 -0
  26. package/src/planrun.js +40 -6
  27. package/src/providerrows.js +3 -3
  28. package/src/run.js +4 -2
  29. package/src/schedulewords.js +1 -1
  30. package/src/source.js +1 -1
  31. package/src/stagewords.js +145 -0
  32. package/src/text.js +4 -2
  33. package/src/userrun.js +28 -0
  34. package/src/validate.js +10 -5
  35. package/src/worktree.js +8 -2
  36. package/types/attemptdiff.d.ts +38 -0
  37. package/types/authorreadout.d.ts +7 -0
  38. package/types/config.d.ts +1 -1
  39. package/types/inputlook.d.ts +0 -23
  40. package/types/labelscan.d.ts +6 -0
  41. package/types/learn.d.ts +575 -0
  42. package/types/learnstore.d.ts +107 -0
  43. package/types/pagerules.d.ts +7 -1
  44. package/types/panel/status.d.ts +8 -0
  45. package/types/planrun.d.ts +13 -3
  46. package/types/reuse.d.ts +1 -1
  47. package/types/run.d.ts +8 -2
  48. package/types/stagewords.d.ts +91 -0
@@ -19,6 +19,12 @@ export function tieOf(from: unknown, jobLines: {
19
19
  * @param {unknown} shape @param {unknown} r @returns {boolean}
20
20
  */
21
21
  export function validReading(shape: unknown, r: unknown): boolean;
22
+ /**
23
+ * The number written at the very end of `s` (only whitespace after it): the leftmost match of NUM followed by `\s*$`. Shared with src/pagedry.js (one copy). Scans back from the end, so a
24
+ * long run of digits and commas costs O(n) where a backtracking regex is quadratic.
25
+ * @param {string} s @returns {string|undefined}
26
+ */
27
+ export function numberAtEnd(s: string): string | undefined;
22
28
  /**
23
29
  * Apply the rules to the AI's map over one saved page. Returns the kept facts and, for everything a rule removed, one `dropped` entry.
24
30
  * The order of the rules is the POC's (apply-v3.mjs): found-on-page, then the rules that read the page. A place is read as a phrase.
@@ -74,7 +80,7 @@ export type KeptFact = {
74
80
  reading?: Reading;
75
81
  };
76
82
  /**
77
- * `other` = what the drop is compared to: the first label (R1), the primary form (R1L), the fact it would have repeated (S1)
83
+ * `other` = what the drop is compared to: the first label (R1), the primary form (R1L), the fact it would have repeated (S1), the phrase it looked for (phrase-no-result)
78
84
  */
79
85
  export type Dropped = {
80
86
  line: number;
@@ -1,3 +1,11 @@
1
+ /**
2
+ * ONE spelling of "this run's outcome is a green that can be reused" (the Reuse picker, the Reuse start/prefill, the Ended block's
3
+ * next line). hamr 2026-10-15: an `already-green` (fixed before any work) counts. `satisfied` is a close verdict, never a run outcome.
4
+ * The SCHEDULE gate (schedules.js greenFor) deliberately stays on a real `green` only (T3).
5
+ * @param {string|null|undefined} outcome
6
+ * @returns {boolean}
7
+ */
8
+ export function isReusableGreen(outcome: string | null | undefined): boolean;
1
9
  /**
2
10
  * The status of one run.
3
11
  * @param {{outcome: string|null|undefined, died?: boolean, category?: string|null, moneyHalt?: boolean}} o
@@ -149,6 +149,8 @@ export function boundedNote(bounded: {
149
149
  * is STATED as well as enforced (the mailbox precedent again: a law only the validator
150
150
  * knows costs a redraft per draft). The arms without it — `false` and `'diff'` — render
151
151
  * byte-identically to the pre-shim prompt.
152
+ * @param {string|null} [hints] the cross-run learnings block (src/learn.js `renderHints`), verbatim; null/empty
153
+ * renders byte-identically to the prompt without it. Only ever passed for the draft and the first replan.
152
154
  */
153
155
  export function planPrompt(job: any, scoutBlob: string, reds: any[] | null, maxStepRounds: number, failure: string | null, scopes: string[] | undefined, materials?: {
154
156
  balanceUsd?: number | null;
@@ -157,7 +159,7 @@ export function planPrompt(job: any, scoutBlob: string, reds: any[] | null, maxS
157
159
  }, startingDraft?: object | null, checkFacts?: {
158
160
  seedRed?: string[];
159
161
  priorChecks?: string[];
160
- }, doorNote?: string | null, readShim?: boolean | "cap" | "diff"): string;
162
+ }, doorNote?: string | null, readShim?: boolean | "cap" | "diff", hints?: string | null): string;
161
163
  /**
162
164
  * Execute a validateJob-GREEN plan-shape job (goal/verdictType/close).
163
165
  * Called by runJob after the approval gate, validation, and the smoke — this
@@ -402,12 +404,16 @@ export function planPrompt(job: any, scoutBlob: string, reds: any[] | null, maxS
402
404
  * the file, end the Loop after that round, and end the leg `stopped` (a checkpoint outcome, filed
403
405
  * the way a mid-step cap-halt is; resume re-enters that step, or continues the phase). Nothing is in flight at that
404
406
  * point, so the spend is exact. `null` = no stop surface (the default).
407
+ * @param {{text: string, ids: string[]}|null} [opts.learnHints=null] cross-run learnings (src/learn.js `renderHints`, made once at
408
+ * run start from the ledger as it stood). Handed to the PLANNER only: the first draft and the FIRST replan. No worker ever
409
+ * receives it, and a later replan gets nothing. Each injection is recorded on the spine as a `learnings` record (the exact
410
+ * text) so the Audit can show it and the byte check can compare it with a fresh render. `null` = no hints (the default).
405
411
  * @returns {Promise<string>} 'green' | 'already-green' | 'escalated' | 'plan-red' |
406
412
  * 'check-red' | 'close-red' | 'close-unsupported' | 'recipe-stale' | 'pricing-red' |
407
413
  * 'branch-red' | 'cap-halt' | 'wall-halt' | 'stopped' | 'provider-red' | 'interpreter-red' |
408
414
  * 'step-stalled' | 'hitl-pause' | 'hitl-decision-red' | `step-red:<id>`
409
415
  */
410
- export function runPlan(job: any, { workdir, provider, nativeProvider, providerFor, judgeProvider, judgeModel, rates, judgeRates, emit, remainingUsd, isUnpriced, spendComplete, capRuns, strikeLimit, closeTimeoutMs, closeDir, maxStepRounds, layerRoot, readShim, scout, scoutRounds, bridge, now, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, priorSpentUsd, reviewDoor, doorRerun, resumable, stopFile }: {
416
+ export function runPlan(job: any, { workdir, provider, nativeProvider, providerFor, judgeProvider, judgeModel, rates, judgeRates, emit, remainingUsd, isUnpriced, spendComplete, capRuns, strikeLimit, closeTimeoutMs, closeDir, maxStepRounds, layerRoot, readShim, scout, scoutRounds, bridge, now, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, priorSpentUsd, reviewDoor, doorRerun, resumable, stopFile, learnHints }: {
411
417
  workdir: string;
412
418
  provider: any;
413
419
  nativeProvider?: ((o: {
@@ -437,7 +443,7 @@ export function runPlan(job: any, { workdir, provider, nativeProvider, providerF
437
443
  maxStepRounds?: number | undefined;
438
444
  scout?: boolean | undefined;
439
445
  scoutRounds?: number | undefined;
440
- readShim?: boolean | "cap" | "diff" | undefined;
446
+ readShim?: boolean | "diff" | "cap" | undefined;
441
447
  layerRoot?: boolean | undefined;
442
448
  bridge?: unknown;
443
449
  priorWallMs?: number | undefined;
@@ -471,6 +477,10 @@ export function runPlan(job: any, { workdir, provider, nativeProvider, providerF
471
477
  } | null | undefined;
472
478
  resumable?: boolean | undefined;
473
479
  stopFile?: string | null | undefined;
480
+ learnHints?: {
481
+ text: string;
482
+ ids: string[];
483
+ } | null | undefined;
474
484
  }): Promise<string>;
475
485
  export { rateSourceFields };
476
486
  /**
package/types/reuse.d.ts CHANGED
@@ -414,7 +414,7 @@ export function runReuse(opts: {
414
414
  closeTimeoutMs?: number | undefined;
415
415
  closeDir?: string | undefined;
416
416
  layerRoot?: boolean | undefined;
417
- readShim?: boolean | "cap" | "diff" | undefined;
417
+ readShim?: boolean | "diff" | "cap" | undefined;
418
418
  resume?: any;
419
419
  runJob?: typeof shippedRunJob | undefined;
420
420
  now?: (() => number) | undefined;
package/types/run.d.ts CHANGED
@@ -198,6 +198,8 @@
198
198
  * @param {string|null} [opts.stopFile=null] P5 item 5 — the run's STOP REQUEST file
199
199
  * (`stopFilePath(spine)`, src/legs.js), forwarded verbatim to the plan flow, which reads it at the
200
200
  * round-boundary seam (where the money cap binds) and ends the leg `stopped`. `null` = no stop surface.
201
+ * @param {{text: string, ids: string[]}|null} [opts.learnHints=null] cross-run learnings for the PLANNER only (the draft and the
202
+ * first replan); forwarded verbatim to the plan flow, which records what it handed over as a `learnings` spine record.
201
203
  * @returns {Promise<string>} outcome: 'green' | 'already-green' | 'escalated' |
202
204
  * 'unapproved-spec' | 'job-red' | 'smoke-red' | 'plan-red' | 'check-red' |
203
205
  * 'close-red' | 'close-unsupported' | 'recipe-stale' | 'branch-red' | 'pricing-red' | 'provider-red' |
@@ -212,7 +214,7 @@
212
214
  * deliberately NOT in the resumable/checkpoint set (`src/reuse.js`) — an
213
215
  * unknown in-flight state is not a known-safe resume point.
214
216
  */
215
- export function runJob(rawSpec: object | string, { approvals, workdir, provider, nativeProvider, providerFor, judgeProvider, judgeModel, rates, judgeRates, emit, capRuns, strikeLimit, shellCapUsd, closeTimeoutMs, closeDir, layerRoot, readShim, scout, bridge, draftSpentUsd, draftSpendComplete, priorSpentUsd, priorSpendComplete, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, reviewDoor, doorRerun, resumable, stopFile }: {
217
+ export function runJob(rawSpec: object | string, { approvals, workdir, provider, nativeProvider, providerFor, judgeProvider, judgeModel, rates, judgeRates, emit, capRuns, strikeLimit, shellCapUsd, closeTimeoutMs, closeDir, layerRoot, readShim, scout, bridge, draftSpentUsd, draftSpendComplete, priorSpentUsd, priorSpendComplete, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, reviewDoor, doorRerun, resumable, stopFile, learnHints }: {
216
218
  approvals: unknown;
217
219
  workdir: string;
218
220
  provider: any;
@@ -255,7 +257,7 @@ export function runJob(rawSpec: object | string, { approvals, workdir, provider,
255
257
  grantUsed?: boolean;
256
258
  } | null | undefined;
257
259
  resumeBranch?: string | null | undefined;
258
- readShim?: boolean | "cap" | "diff" | undefined;
260
+ readShim?: boolean | "diff" | "cap" | undefined;
259
261
  scout?: boolean | undefined;
260
262
  layerRoot?: boolean | undefined;
261
263
  humanRuling?: {
@@ -275,6 +277,10 @@ export function runJob(rawSpec: object | string, { approvals, workdir, provider,
275
277
  } | null | undefined;
276
278
  resumable?: boolean | undefined;
277
279
  stopFile?: string | null | undefined;
280
+ learnHints?: {
281
+ text: string;
282
+ ids: string[];
283
+ } | null | undefined;
278
284
  }): Promise<string>;
279
285
  /** @typedef {{code: string, path: string, detail?: string}} Red */
280
286
  /**
@@ -0,0 +1,91 @@
1
+ /** `cmd`+`args`, as a comparable key — the one signal (besides `kind`
2
+ * itself) that two stages measure the SAME command's output, used to tell a
3
+ * genuine typecheck stage apart from the TYPES genre's other `lower-is-
4
+ * better`/`baseline: 0` stage (the failing-test-count half of `suite-green`'s
5
+ * "two assertions"), which shares that stage's own shape but not its
6
+ * population. `null` for a stage whose `cmd` isn't a non-empty string —
7
+ * never matches anything, so it never falsely suppresses a real typecheck
8
+ * question.
9
+ * @param {any} params @returns {string|null} */
10
+ export function cmdKey(params: any): string | null;
11
+ /**
12
+ * THE PLAIN-QUESTION OWNER (plain-checks build, 2026-09-28, hamr's ruling:
13
+ * the "checks N/M" headline is WITHDRAWN — "confusing, reads like 6 failed
14
+ * when 5 never ran"). ONE pure function mapping a declared stage's own
15
+ * signed `kind`+`params` (plus the close's own `genre`, also a signed field)
16
+ * to a short plain-English question a person can read without knowing the
17
+ * catalogue — never a second spelling on the client, and never anything the
18
+ * model authored (the catalogue's kinds/params are the whole vocabulary this
19
+ * reads).
20
+ *
21
+ * Every branch is traced straight off `src/kinds.js`'s runner semantics and
22
+ * the TYPES genre template (`src/authoring.js` `TYPES_GENRE_TEMPLATE`,
23
+ * `classGuards`'s `MECHANICAL_GUARDS`) — never guessed:
24
+ * - `files-changed` is always the `changed-from-seed` guard: "did it
25
+ * change any file?"
26
+ * - `pattern-absent-in-diff` is always the `no-suppressions` guard: "no
27
+ * casts or silencers added?"
28
+ * - `rubric-step` (Part 1a) is one job line's output file: "line N — does its output pass the line's checks?"
29
+ * - `command-exit` is the genre's `suite-green` stage (the only shipped use
30
+ * of this kind): "does the test suite pass?"
31
+ * - `count-not-worse` splits three ways on its own declared
32
+ * `direction`/`baseline` (never the runtime number — see
33
+ * {@link stageKindMetaFromSpec}'s own doc for why the baseline KIND must
34
+ * travel separately from the measured figure):
35
+ * - `higher-is-better` + `baseline: 'seed'` is the genre's `tests-kept`
36
+ * floor: "did all the old tests still exist?"
37
+ * - `lower-is-better` + `baseline: 0` is the genre's in-scope
38
+ * `typecheck` stage — UNLESS its own `cmd`+`args` is the SAME
39
+ * command a `command-exit` stage in the same close already runs
40
+ * (`suiteCmdKeys`, below): the TYPES template's `suite-green` is "the
41
+ * suite exits clean AND reports zero failing tests — TWO assertions"
42
+ * over ONE population (the same `npm test`/`pytest` invocation), so a
43
+ * `count-not-worse` stage sharing that exact command is the failing-
44
+ * test-count half of that pair, never a typecheck stage: "no failing
45
+ * tests?" — derived from the same cmd+args correlation (the stage
46
+ * counts the suite's own failing tests), never from the stage's own
47
+ * name string. The live check that found this (run mul5fofw's own
48
+ * `suite-zero-failing-tests`, cmd `npm test`, same as its sibling
49
+ * `suite-green` command-exit stage) would otherwise have read "test/
50
+ * has 0 type errors?", which is false: its parser counts FAILING
51
+ * TESTS (`^# fail (\d+)$`), not type-checker output. hamr's ruling:
52
+ * name this half honestly rather than reusing "type errors" for a
53
+ * population it was never about, or falling back to the stage's raw
54
+ * name. When the command differs from every sibling `command-exit`
55
+ * stage, the declared `scope.includePrefixes` naming exactly ONE path
56
+ * is quoted verbatim (a signed param, never invented): "<path> has 0
57
+ * type errors?" — otherwise the generic "type errors is 0?", never a
58
+ * guessed filename.
59
+ * - `lower-is-better` + `baseline: 'seed'` with a non-empty
60
+ * `scope.excludePrefixes` is the genre's `typecheck-outside` ceiling:
61
+ * "other files didn't get more type errors?"
62
+ * The "type errors" wording is genre-owned (`GENRE_WHAT`), because TYPES is
63
+ * the only genre this catalogue admits today (doc-genre kinds are not yet
64
+ * built) — an unrecognised genre, or a `count-not-worse` shape none of the
65
+ * three above matches, returns `null` rather than invent a fourth shape.
66
+ *
67
+ * `null` means "no honest plain question" — the caller (buildAttemptsList,
68
+ * partLine1Text's `attemptChecksLine`) falls back to the stage's OWN name,
69
+ * exactly as it already does for a stage with no resolvable kind at all.
70
+ * @param {{kind: string|null, params: any, genre: string|null, suiteCmdKeys: Set<string>}} o
71
+ * @returns {string|null}
72
+ */
73
+ export function stageQuestionText({ kind, params, genre, suiteCmdKeys, }: {
74
+ kind: string | null;
75
+ params: any;
76
+ genre: string | null;
77
+ suiteCmdKeys: Set<string>;
78
+ }): string | null;
79
+ /**
80
+ * Stage name to its plain question, off a signed spec's `closeDecl.stages` — the one pass that gathers every
81
+ * `command-exit` stage's cmd+args FIRST (so the failing-test-count half of `suite-green`'s pair can be told from a
82
+ * genuine typecheck stage) and then asks {@link stageQuestionText} per stage. `null` for a stage with no honest
83
+ * question; an empty map for a spec with no declared stages.
84
+ * @param {any} spec
85
+ * @returns {Map<string, string|null>}
86
+ */
87
+ export function stageQuestionsByName(spec: any): Map<string, string | null>;
88
+ /** the genre-owned noun for the count a TYPES-genre typecheck stage measures (see {@link stageQuestionText}) */
89
+ export const GENRE_WHAT: Readonly<{
90
+ TYPES: "type errors";
91
+ }>;