zelari-code 2.13.0 → 2.14.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/dist/cli/headless/policyGate.js +101 -0
- package/dist/cli/headless/policyGate.js.map +1 -0
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/kraken/completionProof.js +89 -41
- package/dist/cli/kraken/completionProof.js.map +1 -1
- package/dist/cli/kraken/completionProofAttestation.js +157 -0
- package/dist/cli/kraken/completionProofAttestation.js.map +1 -0
- package/dist/cli/kraken/completionProofPersist.js +219 -0
- package/dist/cli/kraken/completionProofPersist.js.map +1 -0
- package/dist/cli/kraken/completionProofProbe.js +135 -0
- package/dist/cli/kraken/completionProofProbe.js.map +1 -0
- package/dist/cli/kraken/contractCompiler.js +142 -0
- package/dist/cli/kraken/contractCompiler.js.map +1 -0
- package/dist/cli/kraken/delegationPolicy.js +40 -3
- package/dist/cli/kraken/delegationPolicy.js.map +1 -1
- package/dist/cli/kraken/executor.js +462 -9
- package/dist/cli/kraken/executor.js.map +1 -1
- package/dist/cli/kraken/fileOwnership.js +190 -0
- package/dist/cli/kraken/fileOwnership.js.map +1 -0
- package/dist/cli/kraken/modelReputation.js +208 -0
- package/dist/cli/kraken/modelReputation.js.map +1 -0
- package/dist/cli/kraken/nativeVerification.js +53 -11
- package/dist/cli/kraken/nativeVerification.js.map +1 -1
- package/dist/cli/kraken/planner.js +2 -0
- package/dist/cli/kraken/planner.js.map +1 -1
- package/dist/cli/kraken/reputationStore.js +139 -0
- package/dist/cli/kraken/reputationStore.js.map +1 -0
- package/dist/cli/kraken/semanticOwnership.js +184 -0
- package/dist/cli/kraken/semanticOwnership.js.map +1 -0
- package/dist/cli/kraken/spawnRoi.js +209 -0
- package/dist/cli/kraken/spawnRoi.js.map +1 -0
- package/dist/cli/kraken/transactional.js +104 -0
- package/dist/cli/kraken/transactional.js.map +1 -0
- package/dist/cli/kraken/verificationAdapters/dotnet.js +49 -0
- package/dist/cli/kraken/verificationAdapters/dotnet.js.map +1 -0
- package/dist/cli/kraken/verificationAdapters/go.js +36 -0
- package/dist/cli/kraken/verificationAdapters/go.js.map +1 -0
- package/dist/cli/kraken/verificationAdapters/index.js +47 -0
- package/dist/cli/kraken/verificationAdapters/index.js.map +1 -0
- package/dist/cli/kraken/verificationAdapters/java.js +102 -0
- package/dist/cli/kraken/verificationAdapters/java.js.map +1 -0
- package/dist/cli/kraken/verificationAdapters/node.js +90 -0
- package/dist/cli/kraken/verificationAdapters/node.js.map +1 -0
- package/dist/cli/kraken/verificationAdapters/python.js +86 -0
- package/dist/cli/kraken/verificationAdapters/python.js.map +1 -0
- package/dist/cli/kraken/verificationAdapters/rust.js +39 -0
- package/dist/cli/kraken/verificationAdapters/rust.js.map +1 -0
- package/dist/cli/kraken/verificationAdapters/types.js +2 -0
- package/dist/cli/kraken/verificationAdapters/types.js.map +1 -0
- package/dist/cli/kraken/verificationBridge.js +40 -3
- package/dist/cli/kraken/verificationBridge.js.map +1 -1
- package/dist/cli/kraken/verifierLifecycle.js +51 -36
- package/dist/cli/kraken/verifierLifecycle.js.map +1 -1
- package/dist/cli/kraken/verifierRouting.js +264 -0
- package/dist/cli/kraken/verifierRouting.js.map +1 -0
- package/dist/cli/kraken/worktreeScheduling.js +201 -0
- package/dist/cli/kraken/worktreeScheduling.js.map +1 -0
- package/dist/cli/main.bundled.js +3880 -889
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/orchestration/facts.js +127 -0
- package/dist/cli/orchestration/facts.js.map +1 -0
- package/dist/cli/orchestration/policy.js +128 -75
- package/dist/cli/orchestration/policy.js.map +1 -1
- package/dist/cli/orchestration/signals.js +96 -0
- package/dist/cli/orchestration/signals.js.map +1 -0
- package/dist/cli/runHeadless.js +92 -19
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/safety/policyEngine.js +159 -20
- package/dist/cli/safety/policyEngine.js.map +1 -1
- package/dist/cli/safety/policyLoadMode.js +59 -0
- package/dist/cli/safety/policyLoadMode.js.map +1 -0
- package/dist/cli/safety/resourceClaims.js +373 -0
- package/dist/cli/safety/resourceClaims.js.map +1 -0
- package/dist/cli/safety/sandboxPath.js +160 -17
- package/dist/cli/safety/sandboxPath.js.map +1 -1
- package/dist/cli/toolRegistry.js +98 -8
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/execProcess.js +121 -0
- package/dist/cli/tools/execProcess.js.map +1 -0
- package/dist/cli/tools/krakenRadio.js +9 -0
- package/dist/cli/tools/krakenRadio.js.map +1 -1
- package/dist/cli/tools/taskTool.js +8 -3
- package/dist/cli/tools/taskTool.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verifierRouting — t21 (§P1.D × PW §10): risk-based cross-family verifier
|
|
3
|
+
* routing on top of the P0.6 cross-model default.
|
|
4
|
+
*
|
|
5
|
+
* low ⇒ reviewer OFF — deterministic verification only (strict gate).
|
|
6
|
+
* medium ⇒ 1 reviewer, different family, ECONOMICAL (cheapest model
|
|
7
|
+
* within that family; no pricing API exists, so the established
|
|
8
|
+
* cheap heuristic from krakenModel.isCheapModelId/pickCheapModel
|
|
9
|
+
* is the "cheap" signal; unresolvable ⇒ first different-family
|
|
10
|
+
* candidate = the current P0.6 pick).
|
|
11
|
+
* high ⇒ 1 reviewer, different family, STRONGEST available (same
|
|
12
|
+
* situation: prefer non-cheap ids within that family, else the
|
|
13
|
+
* P0.6 first different-family candidate).
|
|
14
|
+
* critical ⇒ 2 reviewers from two DIFFERENT families (neither = builder),
|
|
15
|
+
* merged PESSIMISTICALLY; reviewer disagreement surfaces as a
|
|
16
|
+
* `verifier-divergence` evidence item in the proof (PW §10).
|
|
17
|
+
*
|
|
18
|
+
* Risk resolution precedence: ZELARI_VERIFY_RISK env (host override) >
|
|
19
|
+
* active TaskContract (t22 seam, setActiveContractScope) > 'medium'.
|
|
20
|
+
*
|
|
21
|
+
* ADVISORY-ONLY invariant is untouched: whatever this module routes can never
|
|
22
|
+
* rewrite the deterministic gate verdict — reviewers inform, they do not
|
|
23
|
+
* decide (ADR-0026 / CompletionPolicy locks stay the only authority).
|
|
24
|
+
*/
|
|
25
|
+
import { basename } from 'node:path';
|
|
26
|
+
import { TASK_RISKS } from '@zelari/core';
|
|
27
|
+
import { inferModelFamily, isCheapModelId, pickCheapModel, pickDifferentFamily, resolveCrossModelVerifier, } from '../tools/krakenModel.js';
|
|
28
|
+
// t22 seam (required): the active contract of THIS turn carries its risk.
|
|
29
|
+
import { activeContractScope } from './contractCompiler.js';
|
|
30
|
+
// t29 (§16): passive reputation consult — records come from the injected
|
|
31
|
+
// store snapshot (reputationStore.ts); null/empty source keeps the t21
|
|
32
|
+
// behavior byte-identical. Family constraints below are NOT weakened: the
|
|
33
|
+
// reputation pick only reorders WITHIN an already family-filtered pool.
|
|
34
|
+
import { reputationFamilyPick } from './modelReputation.js';
|
|
35
|
+
// t29: runtime reputation source. The host (graph executor) loads the
|
|
36
|
+
// per-repo JSONL store and injects a snapshot; tests inject fixtures and
|
|
37
|
+
// reset with setReputationSource(null).
|
|
38
|
+
let reputationSource = null;
|
|
39
|
+
/** Inject (or clear, with null) the reputation records routing consults. */
|
|
40
|
+
export function setReputationSource(records) {
|
|
41
|
+
reputationSource = records;
|
|
42
|
+
}
|
|
43
|
+
/** Host override switch: wins over the task-contract risk declaration. */
|
|
44
|
+
export const VERIFY_RISK_ENV = 'ZELARI_VERIFY_RISK';
|
|
45
|
+
/**
|
|
46
|
+
* Active risk for the verify pass: `ZELARI_VERIFY_RISK` wins over the ACTIVE
|
|
47
|
+
* TaskContract (`contractCompiler.setActiveContractScope`, t22); absent both
|
|
48
|
+
* ⇒ 'medium' (the historical single-reviewer behavior). Invalid env values
|
|
49
|
+
* are ignored so a typo cannot silently downgrade enforcement.
|
|
50
|
+
*/
|
|
51
|
+
export function activeRisk(env = process.env) {
|
|
52
|
+
const raw = env[VERIFY_RISK_ENV]?.trim().toLowerCase();
|
|
53
|
+
if (raw && TASK_RISKS.includes(raw))
|
|
54
|
+
return raw;
|
|
55
|
+
return activeContractScope()?.contract.risk ?? 'medium';
|
|
56
|
+
}
|
|
57
|
+
/** Candidate model ids inside one family bucket, stable first-seen order. */
|
|
58
|
+
function idsInFamily(family, candidates) {
|
|
59
|
+
const ids = [];
|
|
60
|
+
for (const c of candidates) {
|
|
61
|
+
if (!c.provider || !c.model)
|
|
62
|
+
continue;
|
|
63
|
+
if (inferModelFamily(c.provider, c.model) === family && !ids.includes(c.model)) {
|
|
64
|
+
ids.push(c.model);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return ids;
|
|
68
|
+
}
|
|
69
|
+
/** t29: family-pool model ids → rankable candidate objects (real provider when known). */
|
|
70
|
+
function poolCandidates(pool, candidates, fallbackProvider) {
|
|
71
|
+
return pool.map((m) => candidates.find((c) => c.model === m) ?? { provider: fallbackProvider, model: m });
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Pick within an already-selected family:
|
|
75
|
+
* - 'cheap': krakenModel.pickCheapModel (the repo's only cheap signal);
|
|
76
|
+
* - 'strong': first id NOT matching the cheap heuristic (no pricing API and
|
|
77
|
+
* no exported strong registry exists — documented per t21 brief; degrades
|
|
78
|
+
* to the family's stable first candidate when everything looks cheap).
|
|
79
|
+
*/
|
|
80
|
+
function pickWithinFamily(family, builderModel, ids, prefer) {
|
|
81
|
+
if (ids.length === 0)
|
|
82
|
+
return builderModel;
|
|
83
|
+
if (prefer === 'cheap') {
|
|
84
|
+
return pickCheapModel(builderModel, ids) ?? ids[0];
|
|
85
|
+
}
|
|
86
|
+
const strong = ids.filter((id) => !isCheapModelId(id));
|
|
87
|
+
return strong[0] ?? ids[0];
|
|
88
|
+
}
|
|
89
|
+
/** Critical fallback: second reviewer from another family than A's/builder. */
|
|
90
|
+
function secondCrossPick(builder, firstFamily, candidates, firstIdentity) {
|
|
91
|
+
const remaining = candidates.filter((c) => inferModelFamily(c.provider, c.model) !== firstFamily);
|
|
92
|
+
const alt = pickDifferentFamilyIdentity(builder, remaining, firstIdentity);
|
|
93
|
+
return alt;
|
|
94
|
+
}
|
|
95
|
+
function pickDifferentFamilyIdentity(builder, candidates, avoidIdentity) {
|
|
96
|
+
const picked = pickDifferentFamily(builder, candidates);
|
|
97
|
+
if (!picked || picked.provider === '')
|
|
98
|
+
return null;
|
|
99
|
+
if (avoidIdentity &&
|
|
100
|
+
picked.provider === avoidIdentity.provider &&
|
|
101
|
+
picked.model === avoidIdentity.model) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
return { identity: picked, family: inferModelFamily(picked.provider, picked.model) };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Resolve the reviewer set for one advisory verify pass.
|
|
108
|
+
*
|
|
109
|
+
* Precedence mirrors the pre-t21 P0.6 resolution path: a FIXED dedicated
|
|
110
|
+
* selection IS the user-configured reviewer (kept verbatim; critical cannot
|
|
111
|
+
* conjure independence around it and degrades to a single review). Inherit
|
|
112
|
+
* mode reuses resolveCrossModelVerifier for the primary so ZELARI_KRAKEN_*
|
|
113
|
+
* semantics stay identical at every risk; strength/economy refinement applies
|
|
114
|
+
* ONLY to a successful cross-family pick — same-family fallback keeps the
|
|
115
|
+
* session identity verbatim (P0.6 parity, never a worse model than today).
|
|
116
|
+
*
|
|
117
|
+
* Degrade-and-continue: missing/incomplete candidates reduce the reviewer
|
|
118
|
+
* count (encoded in rationaleCode), never throw and never fabricate one.
|
|
119
|
+
*/
|
|
120
|
+
export function resolveVerifierRouting(builderIdentity, risk, opts = {}) {
|
|
121
|
+
if (risk === 'low') {
|
|
122
|
+
// PW §10: reviewer OFF — deterministic checks are the whole verdict basis.
|
|
123
|
+
return { risk, rationaleCode: 'reviewer-off-low-risk-deterministic-only', reviewers: [] };
|
|
124
|
+
}
|
|
125
|
+
const base = opts.selectionMode === 'fixed'
|
|
126
|
+
? builderIdentity
|
|
127
|
+
: (opts.session ?? builderIdentity);
|
|
128
|
+
if (!base || !base.provider || !base.model) {
|
|
129
|
+
return { risk, rationaleCode: 'no-verifiable-identity', reviewers: [] };
|
|
130
|
+
}
|
|
131
|
+
if (opts.selectionMode === 'fixed') {
|
|
132
|
+
return {
|
|
133
|
+
risk,
|
|
134
|
+
rationaleCode: risk === 'critical' ? 'critical-fixed-single-reviewer' : 'fixed-dedicated-verifier',
|
|
135
|
+
reviewers: [{ identity: base, family: inferModelFamily(base.provider, base.model), role: 'strongest' }],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const candidates = (opts.familyCandidates ?? []).filter((c) => c.provider && c.model);
|
|
139
|
+
const builderFamily = inferModelFamily(base.provider, base.model);
|
|
140
|
+
const cross = resolveCrossModelVerifier(base, candidates, opts.env ?? process.env);
|
|
141
|
+
if (risk !== 'critical') {
|
|
142
|
+
if (cross && inferModelFamily(cross.provider, cross.model) !== builderFamily) {
|
|
143
|
+
const family = inferModelFamily(cross.provider, cross.model);
|
|
144
|
+
const pool = [...new Set([cross.model, ...idsInFamily(family, candidates)])];
|
|
145
|
+
const prefer = risk === 'medium' ? 'cheap' : 'strong';
|
|
146
|
+
// t29: when a trusted reputation sample exists for this (repo, verify)
|
|
147
|
+
// bucket, the pool's top-ranked identity wins — same family constraint,
|
|
148
|
+
// better in-family choice; otherwise the cheap/strong heuristic stands.
|
|
149
|
+
const repPick = reputationFamilyPick(reputationSource, poolCandidates(pool, candidates, cross.provider), opts.repo ?? basename(process.cwd()));
|
|
150
|
+
const identity = repPick ?? {
|
|
151
|
+
provider: cross.provider,
|
|
152
|
+
model: pickWithinFamily(family, cross.model, pool, prefer),
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
risk,
|
|
156
|
+
rationaleCode: repPick
|
|
157
|
+
? `cross-family-${risk === 'medium' ? 'economical' : 'strongest-available'}-reputation`
|
|
158
|
+
: risk === 'medium'
|
|
159
|
+
? 'cross-family-economical'
|
|
160
|
+
: 'cross-family-strongest-available',
|
|
161
|
+
reviewers: [{ identity, family, role: risk === 'medium' ? 'economical' : 'strongest' }],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
// No usable different-family candidate: keep the session identity verbatim.
|
|
165
|
+
return {
|
|
166
|
+
risk,
|
|
167
|
+
rationaleCode: risk === 'medium' ? 'same-family-fallback-medium' : 'same-family-fallback-high',
|
|
168
|
+
reviewers: [{ identity: base, family: builderFamily, role: risk === 'medium' ? 'economical' : 'strongest' }],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
// critical: two independent families. First choice = strongest within the
|
|
172
|
+
// primary cross family (or session fallback when none exists).
|
|
173
|
+
let firstReviewer;
|
|
174
|
+
if (cross && inferModelFamily(cross.provider, cross.model) !== builderFamily) {
|
|
175
|
+
const family = inferModelFamily(cross.provider, cross.model);
|
|
176
|
+
const pool = [...new Set([cross.model, ...idsInFamily(family, candidates)])];
|
|
177
|
+
firstReviewer = {
|
|
178
|
+
// t29: reputation-refined within the primary family (constraint intact);
|
|
179
|
+
// falls back to the strongest heuristic when no trusted sample exists.
|
|
180
|
+
identity: reputationFamilyPick(reputationSource, poolCandidates(pool, candidates, cross.provider), opts.repo ?? basename(process.cwd())) ?? {
|
|
181
|
+
provider: cross.provider,
|
|
182
|
+
model: pickWithinFamily(family, cross.model, pool, 'strong'),
|
|
183
|
+
},
|
|
184
|
+
family,
|
|
185
|
+
role: 'strongest',
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
firstReviewer = { identity: base, family: builderFamily, role: 'second-opinion' };
|
|
190
|
+
}
|
|
191
|
+
if (firstReviewer.family === builderFamily) {
|
|
192
|
+
// Not even one different-family candidate: single degraded review.
|
|
193
|
+
return { risk, rationaleCode: 'critical-single-same-family-degraded', reviewers: [firstReviewer] };
|
|
194
|
+
}
|
|
195
|
+
let second = secondCrossPick(base, firstReviewer.family, candidates, firstReviewer.identity);
|
|
196
|
+
let code = 'critical-dual-cross-family-pessimistic-merge';
|
|
197
|
+
if (!second) {
|
|
198
|
+
// Only one other family: take a DIFFERENT model within it (weaker but
|
|
199
|
+
// still a distinct judge); nothing left ⇒ drop the second reviewer.
|
|
200
|
+
const sameFamilyAlt = candidates.find((c) => inferModelFamily(c.provider, c.model) === firstReviewer.family &&
|
|
201
|
+
!(c.provider === firstReviewer.identity.provider && c.model === firstReviewer.identity.model));
|
|
202
|
+
second = sameFamilyAlt
|
|
203
|
+
? { identity: { provider: sameFamilyAlt.provider, model: sameFamilyAlt.model }, family: firstReviewer.family }
|
|
204
|
+
: null;
|
|
205
|
+
if (second)
|
|
206
|
+
code = 'critical-second-reviewer-same-family';
|
|
207
|
+
else
|
|
208
|
+
return { risk, rationaleCode: 'critical-single-cross-family-degraded', reviewers: [firstReviewer] };
|
|
209
|
+
}
|
|
210
|
+
const secondRole = second.family === firstReviewer.family ? firstReviewer.role : 'second-opinion';
|
|
211
|
+
return {
|
|
212
|
+
risk,
|
|
213
|
+
rationaleCode: code,
|
|
214
|
+
reviewers: [firstReviewer, { ...second, role: secondRole }],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/** Pessimistic merge order: any blocker/fail dominates confidence. */
|
|
218
|
+
const VERDICT_RANK = {
|
|
219
|
+
rejected: 2,
|
|
220
|
+
unknown: 1,
|
|
221
|
+
confirmed: 0,
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Merge N reviews pessimistically: the WORST verdict wins (rejected beats
|
|
225
|
+
* unknown beats confirmed; earliest review wins exact ties so behavior is
|
|
226
|
+
* deterministic). The merged review is what attaches to the gate payload.
|
|
227
|
+
*/
|
|
228
|
+
export function mergeVerifierVerdicts(reviews) {
|
|
229
|
+
if (reviews.length === 0)
|
|
230
|
+
throw new Error('mergeVerifierVerdicts: no reviews');
|
|
231
|
+
return reviews.reduce((worst, next) => VERDICT_RANK[next.verdict] > VERDICT_RANK[worst.verdict] ? next : worst);
|
|
232
|
+
}
|
|
233
|
+
/** True when the two verdicts disagree (recorded as evidence, not resolved). */
|
|
234
|
+
export function verdictsDiverge(reviews) {
|
|
235
|
+
return new Set(reviews.map((r) => r.verdict)).size > 1;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Build the `verifier-divergence` EVIDENCE item (PW §10): when two critical-
|
|
239
|
+
* risk reviewers disagree (one blocker, one pass — or any mismatch), BOTH
|
|
240
|
+
* verdicts travel side by side into the proof instead of one winning quietly.
|
|
241
|
+
* Rationale text is capped like every other proof cell. Null for <2 reviews
|
|
242
|
+
* (single-reviewer runs carry no divergence entry — payloads stay stable).
|
|
243
|
+
*/
|
|
244
|
+
export function divergenceFromReviews(reviews, meta) {
|
|
245
|
+
if (reviews.length < 2)
|
|
246
|
+
return null;
|
|
247
|
+
return {
|
|
248
|
+
kind: 'verifier-divergence',
|
|
249
|
+
risk: 'critical',
|
|
250
|
+
divergent: verdictsDiverge(reviews),
|
|
251
|
+
mergedVerdict: mergeVerifierVerdicts(reviews).verdict,
|
|
252
|
+
reviews: reviews.map((r, i) => ({
|
|
253
|
+
provider: r.effectiveModel.provider ?? null,
|
|
254
|
+
model: r.effectiveModel.model ?? null,
|
|
255
|
+
family: meta?.[i]?.family ?? inferModelFamily(r.effectiveModel.provider ?? '', r.effectiveModel.model ?? ''),
|
|
256
|
+
role: meta?.[i]?.role ?? 'strongest',
|
|
257
|
+
verdict: r.verdict,
|
|
258
|
+
score: r.score ?? null,
|
|
259
|
+
rationale: r.rationale?.slice(0, 500) ?? null,
|
|
260
|
+
fallback: r.fallback ?? null,
|
|
261
|
+
})),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=verifierRouting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifierRouting.js","sourceRoot":"","sources":["../../../src/cli/kraken/verifierRouting.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worktreeScheduling.ts — P2.C: overlap-scored worktree scheduling (pure).
|
|
3
|
+
*
|
|
4
|
+
* P2.A ownership arbitration serializes ANY overlapping pair of writers: the
|
|
5
|
+
* safe default, but it pays the full latency of the first writer before the
|
|
6
|
+
* second starts even when the overlap is thin. The executor already has the
|
|
7
|
+
* isolation machinery to do better (krakenWorktree): a tentacle can run in its
|
|
8
|
+
* own git worktree, and the executor merges worktree branches SEQUENTIALLY
|
|
9
|
+
* (Correction 4 — conflict keeps the branch, never aborts), so two writers in
|
|
10
|
+
* two worktrees can never corrupt each other mid-flight; the only cost of a
|
|
11
|
+
* parallel pair is a possibly-expensive MERGE. That reframes the admission
|
|
12
|
+
* question from "do they overlap?" (binary) to "how much do they overlap?"
|
|
13
|
+
* (graded): thin overlap → the merge is cheap, admit in parallel under
|
|
14
|
+
* isolation; near-identical scopes → the merge conflict would cost more than
|
|
15
|
+
* the serialization, keep the P2.A deferral.
|
|
16
|
+
*
|
|
17
|
+
* - `estimateOverlapScore(aScopes, bScopes)` — the graded heuristic. Honest
|
|
18
|
+
* about its granularity: it is a MAX over scope-glob pairs of three values
|
|
19
|
+
* only — identical (normalized) glob → 1; anything `globsOverlap` calls
|
|
20
|
+
* overlapping but not identical (ancestor/containment, wildcard claims,
|
|
21
|
+
* cross-segment globs) → 0.5; disjoint → 0. It does NOT measure how many
|
|
22
|
+
* files two globs share; it measures whether the overlap is "same claim"
|
|
23
|
+
* (1, merge = guaranteed conflict) or "different claims that touch" (0.5,
|
|
24
|
+
* merge = plausible conflict). A wildcard `**` scope scores 0.5 against
|
|
25
|
+
* anything: undeclared-scope writers are the very case isolation helps,
|
|
26
|
+
* and `OVERLAP_HIGH_THRESHOLD` is the tuning knob if a repo finds that too
|
|
27
|
+
* brave.
|
|
28
|
+
* - `classifyOverlap(score)` — three bands with exported, tunable consts:
|
|
29
|
+
* score ≥ OVERLAP_HIGH_THRESHOLD (0.75) → 'high' (serialize as today);
|
|
30
|
+
* score < OVERLAP_LOW_THRESHOLD (0.5) → 'disjoint' (arbitration never
|
|
31
|
+
* deferred it anyway); in between → 'low' (isolate and admit). The band
|
|
32
|
+
* that matters in practice is exactly [0.5, 0.75): a node that arbitration
|
|
33
|
+
* deferred ALWAYS overlaps a racing writer, so its score is ≥ 0.5 — a
|
|
34
|
+
* "low" band strictly below 0.5 would make parallel admission dead code.
|
|
35
|
+
* - `worktreeSchedulingDecision(node, racingNodes, env)` — the pure policy
|
|
36
|
+
* the executor consults for a deferred writer: is the env mode 'auto', is
|
|
37
|
+
* the node worktree-capable, and is its best overlap score low enough?
|
|
38
|
+
* Returns a mode plus the score, the rationale code and the racing node
|
|
39
|
+
* that produced the best score (for telemetry). Reads NO process.env —
|
|
40
|
+
* the caller passes the env slice in.
|
|
41
|
+
* - `resolveWorktreeMode(envValue)` — the ZELARI_KRAKEN_WORKTREE value
|
|
42
|
+
* grammar for scheduling: undefined/''/`0` → 'off', `1`/`true` → 'on',
|
|
43
|
+
* `auto` → 'auto'; ANYTHING ELSE → 'off' (documented fallback: an
|
|
44
|
+
* unrecognized value must never widen parallelism). Note 'yes'/'on' enable
|
|
45
|
+
* worktree CREATION in krakenWorktree but resolve to 'off' here —
|
|
46
|
+
* scheduling mode stays conservative for unknown values.
|
|
47
|
+
*
|
|
48
|
+
* PURE by contract: no fs, no clock, no process.env reads, no imports beyond
|
|
49
|
+
* fileOwnership (glob semantics are its, never duplicated) and ambient node
|
|
50
|
+
* types. Platform case folding is injected by the caller, like fileOwnership.
|
|
51
|
+
*
|
|
52
|
+
* @since v0.10.x — Kraken worktree scheduling (P2.C / t27)
|
|
53
|
+
*/
|
|
54
|
+
import { globsOverlap, normalizeGlob, writeScopeOf, } from './fileOwnership.js';
|
|
55
|
+
/**
|
|
56
|
+
* Score assigned to a scope-glob pair that overlaps without being identical
|
|
57
|
+
* (containment, wildcard claims, cross-segment globs). The only non-binary
|
|
58
|
+
* rung of the heuristic — see the module doc for why.
|
|
59
|
+
*/
|
|
60
|
+
export const PARTIAL_OVERLAP_SCORE = 0.5;
|
|
61
|
+
/**
|
|
62
|
+
* At or above this overlap score a parallel pair's merge is presumed too
|
|
63
|
+
* expensive: keep the P2.A sequential deferral. Tunable by design.
|
|
64
|
+
*/
|
|
65
|
+
export const OVERLAP_HIGH_THRESHOLD = 0.75;
|
|
66
|
+
/**
|
|
67
|
+
* Below this score two scopes are disjoint — arbitration never deferred the
|
|
68
|
+
* pair in the first place, so the classifier reports it only for completeness.
|
|
69
|
+
* The 'low' (isolate-and-admit) band is [OVERLAP_LOW_THRESHOLD,
|
|
70
|
+
* OVERLAP_HIGH_THRESHOLD). Tunable by design.
|
|
71
|
+
*/
|
|
72
|
+
export const OVERLAP_LOW_THRESHOLD = 0.5;
|
|
73
|
+
/**
|
|
74
|
+
* Band a 0..1 overlap score falls into: 'high' serializes (as P2.A always
|
|
75
|
+
* did), 'low' may run in parallel under worktree isolation, 'disjoint' never
|
|
76
|
+
* needed either.
|
|
77
|
+
*/
|
|
78
|
+
export function classifyOverlap(score) {
|
|
79
|
+
if (score >= OVERLAP_HIGH_THRESHOLD)
|
|
80
|
+
return 'high';
|
|
81
|
+
if (score < OVERLAP_LOW_THRESHOLD)
|
|
82
|
+
return 'disjoint';
|
|
83
|
+
return 'low';
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Estimate how strongly two normalized write-scope sets overlap: 0 (disjoint)
|
|
87
|
+
* .. 1 (same claim). Max over all scope-glob pairs — one hot pair is enough to
|
|
88
|
+
* classify the whole node pair. Deterministic; identical glob pairs
|
|
89
|
+
* short-circuit to 1.
|
|
90
|
+
*/
|
|
91
|
+
export function estimateOverlapScore(aScopes, bScopes, opts = {}) {
|
|
92
|
+
const fold = opts.caseInsensitive ?? true;
|
|
93
|
+
// Mirror globsOverlap's folding so "identical" and "overlapping" agree on
|
|
94
|
+
// what the caller's filesystem considers the same name.
|
|
95
|
+
const norm = (g) => normalizeGlob(fold ? g.toLowerCase() : g);
|
|
96
|
+
let best = 0;
|
|
97
|
+
for (const a of aScopes) {
|
|
98
|
+
for (const b of bScopes) {
|
|
99
|
+
let score;
|
|
100
|
+
if (norm(a) === norm(b)) {
|
|
101
|
+
score = 1; // same claim: the merge would be a guaranteed conflict
|
|
102
|
+
}
|
|
103
|
+
else if (globsOverlap(a, b, fold)) {
|
|
104
|
+
score = PARTIAL_OVERLAP_SCORE; // different claims that touch
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
score = 0;
|
|
108
|
+
}
|
|
109
|
+
if (score > best)
|
|
110
|
+
best = score;
|
|
111
|
+
if (best === 1)
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return best;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Kinds whose tentacle the existing worktree machinery can isolate: the
|
|
119
|
+
* `runTentacle` creation path is driven by the `general` agent kind, and the
|
|
120
|
+
* executor maps both `general` and `fix` graph nodes onto it (rework — a `fix`
|
|
121
|
+
* editing an EXISTING worktree — is excluded by the executor's rework map,
|
|
122
|
+
* which this pure module cannot see).
|
|
123
|
+
*/
|
|
124
|
+
export const WORKTREE_CAPABLE_KINDS = ['general', 'fix'];
|
|
125
|
+
/** True when a node's kind can run inside a git worktree at all. */
|
|
126
|
+
export function isWorktreeCapableKind(kind) {
|
|
127
|
+
return WORKTREE_CAPABLE_KINDS.includes(kind);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Resolve the ZELARI_KRAKEN_WORKTREE scheduling mode from a raw env value.
|
|
131
|
+
* `undefined`/''/`0` → 'off' (today's behavior), `1`/`true` → 'on' (always
|
|
132
|
+
* worktree, unchanged), `auto` → 'auto' (scheduler decides per overlap).
|
|
133
|
+
* Anything else → 'off': an unrecognized value must never widen parallelism.
|
|
134
|
+
*/
|
|
135
|
+
export function resolveWorktreeMode(envValue) {
|
|
136
|
+
const v = (envValue ?? '').trim().toLowerCase();
|
|
137
|
+
if (v === 'auto')
|
|
138
|
+
return 'auto';
|
|
139
|
+
if (v === '1' || v === 'true')
|
|
140
|
+
return 'on';
|
|
141
|
+
return 'off';
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The P2.C admission policy for one node against the writers it would race
|
|
145
|
+
* with. Pure: the env slice is passed in, never read from process.env.
|
|
146
|
+
*
|
|
147
|
+
* `racingNodes` is the set of writers the node would run alongside — already
|
|
148
|
+
* in flight PLUS admitted this same round (from the scheduler's viewpoint
|
|
149
|
+
* both are running the moment admission returns). Only the 'auto' mode ever
|
|
150
|
+
* yields 'parallel-worktree'; 'off'/'on' modes defer defensively so a caller
|
|
151
|
+
* that skips its own mode check cannot accidentally widen parallelism.
|
|
152
|
+
*
|
|
153
|
+
* Worktree capability is checked per kind here; the caller still owns the
|
|
154
|
+
* per-node state this module cannot see (rework lineage, a caller-level
|
|
155
|
+
* `allowWorktree: false`).
|
|
156
|
+
*/
|
|
157
|
+
export function worktreeSchedulingDecision(node, racingNodes, env, opts = {}) {
|
|
158
|
+
if (resolveWorktreeMode(env.ZELARI_KRAKEN_WORKTREE) !== 'auto') {
|
|
159
|
+
return { mode: 'defer', overlapScore: 0, rationaleCode: 'worktree-mode-not-auto' };
|
|
160
|
+
}
|
|
161
|
+
const nodeScopes = writeScopeOf(node);
|
|
162
|
+
if (!nodeScopes) {
|
|
163
|
+
// Read-only node: arbitration never defers these anyway; say so honestly.
|
|
164
|
+
return { mode: 'defer', overlapScore: 0, rationaleCode: 'read-only-node' };
|
|
165
|
+
}
|
|
166
|
+
if (!isWorktreeCapableKind(node.kind)) {
|
|
167
|
+
return { mode: 'defer', overlapScore: 0, rationaleCode: 'kind-not-worktree-capable' };
|
|
168
|
+
}
|
|
169
|
+
let best = 0;
|
|
170
|
+
let bestMatchId;
|
|
171
|
+
for (const other of racingNodes) {
|
|
172
|
+
const otherScopes = writeScopeOf(other);
|
|
173
|
+
if (!otherScopes)
|
|
174
|
+
continue; // read-only racer: never a write conflict
|
|
175
|
+
const score = estimateOverlapScore(nodeScopes, otherScopes, opts);
|
|
176
|
+
if (score > best) {
|
|
177
|
+
best = score;
|
|
178
|
+
bestMatchId = other.id;
|
|
179
|
+
}
|
|
180
|
+
if (best === 1)
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
if (best === 0) {
|
|
184
|
+
return { mode: 'plain-parallel', overlapScore: 0, rationaleCode: 'no-racing-overlap' };
|
|
185
|
+
}
|
|
186
|
+
if (classifyOverlap(best) === 'low') {
|
|
187
|
+
return {
|
|
188
|
+
mode: 'parallel-worktree',
|
|
189
|
+
overlapScore: best,
|
|
190
|
+
rationaleCode: 'low-overlap-worktree',
|
|
191
|
+
...(bestMatchId !== undefined ? { bestMatchId } : {}),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
mode: 'defer',
|
|
196
|
+
overlapScore: best,
|
|
197
|
+
rationaleCode: 'high-overlap',
|
|
198
|
+
...(bestMatchId !== undefined ? { bestMatchId } : {}),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=worktreeScheduling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktreeScheduling.js","sourceRoot":"","sources":["../../../src/cli/kraken/worktreeScheduling.ts"],"names":[],"mappings":"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"}
|