bareloop 0.8.0 → 0.9.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/types/text.d.ts CHANGED
@@ -33,3 +33,87 @@ export function priceOf(r: any): {
33
33
  costUsd: number | null;
34
34
  unpricedRounds: number;
35
35
  };
36
+ /**
37
+ * ONE raw model emission, ready to persist.
38
+ *
39
+ * Three things happen here and NONE of them is optional at a call site:
40
+ * - the text goes through the ONE redactor (`redactSecrets`, over the ONE
41
+ * `SECRET_PATTERNS` inventory). A raw is the single most likely record in
42
+ * this system to carry a live credential: it is whatever a model read out
43
+ * of a repository, verbatim, and it lands in a file that outlives the run;
44
+ * - it is BOUNDED, and the bound ANNOUNCES itself with the full size (F28 —
45
+ * a bound eats detail, never evidence). The cut is decoded rather than
46
+ * sliced so a multi-byte character is never split in half;
47
+ * - it carries its own address: which call produced it (`label`, the same
48
+ * spelling the cost book meters under) and which attempt (`attempt`), plus
49
+ * the typed failure that describes it when there is one.
50
+ *
51
+ * `cause`/`reason` are `null` when the emission was fine — an ABSENT failure is
52
+ * absent, never an empty string that reads like a diagnosis nobody wrote.
53
+ * @param {{label: string, attempt: number, text: unknown, cause?: string|null,
54
+ * reason?: string|null, cap?: number}} o
55
+ * @returns {{label: string, attempt: number, bytes: number, trimmed: boolean,
56
+ * text: string, cause: string|null, reason: string|null}}
57
+ */
58
+ export function scrubRaw({ label, attempt, text, cause, reason, cap }: {
59
+ label: string;
60
+ attempt: number;
61
+ text: unknown;
62
+ cause?: string | null;
63
+ reason?: string | null;
64
+ cap?: number;
65
+ }): {
66
+ label: string;
67
+ attempt: number;
68
+ bytes: number;
69
+ trimmed: boolean;
70
+ text: string;
71
+ cause: string | null;
72
+ reason: string | null;
73
+ };
74
+ /**
75
+ * Stamp a persisted raw with the verdict that DESCRIBES it — through the same
76
+ * boundary the raw's own text rode.
77
+ *
78
+ * It exists because the verdict is not known when the raw is created: a writer
79
+ * records the emission the moment the call returns (so a metered call can never
80
+ * be missing its output), and only classifies it afterwards. The obvious
81
+ * shape — assigning `cause`/`reason` onto the stored object at that later
82
+ * point — writes an UNSCRUBBED string into a record that is already past the
83
+ * one redactor, and `reason` is the worst field to do it to: it quotes the
84
+ * transport's own error prose, which nobody in this repo wrote and nobody
85
+ * bounds. So the stamp is a function, not an assignment, and `scrubRaw` renders
86
+ * its own two fields through it: ONE place decides how a diagnosis becomes
87
+ * persistable, exactly as ONE place decides it for the text.
88
+ *
89
+ * `cause` is enumerated (`SURVEY_CAUSES`) and passes through untouched; a mask
90
+ * over a closed set would only hide a bug in the set.
91
+ * @template {{cause: string|null, reason: string|null}} T
92
+ * @param {T} raw @param {{cause?: string|null, reason?: string|null}} o @returns {T}
93
+ */
94
+ export function stampRaw<T extends {
95
+ cause: string | null;
96
+ reason: string | null;
97
+ }>(raw: T, { cause, reason }: {
98
+ cause?: string | null;
99
+ reason?: string | null;
100
+ }): T;
101
+ /**
102
+ * The ceiling on ONE persisted raw. Sized to the survey's own prompt-ingredient
103
+ * cap (`AUTHOR_SCOUT_BLOB_MAX`, 8000) rather than picked: the largest raw this
104
+ * repository produces is a survey, the archive's real surveys ran 5991-8056
105
+ * bytes, and a record that cannot hold one whole specimen cannot answer the
106
+ * question it exists for. The two constants are deliberately independent
107
+ * numbers — one bounds a PROMPT, this one bounds a RECORD.
108
+ */
109
+ export const RAW_PERSIST_MAX: 8000;
110
+ /**
111
+ * The announced-trim marker for a persisted raw. A NEW spelling rather than a
112
+ * reuse of `GAP_TRIM_MARKER` ('gap trimmed:'), and that is a decision, not
113
+ * decoration: `src/root.js` READS `GAP_TRIM_MARKER` to mean *this gap was cut,
114
+ * so Layer R must not compare it*, so a second producer of those bytes in a
115
+ * different population is exactly the F90 hazard — a reader must know every
116
+ * producer of its marker. Nothing reads this one yet; it exists so the trim
117
+ * announces itself (F28) and so a future reader has an unambiguous key.
118
+ */
119
+ export const RAW_TRIM_MARKER: "raw trimmed:";
@@ -0,0 +1,102 @@
1
+ /**
2
+ * The branch name a job's run works on, DERIVED from the signed spec.
3
+ *
4
+ * **The source is `spec.job`, and the choice is load-bearing.** It is the one
5
+ * human-authored, REQUIRED, kebab-case name the spec carries; it is inside the
6
+ * spec hash, so the branch name cannot drift from what the operator signed; and
7
+ * it is stable across a resume and across a cold re-run of the same spec, which
8
+ * is precisely why the collision walk below has to exist. In practice it already
9
+ * reads as tool-and-problem (`bareagent-u-types`, `litectx-u-types`), which is
10
+ * the shape hamr's ruling asks for.
11
+ *
12
+ * **The goal prose was REJECTED as a source**, and not for tidiness: a goal is
13
+ * long, is edited freely between runs without changing what the job IS, and
14
+ * opens on boilerplate ("An existing pytest test suite for the SOAR
15
+ * orchestrator…"). Deriving from it would make the branch name unstable under
16
+ * edits that change nothing, and unreadable when it was stable. A run id was
17
+ * rejected by the ruling itself.
18
+ *
19
+ * Deterministic, pure, and total: any input yields a name matching
20
+ * `WORK_BRANCH_RE`, so a caller never has to check.
21
+ * @param {any} spec a job-v1 spec (validated or raw)
22
+ * @returns {string}
23
+ */
24
+ export function workBranchName(spec: any): string;
25
+ /**
26
+ * Put the run on its OWN branch, before it can touch any code.
27
+ *
28
+ * The three shapes, and each one's honest answer:
29
+ *
30
+ * - **Cold.** `name` is free → create it at HEAD and switch. The handed branch
31
+ * keeps its ref and its history; a new branch at HEAD moves no commit, so this
32
+ * costs the patient nothing and changes nothing it can measure (the declared
33
+ * close's seed is HEAD either way).
34
+ * - **Cold, and the name is taken.** Suffix `-2`, `-3`… until one is free.
35
+ * Deterministic given the repository, and it NEVER reuses another run's
36
+ * branch — a second run of the same spec is a second blast radius.
37
+ * - **Resume.** `resume` names the branch the killed leg recorded on its own
38
+ * spine. It is checked out, never re-created and never suffixed: the work the
39
+ * operator already paid for is sitting on it, and minting `-2` beside it would
40
+ * strand exactly the progress a resume exists to keep. A `resume` branch that
41
+ * is GONE is a STOP, never a silent fresh start — the run would be grading a
42
+ * tree whose history it does not own.
43
+ *
44
+ * **Every failure is a stop with a name.** Not a git repository, no git on PATH,
45
+ * a checkout git refuses, a crowded namespace, a resume branch a human deleted:
46
+ * each comes back as `stop` and `branch: null`. There is no fallback to "just
47
+ * work on whatever was handed to us" anywhere in this function, because that
48
+ * fallback IS the thing the hard rule forbids.
49
+ *
50
+ * A DETACHED head is not a failure. Creating a branch from it is exactly the
51
+ * bounding the rule asks for (and it attaches a head that was floating), so it
52
+ * succeeds with `from: null` — there was no branch to be handed, and the record
53
+ * says so rather than inventing one.
54
+ *
55
+ * @param {string} workdir the patient (the fence's root)
56
+ * @param {object} o
57
+ * @param {string} o.name the derived name (`workBranchName`); refused unless it
58
+ * matches `WORK_BRANCH_RE`, which is what makes `main` unreachable even from a
59
+ * hand-passed argument
60
+ * @param {string|null} [o.resume] RESUME — the branch this run already owns
61
+ * @returns {Promise<{stop: string, branch: null}
62
+ * | {stop: null, branch: string, created: boolean, resumed: boolean, from: string|null, base: string|null,
63
+ * repo: string, collided: number}>}
64
+ */
65
+ export function prepareWorkBranch(workdir: string, { name, resume }: {
66
+ name: string;
67
+ resume?: string | null | undefined;
68
+ }): Promise<{
69
+ stop: string;
70
+ branch: null;
71
+ } | {
72
+ stop: null;
73
+ branch: string;
74
+ created: boolean;
75
+ resumed: boolean;
76
+ from: string | null;
77
+ base: string | null;
78
+ repo: string;
79
+ collided: number;
80
+ }>;
81
+ /** Every work branch carries it. It is what makes `main`, `master` and every
82
+ * branch a human could plausibly have handed the run INEXPRESSIBLE as a work
83
+ * branch name — a structural guarantee rather than a denylist somebody has to
84
+ * keep current. */
85
+ export const WORK_BRANCH_PREFIX: "bareloop-";
86
+ /** The full legal shape of a work branch. Deliberately narrower than git's own
87
+ * `check-ref-format`: the kebab alphabet the job validator already holds every
88
+ * spec slug to (`src/job.js` SLUG_RE), plus the prefix. Everything git forbids —
89
+ * `..`, `@{`, a trailing `.lock`, a leading `-`, a `/` component, control
90
+ * characters — is unreachable inside it, so ref-safety is a property of the
91
+ * alphabet and never a sanitiser somebody has to remember to call. */
92
+ export const WORK_BRANCH_RE: RegExp;
93
+ /** The slug's ceiling, before any collision suffix. A ref component has a
94
+ * filesystem-length limit and a human scanning `git branch` has a much smaller
95
+ * one; 60 characters is longer than every job slug in `jobs/` and short enough
96
+ * to read in a column. */
97
+ export const MAX_SLUG_LEN: 60;
98
+ /** How far the collision walk goes before it STOPS and says so. A run that
99
+ * cannot find a free name in this many attempts is looking at a patient nobody
100
+ * has swept in a very long time, and silently walking to `-500` would be the
101
+ * runner papering over an operator problem. */
102
+ export const MAX_BRANCH_COLLISIONS: 50;