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.
Files changed (84) hide show
  1. package/dist/cli/headless/policyGate.js +101 -0
  2. package/dist/cli/headless/policyGate.js.map +1 -0
  3. package/dist/cli/headless.js.map +1 -1
  4. package/dist/cli/kraken/completionProof.js +89 -41
  5. package/dist/cli/kraken/completionProof.js.map +1 -1
  6. package/dist/cli/kraken/completionProofAttestation.js +157 -0
  7. package/dist/cli/kraken/completionProofAttestation.js.map +1 -0
  8. package/dist/cli/kraken/completionProofPersist.js +219 -0
  9. package/dist/cli/kraken/completionProofPersist.js.map +1 -0
  10. package/dist/cli/kraken/completionProofProbe.js +135 -0
  11. package/dist/cli/kraken/completionProofProbe.js.map +1 -0
  12. package/dist/cli/kraken/contractCompiler.js +142 -0
  13. package/dist/cli/kraken/contractCompiler.js.map +1 -0
  14. package/dist/cli/kraken/delegationPolicy.js +40 -3
  15. package/dist/cli/kraken/delegationPolicy.js.map +1 -1
  16. package/dist/cli/kraken/executor.js +462 -9
  17. package/dist/cli/kraken/executor.js.map +1 -1
  18. package/dist/cli/kraken/fileOwnership.js +190 -0
  19. package/dist/cli/kraken/fileOwnership.js.map +1 -0
  20. package/dist/cli/kraken/modelReputation.js +208 -0
  21. package/dist/cli/kraken/modelReputation.js.map +1 -0
  22. package/dist/cli/kraken/nativeVerification.js +53 -11
  23. package/dist/cli/kraken/nativeVerification.js.map +1 -1
  24. package/dist/cli/kraken/planner.js +2 -0
  25. package/dist/cli/kraken/planner.js.map +1 -1
  26. package/dist/cli/kraken/reputationStore.js +139 -0
  27. package/dist/cli/kraken/reputationStore.js.map +1 -0
  28. package/dist/cli/kraken/semanticOwnership.js +184 -0
  29. package/dist/cli/kraken/semanticOwnership.js.map +1 -0
  30. package/dist/cli/kraken/spawnRoi.js +209 -0
  31. package/dist/cli/kraken/spawnRoi.js.map +1 -0
  32. package/dist/cli/kraken/transactional.js +104 -0
  33. package/dist/cli/kraken/transactional.js.map +1 -0
  34. package/dist/cli/kraken/verificationAdapters/dotnet.js +49 -0
  35. package/dist/cli/kraken/verificationAdapters/dotnet.js.map +1 -0
  36. package/dist/cli/kraken/verificationAdapters/go.js +36 -0
  37. package/dist/cli/kraken/verificationAdapters/go.js.map +1 -0
  38. package/dist/cli/kraken/verificationAdapters/index.js +47 -0
  39. package/dist/cli/kraken/verificationAdapters/index.js.map +1 -0
  40. package/dist/cli/kraken/verificationAdapters/java.js +102 -0
  41. package/dist/cli/kraken/verificationAdapters/java.js.map +1 -0
  42. package/dist/cli/kraken/verificationAdapters/node.js +90 -0
  43. package/dist/cli/kraken/verificationAdapters/node.js.map +1 -0
  44. package/dist/cli/kraken/verificationAdapters/python.js +86 -0
  45. package/dist/cli/kraken/verificationAdapters/python.js.map +1 -0
  46. package/dist/cli/kraken/verificationAdapters/rust.js +39 -0
  47. package/dist/cli/kraken/verificationAdapters/rust.js.map +1 -0
  48. package/dist/cli/kraken/verificationAdapters/types.js +2 -0
  49. package/dist/cli/kraken/verificationAdapters/types.js.map +1 -0
  50. package/dist/cli/kraken/verificationBridge.js +40 -3
  51. package/dist/cli/kraken/verificationBridge.js.map +1 -1
  52. package/dist/cli/kraken/verifierLifecycle.js +51 -36
  53. package/dist/cli/kraken/verifierLifecycle.js.map +1 -1
  54. package/dist/cli/kraken/verifierRouting.js +264 -0
  55. package/dist/cli/kraken/verifierRouting.js.map +1 -0
  56. package/dist/cli/kraken/worktreeScheduling.js +201 -0
  57. package/dist/cli/kraken/worktreeScheduling.js.map +1 -0
  58. package/dist/cli/main.bundled.js +3880 -889
  59. package/dist/cli/main.bundled.js.map +4 -4
  60. package/dist/cli/orchestration/facts.js +127 -0
  61. package/dist/cli/orchestration/facts.js.map +1 -0
  62. package/dist/cli/orchestration/policy.js +128 -75
  63. package/dist/cli/orchestration/policy.js.map +1 -1
  64. package/dist/cli/orchestration/signals.js +96 -0
  65. package/dist/cli/orchestration/signals.js.map +1 -0
  66. package/dist/cli/runHeadless.js +92 -19
  67. package/dist/cli/runHeadless.js.map +1 -1
  68. package/dist/cli/safety/policyEngine.js +159 -20
  69. package/dist/cli/safety/policyEngine.js.map +1 -1
  70. package/dist/cli/safety/policyLoadMode.js +59 -0
  71. package/dist/cli/safety/policyLoadMode.js.map +1 -0
  72. package/dist/cli/safety/resourceClaims.js +373 -0
  73. package/dist/cli/safety/resourceClaims.js.map +1 -0
  74. package/dist/cli/safety/sandboxPath.js +160 -17
  75. package/dist/cli/safety/sandboxPath.js.map +1 -1
  76. package/dist/cli/toolRegistry.js +98 -8
  77. package/dist/cli/toolRegistry.js.map +1 -1
  78. package/dist/cli/tools/execProcess.js +121 -0
  79. package/dist/cli/tools/execProcess.js.map +1 -0
  80. package/dist/cli/tools/krakenRadio.js +9 -0
  81. package/dist/cli/tools/krakenRadio.js.map +1 -1
  82. package/dist/cli/tools/taskTool.js +8 -3
  83. package/dist/cli/tools/taskTool.js.map +1 -1
  84. 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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"}