bareloop 0.13.0 → 0.15.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 +189 -0
- package/README.md +1 -1
- package/bareloop.context.md +375 -5
- package/package.json +3 -3
- package/src/behaviour.js +150 -0
- package/src/bridges.js +1 -1
- package/src/calibrate.js +1 -1
- package/src/cardauthor.js +1 -1
- package/src/codeversion.js +155 -0
- package/src/index.js +17 -0
- package/src/job.js +1 -1
- package/src/judged.js +1 -1
- package/src/ledger.js +2 -2
- package/src/planrun.js +140 -3
- package/src/promptregisters.js +93 -0
- package/src/readshim.js +39 -7
- package/src/replay.js +1212 -0
- package/src/reuse.js +24 -2
- package/src/root.js +1 -1
- package/src/run.js +29 -2
- package/src/selection.js +1 -1
- package/src/transport.js +71 -0
- package/types/behaviour.d.ts +46 -0
- package/types/codeversion.d.ts +18 -0
- package/types/index.d.ts +3 -0
- package/types/ledger.d.ts +14 -0
- package/types/promptregisters.d.ts +31 -0
- package/types/readshim.d.ts +17 -6
- package/types/replay.d.ts +195 -0
- package/types/reuse.d.ts +22 -0
- package/types/transport.d.ts +19 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// THE PROMPT REGISTER INVENTORY — build-list item 5 (PRD TODO #8), Q9 answered
|
|
2
|
+
// (hamr, 2026-08-25): "a check". A commit that changes a prompt register must
|
|
3
|
+
// say, in its message, the failure that caused the change, what it addresses,
|
|
4
|
+
// and what it corrects — doctrine already stated once ("a frozen rule without
|
|
5
|
+
// a wired detector is prose", docs/product/2026-08-23-agreed-build-list.md:187-189)
|
|
6
|
+
// so convention-only was rejected. This module is the ONE place that inventory
|
|
7
|
+
// lives: `scripts/prompt-commit-check.mjs` reads it to decide which commits the
|
|
8
|
+
// rule applies to, and this file's own doc-comment is where a human reads it.
|
|
9
|
+
//
|
|
10
|
+
// The inventory is FILE-scoped, not const-scoped. Several of the files below
|
|
11
|
+
// hold more than one named prompt const, and at least two (authorscout.js,
|
|
12
|
+
// cardauthor.js) also build a model-facing prompt INLINE inside a function
|
|
13
|
+
// (template literals, not a top-level const) rather than assembling one from a
|
|
14
|
+
// named export. A const-scoped check would silently miss those; a file-scoped
|
|
15
|
+
// one does not, because the whole file is in scope once it appears here. The
|
|
16
|
+
// `name` field on each entry is documentation only — grepped to the byte range
|
|
17
|
+
// that motivated it — and carries no weight in `isPromptFile`.
|
|
18
|
+
//
|
|
19
|
+
// Verified against source 2026-08-25 (grepped for exported/module-level string
|
|
20
|
+
// consts that read as model-facing instruction text, and separately for inline
|
|
21
|
+
// template-literal prompts built inside a function). Files checked and found to
|
|
22
|
+
// carry NO prompt content: src/plan.js, src/validate.js, src/kinds.js,
|
|
23
|
+
// src/text.js, src/declaredclose.js, src/calibrate.js, src/index.js (the last
|
|
24
|
+
// is the barrel export only).
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* One entry in the prompt-register inventory: a file known to carry
|
|
28
|
+
* model-facing prompt/instruction text, and the name(s) that live there for a
|
|
29
|
+
* human reading this file. `name` may point at a named const OR at a function
|
|
30
|
+
* that builds a prompt inline (e.g. `scoutPrompt()`) — the check only ever
|
|
31
|
+
* keys on `file`.
|
|
32
|
+
* @typedef {{file: string, name: string}} PromptRegisterEntry
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/** @type {ReadonlyArray<PromptRegisterEntry>} */
|
|
36
|
+
export const PROMPT_REGISTERS = Object.freeze([
|
|
37
|
+
// src/authorscout.js — the read-only repo-survey scout that opens authoring
|
|
38
|
+
Object.freeze({ file: 'src/authorscout.js', name: 'SCOUT_SYSTEM' }),
|
|
39
|
+
Object.freeze({ file: 'src/authorscout.js', name: 'SCOUT_RECOVERY_PROMPT' }),
|
|
40
|
+
Object.freeze({ file: 'src/authorscout.js', name: 'scoutPrompt() (inline template, not a const)' }),
|
|
41
|
+
|
|
42
|
+
// src/authorflow.js — the declaration-authoring turn (system + revise/re-ask)
|
|
43
|
+
Object.freeze({ file: 'src/authorflow.js', name: 'AUTHOR_SYSTEM' }),
|
|
44
|
+
Object.freeze({ file: 'src/authorflow.js', name: 'REVISE_INSTRUCTION' }),
|
|
45
|
+
Object.freeze({ file: 'src/authorflow.js', name: 'STRUCTURE_INSTRUCTION_TOOL' }),
|
|
46
|
+
Object.freeze({ file: 'src/authorflow.js', name: 'STRUCTURE_INSTRUCTION_TEXT' }),
|
|
47
|
+
|
|
48
|
+
// src/judged.js — the softgreen judge's locate prompt (spine + per-rule asks)
|
|
49
|
+
Object.freeze({ file: 'src/judged.js', name: 'PROMPT_HEAD' }),
|
|
50
|
+
Object.freeze({ file: 'src/judged.js', name: 'PROMPT_TAIL' }),
|
|
51
|
+
Object.freeze({ file: 'src/judged.js', name: 'JUDGE_RULES[*].ask (per-rule fragments)' }),
|
|
52
|
+
Object.freeze({ file: 'src/judged.js', name: 'locatePrompt() (inline template, not a const)' }),
|
|
53
|
+
|
|
54
|
+
// src/cardauthor.js — compiles a person's own words into a judged rubric
|
|
55
|
+
Object.freeze({ file: 'src/cardauthor.js', name: 'COMPILE_SYSTEM' }),
|
|
56
|
+
Object.freeze({ file: 'src/cardauthor.js', name: 'cardCasesPrompt() (inline template, not a const)' }),
|
|
57
|
+
|
|
58
|
+
// src/readshim.js — the capped/deduplicating read shim's worker-facing strategy
|
|
59
|
+
Object.freeze({ file: 'src/readshim.js', name: 'READ_SHIM_STRATEGY' }),
|
|
60
|
+
Object.freeze({ file: 'src/readshim.js', name: 'READ_SHIM_DIFF_STRATEGY' }),
|
|
61
|
+
|
|
62
|
+
// src/tools.js — the worker tool menu: personas, retrieval/edit/component strategy
|
|
63
|
+
Object.freeze({ file: 'src/tools.js', name: 'PERSONA_TOOLS' }),
|
|
64
|
+
Object.freeze({ file: 'src/tools.js', name: 'RETRIEVAL_STRATEGY' }),
|
|
65
|
+
Object.freeze({ file: 'src/tools.js', name: 'EDIT_STRATEGY' }),
|
|
66
|
+
Object.freeze({ file: 'src/tools.js', name: 'COMPONENT_STRATEGIES' }),
|
|
67
|
+
Object.freeze({ file: 'src/tools.js', name: 'strategyFor() (assembles per-grant strategy prose)' }),
|
|
68
|
+
Object.freeze({ file: 'src/tools.js', name: 'LINE_SPACE (module-private, not exported)' }),
|
|
69
|
+
Object.freeze({ file: 'src/tools.js', name: 'PERSONA (module-private, not exported, currently unreferenced)' }),
|
|
70
|
+
|
|
71
|
+
// src/planrun.js — the native-surface read-truncation strategy line
|
|
72
|
+
Object.freeze({ file: 'src/planrun.js', name: 'NATIVE_READ_STRATEGY' }),
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
/** The distinct file paths carrying prompt registers, repo-root-relative with
|
|
76
|
+
* forward slashes — derived from `PROMPT_REGISTERS` so the set can never drift
|
|
77
|
+
* from the entries above. @type {ReadonlySet<string>} */
|
|
78
|
+
const PROMPT_FILES = new Set(PROMPT_REGISTERS.map((e) => e.file));
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* True when `path` names one of the inventoried prompt-register files. Accepts
|
|
82
|
+
* either a repo-root-relative path (`src/tools.js`) or an absolute/relative
|
|
83
|
+
* path ending in one (`/home/x/bareloop/src/tools.js`, `./src/tools.js`) — git
|
|
84
|
+
* hands the check root-relative paths, but a caller working from an absolute
|
|
85
|
+
* path should not have to strip the prefix itself.
|
|
86
|
+
* @param {string} path
|
|
87
|
+
* @returns {boolean}
|
|
88
|
+
*/
|
|
89
|
+
export function isPromptFile(path) {
|
|
90
|
+
if (typeof path !== 'string' || path === '') return false;
|
|
91
|
+
const normalized = path.replace(/\\/g, '/');
|
|
92
|
+
return [...PROMPT_FILES].some((f) => normalized === f || normalized.endsWith(`/${f}`));
|
|
93
|
+
}
|
package/src/readshim.js
CHANGED
|
@@ -332,13 +332,18 @@ const stalePastEofNotice = (path, startLine, endLine, lines, detail) =>
|
|
|
332
332
|
* reference to the ledger — see its own note. Both live in one object because
|
|
333
333
|
* they are one worker's shim: the read seam and the retrieval seam the cap
|
|
334
334
|
* steers a capped worker towards.
|
|
335
|
-
* @param {{cap?: number, arm?: ReadShimArm
|
|
336
|
-
* shipped value is `READ_SHIM_CAP`; the option
|
|
337
|
-
* two bounds to one number rather than two
|
|
338
|
-
* levers are live (`readShimArm`). Defaults to
|
|
339
|
-
* before the arms existed keeps the behaviour it
|
|
335
|
+
* @param {{cap?: number, arm?: ReadShimArm, onCount?: ((kind: 'pointered'|'capped', bytesWithheld: number) => void)|null}} [opts]
|
|
336
|
+
* `cap`: delivery bound in bytes (the shipped value is `READ_SHIM_CAP`; the option
|
|
337
|
+
* exists so the native path can pin the two bounds to one number rather than two
|
|
338
|
+
* constants drifting apart). `arm`: which levers are live (`readShimArm`). Defaults to
|
|
339
|
+
* ALL of them, so every caller written before the arms existed keeps the behaviour it
|
|
340
|
+
* was written against. `onCount`: MEMORY-CACHE — fired once per pointer/capped-slice
|
|
341
|
+
* response with exactly the bytes withheld on THAT response, for a caller (e.g.
|
|
342
|
+
* `runPlan`) that sums several workers' shims into one run-level readout. Never called
|
|
343
|
+
* for a diff response (L2 is its own lever, uncounted here) or a first read under the
|
|
344
|
+
* cap (nothing was withheld).
|
|
340
345
|
*/
|
|
341
|
-
export function createReadShim({ cap = READ_SHIM_CAP, arm = ARM_ALL } = {}) {
|
|
346
|
+
export function createReadShim({ cap = READ_SHIM_CAP, arm = ARM_ALL, onCount = null } = {}) {
|
|
342
347
|
/** path → what the worker has been handed of the CURRENT content. `full` is
|
|
343
348
|
* the delivered text itself, kept ONLY while the worker holds the whole file
|
|
344
349
|
* (null otherwise) because that is the only state a diff is legal from.
|
|
@@ -354,6 +359,22 @@ export function createReadShim({ cap = READ_SHIM_CAP, arm = ARM_ALL } = {}) {
|
|
|
354
359
|
* @type {Map<string, {hash: string, total: number, delivered: number, full: string|null}>} */
|
|
355
360
|
const ledger = new Map();
|
|
356
361
|
|
|
362
|
+
// MEMORY-CACHE counters (this worker's own ledger, summed by a caller across
|
|
363
|
+
// workers for a run-level readout — see `onCount` above). Counted only where
|
|
364
|
+
// it is EXACT, never estimated: a pointer withholds exactly `total` bytes (the
|
|
365
|
+
// whole delivery it replaces) and a capped slice withholds exactly `total - end`
|
|
366
|
+
// (the tail this call did not send) — both numbers the ledger already computes
|
|
367
|
+
// for its own bookkeeping below, read here rather than re-derived.
|
|
368
|
+
let pointered = 0;
|
|
369
|
+
let capped = 0;
|
|
370
|
+
let bytesWithheld = 0;
|
|
371
|
+
/** @param {'pointered'|'capped'} kind @param {number} bytes */
|
|
372
|
+
const count = (kind, bytes) => {
|
|
373
|
+
if (kind === 'pointered') pointered++; else capped++;
|
|
374
|
+
bytesWithheld += bytes;
|
|
375
|
+
if (onCount) onCount(kind, bytes);
|
|
376
|
+
};
|
|
377
|
+
|
|
357
378
|
/**
|
|
358
379
|
* THE STALE-POINTER SERVE. When `ctx_get` throws `StalePointerError` — the file
|
|
359
380
|
* changed on disk after indexing — hand back the REQUESTED LINE RANGE read
|
|
@@ -510,6 +531,9 @@ export function createReadShim({ cap = READ_SHIM_CAP, arm = ARM_ALL } = {}) {
|
|
|
510
531
|
// slice path and is delivered (as nothing) truthfully.
|
|
511
532
|
if (arm.pointer && seen && seen.hash === hash && start >= total) {
|
|
512
533
|
ledger.set(path, { hash, total, delivered: total, full: r });
|
|
534
|
+
// Withheld = the whole delivery this pointer replaces: `total` bytes the
|
|
535
|
+
// worker asked to be re-sent and was not, because it already holds them.
|
|
536
|
+
count('pointered', total);
|
|
513
537
|
return pointer(path, total);
|
|
514
538
|
}
|
|
515
539
|
ledger.set(path, { hash, total, delivered: end, full: end === total ? r : null });
|
|
@@ -518,12 +542,20 @@ export function createReadShim({ cap = READ_SHIM_CAP, arm = ARM_ALL } = {}) {
|
|
|
518
542
|
// no notice, no reframing. That is the overwhelming majority of reads, and
|
|
519
543
|
// it keeps the shim invisible where it buys nothing.
|
|
520
544
|
if (start === 0 && end === total) return slice;
|
|
545
|
+
// Withheld = the tail beyond this slice (`total - end`), not the slice
|
|
546
|
+
// itself — the slice IS what was sent; the remainder is what was not.
|
|
547
|
+
if (end < total) count('capped', total - end);
|
|
521
548
|
return slice + (end < total ? capNotice(start, end, total) : doneNotice(start, total));
|
|
522
549
|
};
|
|
523
550
|
return tool;
|
|
524
551
|
};
|
|
525
552
|
|
|
526
|
-
|
|
553
|
+
/** MEMORY-CACHE: this worker's own counts, exact — never re-derived, never
|
|
554
|
+
* estimated. `bytesWithheld` sums only the `pointered`/`capped` cases above;
|
|
555
|
+
* the diff lever (L2) is a different response shape and is not counted here. */
|
|
556
|
+
const summary = () => ({ pointered, capped, bytesWithheld });
|
|
557
|
+
|
|
558
|
+
return { arm, cap, wrapRead, serveStale, summary };
|
|
527
559
|
}
|
|
528
560
|
|
|
529
561
|
/**
|