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/CHANGELOG.md +421 -0
- package/README.md +10 -3
- package/bareloop.context.md +198 -17
- package/package.json +1 -1
- package/src/authorflow.js +1280 -0
- package/src/authoring.js +1296 -0
- package/src/authorjob.js +793 -0
- package/src/authorscout.js +706 -0
- package/src/bridges.js +2 -2
- package/src/declaredclose.js +519 -0
- package/src/index.js +72 -0
- package/src/job.js +48 -4
- package/src/kinds.js +1354 -0
- package/src/ledger.js +13 -0
- package/src/plan.js +26 -1
- package/src/planrun.js +239 -16
- package/src/ralph.js +19 -4
- package/src/reuse.js +68 -7
- package/src/root.js +8 -1
- package/src/run.js +10 -3
- package/src/text.js +120 -0
- package/src/workbranch.js +261 -0
- package/types/authorflow.d.ts +720 -0
- package/types/authoring.d.ts +447 -0
- package/types/authorjob.d.ts +241 -0
- package/types/authorscout.d.ts +307 -0
- package/types/declaredclose.d.ts +174 -0
- package/types/index.d.ts +9 -1
- package/types/job.d.ts +1 -0
- package/types/kinds.d.ts +343 -0
- package/types/plan.d.ts +18 -0
- package/types/planrun.d.ts +10 -3
- package/types/ralph.d.ts +1 -0
- package/types/run.d.ts +10 -2
- package/types/text.d.ts +84 -0
- package/types/workbranch.d.ts +102 -0
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;
|