trellis 3.1.31 → 3.1.33
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 +10 -0
- package/README.md +1 -1
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +320 -24
- package/dist/cli/lane.d.ts +6 -0
- package/dist/cli/lane.d.ts.map +1 -0
- package/dist/cms/client.d.ts +1 -1
- package/dist/cms/client.d.ts.map +1 -1
- package/dist/cms/index.js +0 -16
- package/dist/cms/types.d.ts +1 -1
- package/dist/cms/types.d.ts.map +1 -1
- package/dist/context/manager.d.ts +1 -1
- package/dist/context/manager.d.ts.map +1 -1
- package/dist/context/types.d.ts +1 -1
- package/dist/context/types.d.ts.map +1 -1
- package/dist/decisions/index.js +2 -2
- package/dist/engine.d.ts +86 -2
- package/dist/engine.d.ts.map +1 -1
- package/dist/{index-65z0xfjw.js → index-4cdr7x2x.js} +2 -2
- package/dist/{index-r5064492.js → index-nq520y6k.js} +7 -3
- package/dist/{index-v9b4hqa7.js → index-nzb3am4f.js} +4 -1
- package/dist/{index-8f2z3b0h.js → index-rv1p47gp.js} +996 -172
- package/dist/{index-1tv9sbwp.js → index-w5wccer3.js} +1 -1
- package/dist/{index-p3nnc7ac.js → index-xhcp6xrn.js} +462 -31
- package/dist/index.js +39 -7
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/plugins/agent-memory/graph-context-manager.d.ts +5 -0
- package/dist/plugins/agent-memory/graph-context-manager.d.ts.map +1 -1
- package/dist/{remote-manager-8qbz3mrn.js → remote-manager-xvbg4230.js} +4 -4
- package/dist/vcs/branch.d.ts +6 -0
- package/dist/vcs/branch.d.ts.map +1 -1
- package/dist/vcs/decompose.d.ts.map +1 -1
- package/dist/vcs/engine-context.d.ts +9 -2
- package/dist/vcs/engine-context.d.ts.map +1 -1
- package/dist/vcs/index.d.ts +2 -0
- package/dist/vcs/index.d.ts.map +1 -1
- package/dist/vcs/index.js +36 -4
- package/dist/vcs/issue.d.ts +2 -0
- package/dist/vcs/issue.d.ts.map +1 -1
- package/dist/vcs/lane-materialize.d.ts +45 -0
- package/dist/vcs/lane-materialize.d.ts.map +1 -0
- package/dist/vcs/lane-promote.d.ts +56 -0
- package/dist/vcs/lane-promote.d.ts.map +1 -0
- package/dist/vcs/lane.d.ts +55 -0
- package/dist/vcs/lane.d.ts.map +1 -0
- package/dist/vcs/op-log.d.ts +29 -0
- package/dist/vcs/op-log.d.ts.map +1 -0
- package/dist/vcs/types.d.ts +15 -1
- package/dist/vcs/types.d.ts.map +1 -1
- package/dist/watcher/ingestion.d.ts +1 -0
- package/dist/watcher/ingestion.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
BlobStore,
|
|
4
|
+
JsonOpLog,
|
|
5
|
+
LaneOpLog,
|
|
4
6
|
addCriterion,
|
|
5
7
|
assignIssue,
|
|
6
8
|
blockIssue,
|
|
@@ -10,12 +12,14 @@ import {
|
|
|
10
12
|
createBranch,
|
|
11
13
|
createCheckpoint,
|
|
12
14
|
createIssue,
|
|
15
|
+
createLaneMeta,
|
|
13
16
|
createMilestone,
|
|
14
17
|
decompose,
|
|
15
18
|
deleteBranch,
|
|
16
19
|
diffFileStates,
|
|
17
20
|
diffOpRange,
|
|
18
21
|
getActiveIssues,
|
|
22
|
+
getBranchHeadOpHash,
|
|
19
23
|
getIssue,
|
|
20
24
|
init_blob_store,
|
|
21
25
|
init_branch,
|
|
@@ -23,39 +27,50 @@ import {
|
|
|
23
27
|
init_decompose,
|
|
24
28
|
init_diff,
|
|
25
29
|
init_issue,
|
|
30
|
+
init_lane,
|
|
26
31
|
init_merge,
|
|
27
32
|
init_milestone,
|
|
33
|
+
init_op_log,
|
|
34
|
+
laneDir,
|
|
28
35
|
listBranches,
|
|
29
36
|
listCheckpoints,
|
|
30
37
|
listIssues,
|
|
38
|
+
listLaneMetas,
|
|
31
39
|
listMilestones,
|
|
32
40
|
loadBranchState,
|
|
41
|
+
loadLaneMeta,
|
|
33
42
|
pauseIssue,
|
|
34
43
|
reopenIssue,
|
|
44
|
+
resolveLaneHeadFromJournal,
|
|
35
45
|
resumeIssue,
|
|
36
46
|
runCriteria,
|
|
37
47
|
saveBranchState,
|
|
48
|
+
saveLaneMeta,
|
|
38
49
|
setCriterionStatus,
|
|
50
|
+
shouldAdvanceBranchHead,
|
|
39
51
|
startIssue,
|
|
40
52
|
switchBranch,
|
|
41
53
|
threeWayMerge,
|
|
54
|
+
threeWayTextMerge,
|
|
42
55
|
triageIssue,
|
|
43
56
|
unblockIssue,
|
|
44
|
-
updateIssue
|
|
45
|
-
|
|
57
|
+
updateIssue,
|
|
58
|
+
updateLaneHead
|
|
59
|
+
} from "./index-xhcp6xrn.js";
|
|
46
60
|
import {
|
|
47
61
|
getDecision,
|
|
48
62
|
getDecisionChain,
|
|
49
63
|
init_decisions,
|
|
50
64
|
queryDecisions,
|
|
51
65
|
recordDecision
|
|
52
|
-
} from "./index-
|
|
66
|
+
} from "./index-4cdr7x2x.js";
|
|
53
67
|
import {
|
|
54
68
|
DEFAULT_CONFIG,
|
|
55
69
|
createVcsOp,
|
|
56
70
|
init_ops,
|
|
57
|
-
init_types
|
|
58
|
-
|
|
71
|
+
init_types,
|
|
72
|
+
isVcsOpKind
|
|
73
|
+
} from "./index-nzb3am4f.js";
|
|
59
74
|
import {
|
|
60
75
|
EAVStore,
|
|
61
76
|
init_eav_store
|
|
@@ -308,6 +323,9 @@ class Ingestion {
|
|
|
308
323
|
getLastOpHash() {
|
|
309
324
|
return this.lastOpHash;
|
|
310
325
|
}
|
|
326
|
+
setLastOpHash(hash) {
|
|
327
|
+
this.lastOpHash = hash;
|
|
328
|
+
}
|
|
311
329
|
}
|
|
312
330
|
var EXT_LANGUAGE;
|
|
313
331
|
var init_ingestion = __esm(() => {
|
|
@@ -5333,171 +5351,443 @@ var init_semantic = __esm(() => {
|
|
|
5333
5351
|
init_semantic_merge();
|
|
5334
5352
|
});
|
|
5335
5353
|
|
|
5336
|
-
// src/
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
}
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
this.ops = JSON.parse(raw);
|
|
5364
|
-
} catch (err) {
|
|
5365
|
-
const backupPath = this.filePath + ".bak";
|
|
5366
|
-
if (existsSync4(backupPath)) {
|
|
5367
|
-
const backupRaw = readFileSync4(backupPath, "utf-8");
|
|
5368
|
-
this.ops = JSON.parse(backupRaw);
|
|
5369
|
-
writeFileSync3(this.filePath, backupRaw);
|
|
5370
|
-
} else {
|
|
5371
|
-
throw new Error(`Corrupted ops.json and no backup found. Run \`trellis repair\` to attempt recovery.`);
|
|
5372
|
-
}
|
|
5373
|
-
}
|
|
5374
|
-
}
|
|
5375
|
-
}
|
|
5376
|
-
append(op) {
|
|
5377
|
-
this.withLock(() => {
|
|
5378
|
-
const diskOps = this.readOpsFromDisk();
|
|
5379
|
-
this.ops = diskOps;
|
|
5380
|
-
if (this.ops.some((existing) => existing.hash === op.hash)) {
|
|
5381
|
-
return;
|
|
5382
|
-
}
|
|
5383
|
-
this.ops.push(op);
|
|
5384
|
-
this.flush();
|
|
5385
|
-
});
|
|
5386
|
-
}
|
|
5387
|
-
readAll() {
|
|
5388
|
-
return [...this.ops];
|
|
5354
|
+
// src/vcs/lane-promote.ts
|
|
5355
|
+
function replayOpIntoStore(store, op) {
|
|
5356
|
+
const d = decompose(op);
|
|
5357
|
+
if (d.deleteFacts.length > 0)
|
|
5358
|
+
store.deleteFacts(d.deleteFacts);
|
|
5359
|
+
if (d.deleteLinks.length > 0)
|
|
5360
|
+
store.deleteLinks(d.deleteLinks);
|
|
5361
|
+
if (d.addFacts.length > 0)
|
|
5362
|
+
store.addFacts(d.addFacts);
|
|
5363
|
+
if (d.addLinks.length > 0)
|
|
5364
|
+
store.addLinks(d.addLinks);
|
|
5365
|
+
}
|
|
5366
|
+
function resolveBranchHeadFromOps(ops, branchName) {
|
|
5367
|
+
for (let i = ops.length - 1;i >= 0; i--) {
|
|
5368
|
+
const op = ops[i];
|
|
5369
|
+
if (op.kind === "vcs:branchAdvance" && op.vcs?.branchName === branchName && op.vcs.targetOpHash) {
|
|
5370
|
+
return op.vcs.targetOpHash;
|
|
5371
|
+
}
|
|
5372
|
+
}
|
|
5373
|
+
return ops[ops.length - 1]?.hash;
|
|
5374
|
+
}
|
|
5375
|
+
function buildStoreUpTo(ops, atOpHash) {
|
|
5376
|
+
const store = new EAVStore;
|
|
5377
|
+
for (const op of ops) {
|
|
5378
|
+
replayOpIntoStore(store, op);
|
|
5379
|
+
if (atOpHash && op.hash === atOpHash)
|
|
5380
|
+
break;
|
|
5389
5381
|
}
|
|
5390
|
-
|
|
5391
|
-
|
|
5382
|
+
return store;
|
|
5383
|
+
}
|
|
5384
|
+
function getFactValue(store, entity, attribute) {
|
|
5385
|
+
const facts = store.getFactsByEntity(entity).filter((f) => f.a === attribute);
|
|
5386
|
+
return facts[facts.length - 1]?.v;
|
|
5387
|
+
}
|
|
5388
|
+
function entityAttributes(store, entity) {
|
|
5389
|
+
const map = new Map;
|
|
5390
|
+
for (const fact of store.getFactsByEntity(entity)) {
|
|
5391
|
+
map.set(fact.a, fact.v);
|
|
5392
5392
|
}
|
|
5393
|
-
|
|
5394
|
-
|
|
5393
|
+
return map;
|
|
5394
|
+
}
|
|
5395
|
+
function collectTouchedAttributes(op) {
|
|
5396
|
+
const d = decompose(op);
|
|
5397
|
+
const map = new Map;
|
|
5398
|
+
const touch = (fact) => {
|
|
5399
|
+
if (!map.has(fact.e))
|
|
5400
|
+
map.set(fact.e, new Set);
|
|
5401
|
+
map.get(fact.e).add(fact.a);
|
|
5402
|
+
};
|
|
5403
|
+
for (const f of d.addFacts)
|
|
5404
|
+
touch(f);
|
|
5405
|
+
for (const f of d.deleteFacts)
|
|
5406
|
+
touch(f);
|
|
5407
|
+
return map;
|
|
5408
|
+
}
|
|
5409
|
+
function collectTouchedEntities(op) {
|
|
5410
|
+
const d = decompose(op);
|
|
5411
|
+
const entities = new Set;
|
|
5412
|
+
for (const f of [...d.addFacts, ...d.deleteFacts])
|
|
5413
|
+
entities.add(f.e);
|
|
5414
|
+
for (const l of [...d.addLinks, ...d.deleteLinks]) {
|
|
5415
|
+
entities.add(l.e1);
|
|
5416
|
+
entities.add(l.e2);
|
|
5395
5417
|
}
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5418
|
+
return entities;
|
|
5419
|
+
}
|
|
5420
|
+
function atomsEqual(a, b) {
|
|
5421
|
+
if (a === b)
|
|
5422
|
+
return true;
|
|
5423
|
+
if (a === undefined || b === undefined)
|
|
5424
|
+
return false;
|
|
5425
|
+
return String(a) === String(b);
|
|
5426
|
+
}
|
|
5427
|
+
function buildLaneFileState(laneOps, throughIndex) {
|
|
5428
|
+
const slice = laneOps.slice(0, throughIndex + 1);
|
|
5429
|
+
return buildFileStateAtOp(slice);
|
|
5430
|
+
}
|
|
5431
|
+
function filePathsFromOp(op) {
|
|
5432
|
+
const paths = [];
|
|
5433
|
+
if (op.vcs?.filePath)
|
|
5434
|
+
paths.push(op.vcs.filePath);
|
|
5435
|
+
if (op.vcs?.oldFilePath)
|
|
5436
|
+
paths.push(op.vcs.oldFilePath);
|
|
5437
|
+
return paths;
|
|
5438
|
+
}
|
|
5439
|
+
function detectEntityConflicts(op, baseStore, headStore) {
|
|
5440
|
+
const conflicts = [];
|
|
5441
|
+
const d = decompose(op);
|
|
5442
|
+
const touchedEntities = collectTouchedEntities(op);
|
|
5443
|
+
const laneAttrsByEntity = collectTouchedAttributes(op);
|
|
5444
|
+
for (const fact of d.addFacts) {
|
|
5445
|
+
const headValue = getFactValue(headStore, fact.e, fact.a);
|
|
5446
|
+
const baseValue = getFactValue(baseStore, fact.e, fact.a);
|
|
5447
|
+
if (atomsEqual(headValue, fact.v))
|
|
5448
|
+
continue;
|
|
5449
|
+
if (!atomsEqual(headValue, baseValue) && headValue !== undefined) {
|
|
5450
|
+
conflicts.push({
|
|
5451
|
+
class: "hard",
|
|
5452
|
+
laneOpHash: op.hash,
|
|
5453
|
+
entityId: fact.e,
|
|
5454
|
+
attribute: fact.a,
|
|
5455
|
+
integrationValue: headValue,
|
|
5456
|
+
laneValue: fact.v,
|
|
5457
|
+
suggestion: "manual",
|
|
5458
|
+
message: `Integration changed ${fact.e}.${fact.a} since fork`
|
|
5459
|
+
});
|
|
5405
5460
|
}
|
|
5406
|
-
const tempPath = `${this.filePath}.tmp`;
|
|
5407
|
-
writeFileSync3(tempPath, JSON.stringify(this.ops, null, 2));
|
|
5408
|
-
renameSync(tempPath, this.filePath);
|
|
5409
5461
|
}
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
const
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
const
|
|
5420
|
-
if (
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5462
|
+
for (const entityId of touchedEntities) {
|
|
5463
|
+
const laneAttrs = laneAttrsByEntity.get(entityId) ?? new Set;
|
|
5464
|
+
if (laneAttrs.size === 0)
|
|
5465
|
+
continue;
|
|
5466
|
+
const baseAttrs = entityAttributes(baseStore, entityId);
|
|
5467
|
+
const headAttrs = entityAttributes(headStore, entityId);
|
|
5468
|
+
for (const [attribute, headValue] of headAttrs) {
|
|
5469
|
+
if (laneAttrs.has(attribute))
|
|
5470
|
+
continue;
|
|
5471
|
+
const baseValue = baseAttrs.get(attribute);
|
|
5472
|
+
if (!atomsEqual(headValue, baseValue)) {
|
|
5473
|
+
conflicts.push({
|
|
5474
|
+
class: "soft",
|
|
5475
|
+
laneOpHash: op.hash,
|
|
5476
|
+
entityId,
|
|
5477
|
+
attribute,
|
|
5478
|
+
integrationValue: headValue,
|
|
5479
|
+
suggestion: "manual",
|
|
5480
|
+
message: `Integration updated ${entityId}.${attribute} while lane touched the same entity`
|
|
5481
|
+
});
|
|
5425
5482
|
}
|
|
5426
|
-
throw new Error(`Corrupted ops.json and no backup found. Run \`trellis repair\` to attempt recovery.`);
|
|
5427
5483
|
}
|
|
5428
5484
|
}
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5485
|
+
return conflicts;
|
|
5486
|
+
}
|
|
5487
|
+
function blobText(blobStore, hash) {
|
|
5488
|
+
if (!hash || !blobStore)
|
|
5489
|
+
return;
|
|
5490
|
+
const content = blobStore.get(hash);
|
|
5491
|
+
if (!content)
|
|
5492
|
+
return;
|
|
5493
|
+
const text = content.toString("utf-8");
|
|
5494
|
+
return text.endsWith(`
|
|
5495
|
+
`) ? text.slice(0, -1) : text;
|
|
5496
|
+
}
|
|
5497
|
+
function mergeLaneFile(path, base, ours, theirs, blobStore) {
|
|
5498
|
+
const b = base.get(path);
|
|
5499
|
+
const o = ours.get(path);
|
|
5500
|
+
const t = theirs.get(path);
|
|
5501
|
+
const baseHash = b && !b.deleted ? b.contentHash : undefined;
|
|
5502
|
+
const oursHash = o && !o.deleted ? o.contentHash : undefined;
|
|
5503
|
+
const theirsHash = t && !t.deleted ? t.contentHash : undefined;
|
|
5504
|
+
if (oursHash === theirsHash)
|
|
5505
|
+
return { clean: true };
|
|
5506
|
+
if (theirsHash === baseHash && oursHash !== baseHash)
|
|
5507
|
+
return { clean: true };
|
|
5508
|
+
if (oursHash === baseHash && theirsHash !== baseHash) {
|
|
5509
|
+
const theirsContent2 = blobText(blobStore, theirsHash);
|
|
5510
|
+
return { clean: true, merged: theirsContent2 };
|
|
5511
|
+
}
|
|
5512
|
+
const baseContent = blobText(blobStore, baseHash) ?? "";
|
|
5513
|
+
const oursContent = blobText(blobStore, oursHash);
|
|
5514
|
+
const theirsContent = blobText(blobStore, theirsHash);
|
|
5515
|
+
if (oursContent === undefined || theirsContent === undefined) {
|
|
5516
|
+
return { clean: false, conflictKind: "modify-modify" };
|
|
5517
|
+
}
|
|
5518
|
+
const textResult = threeWayTextMerge(baseContent, oursContent, theirsContent);
|
|
5519
|
+
if (textResult.clean) {
|
|
5520
|
+
return { clean: true, merged: textResult.merged };
|
|
5521
|
+
}
|
|
5522
|
+
return { clean: false, conflictKind: "modify-modify" };
|
|
5523
|
+
}
|
|
5524
|
+
function detectFileConflict(op, integrationOps, laneOps, laneOpIndex, baseOpHash, snapshotHead, blobStore) {
|
|
5525
|
+
const paths = filePathsFromOp(op);
|
|
5526
|
+
if (paths.length === 0)
|
|
5527
|
+
return {};
|
|
5528
|
+
const base = buildFileStateAtOp(integrationOps, baseOpHash);
|
|
5529
|
+
const ours = buildFileStateAtOp(integrationOps, snapshotHead);
|
|
5530
|
+
const theirs = buildLaneFileState(laneOps, laneOpIndex);
|
|
5531
|
+
for (const path of paths) {
|
|
5532
|
+
const baseState = base.get(path);
|
|
5533
|
+
const headState = ours.get(path);
|
|
5534
|
+
const laneState = theirs.get(path);
|
|
5535
|
+
const baseHash = baseState && !baseState.deleted ? baseState.contentHash : undefined;
|
|
5536
|
+
const headHash = headState && !headState.deleted ? headState.contentHash : undefined;
|
|
5537
|
+
const laneHash = laneState && !laneState.deleted ? laneState.contentHash : undefined;
|
|
5538
|
+
if (headHash === laneHash)
|
|
5539
|
+
continue;
|
|
5540
|
+
if (headHash === baseHash && laneHash !== baseHash)
|
|
5541
|
+
continue;
|
|
5542
|
+
if (laneHash === baseHash && headHash !== baseHash)
|
|
5543
|
+
continue;
|
|
5544
|
+
const mergeResult = mergeLaneFile(path, base, ours, theirs, blobStore);
|
|
5545
|
+
if (mergeResult.clean) {
|
|
5546
|
+
if (mergeResult.merged !== undefined) {
|
|
5547
|
+
return { mergedContent: mergeResult.merged };
|
|
5443
5548
|
}
|
|
5549
|
+
continue;
|
|
5444
5550
|
}
|
|
5445
|
-
|
|
5446
|
-
|
|
5551
|
+
return {
|
|
5552
|
+
conflict: {
|
|
5553
|
+
class: "file",
|
|
5554
|
+
laneOpHash: op.hash,
|
|
5555
|
+
filePath: path,
|
|
5556
|
+
suggestion: "manual",
|
|
5557
|
+
message: `File conflict on ${path} (${mergeResult.conflictKind ?? "modify-modify"})`
|
|
5558
|
+
}
|
|
5559
|
+
};
|
|
5560
|
+
}
|
|
5561
|
+
return {};
|
|
5562
|
+
}
|
|
5563
|
+
function isBlockingConflict(conflict) {
|
|
5564
|
+
return conflict.class === "soft" || conflict.class === "hard" || conflict.class === "file";
|
|
5565
|
+
}
|
|
5566
|
+
async function planLanePromote(params) {
|
|
5567
|
+
const {
|
|
5568
|
+
laneId,
|
|
5569
|
+
meta,
|
|
5570
|
+
targetBranch,
|
|
5571
|
+
snapshotHead,
|
|
5572
|
+
integrationOps,
|
|
5573
|
+
laneOps,
|
|
5574
|
+
parentLaneOps,
|
|
5575
|
+
blobStore
|
|
5576
|
+
} = params;
|
|
5577
|
+
const baseStore = buildStoreUpTo(integrationOps, meta.baseOpHash);
|
|
5578
|
+
const headStore = buildStoreUpTo(integrationOps, snapshotHead);
|
|
5579
|
+
const fileLaneOps = meta.forkKind === "child" && parentLaneOps?.length ? [...parentLaneOps, ...laneOps] : laneOps;
|
|
5580
|
+
const childOpOffset = parentLaneOps?.length ?? 0;
|
|
5581
|
+
const opsToReplay = [];
|
|
5582
|
+
const conflicts = [];
|
|
5583
|
+
let safeOpCount = 0;
|
|
5584
|
+
for (let i = 0;i < laneOps.length; i++) {
|
|
5585
|
+
const op = laneOps[i];
|
|
5586
|
+
if (SKIP_PROMOTE_KINDS.has(op.kind))
|
|
5587
|
+
continue;
|
|
5588
|
+
if (FILE_OP_KINDS2.has(op.kind)) {
|
|
5589
|
+
const fileResult = detectFileConflict(op, integrationOps, fileLaneOps, childOpOffset + i, meta.baseOpHash, snapshotHead, blobStore);
|
|
5590
|
+
if (fileResult.conflict) {
|
|
5591
|
+
conflicts.push(fileResult.conflict);
|
|
5592
|
+
continue;
|
|
5593
|
+
}
|
|
5594
|
+
opsToReplay.push({
|
|
5595
|
+
sourceOp: op,
|
|
5596
|
+
mergedContent: fileResult.mergedContent
|
|
5597
|
+
});
|
|
5598
|
+
safeOpCount++;
|
|
5599
|
+
continue;
|
|
5447
5600
|
}
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
closeSync(lockFd);
|
|
5452
|
-
try {
|
|
5453
|
-
unlinkSync(this.lockPath);
|
|
5454
|
-
} catch {}
|
|
5601
|
+
const decomposed = decompose(op);
|
|
5602
|
+
if (decomposed.addFacts.length > 0 && decomposed.deleteFacts.length === 0 && decomposed.addFacts.every((fact) => atomsEqual(getFactValue(headStore, fact.e, fact.a), fact.v))) {
|
|
5603
|
+
continue;
|
|
5455
5604
|
}
|
|
5605
|
+
const entityConflicts = detectEntityConflicts(op, baseStore, headStore);
|
|
5606
|
+
const blockingEntity = entityConflicts.filter(isBlockingConflict);
|
|
5607
|
+
conflicts.push(...entityConflicts);
|
|
5608
|
+
if (blockingEntity.length > 0)
|
|
5609
|
+
continue;
|
|
5610
|
+
opsToReplay.push({ sourceOp: op });
|
|
5611
|
+
safeOpCount++;
|
|
5612
|
+
replayOpIntoStore(headStore, op);
|
|
5613
|
+
}
|
|
5614
|
+
const blockingConflicts = conflicts.filter(isBlockingConflict);
|
|
5615
|
+
return {
|
|
5616
|
+
laneId,
|
|
5617
|
+
targetBranch,
|
|
5618
|
+
snapshotHead,
|
|
5619
|
+
baseOpHash: meta.baseOpHash,
|
|
5620
|
+
opsToReplay,
|
|
5621
|
+
conflicts,
|
|
5622
|
+
blockingConflicts,
|
|
5623
|
+
safeOpCount,
|
|
5624
|
+
canPromote: blockingConflicts.length === 0 && opsToReplay.length > 0
|
|
5625
|
+
};
|
|
5626
|
+
}
|
|
5627
|
+
async function rechainOpForIntegration(op, previousHash) {
|
|
5628
|
+
if (!isVcsOpKindSafe(op.kind)) {
|
|
5629
|
+
throw new Error(`Cannot rechain op kind '${op.kind}' for integration replay`);
|
|
5456
5630
|
}
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5631
|
+
return createVcsOp(op.kind, {
|
|
5632
|
+
agentId: op.agentId,
|
|
5633
|
+
previousHash,
|
|
5634
|
+
vcs: { ...op.vcs }
|
|
5635
|
+
});
|
|
5636
|
+
}
|
|
5637
|
+
function isVcsOpKindSafe(kind) {
|
|
5638
|
+
return kind.startsWith("vcs:");
|
|
5639
|
+
}
|
|
5640
|
+
function formatPromoteExplain(plan) {
|
|
5641
|
+
const lines = [
|
|
5642
|
+
`Lane promote plan: ${plan.laneId}`,
|
|
5643
|
+
` Target branch: ${plan.targetBranch}`,
|
|
5644
|
+
` Fork base: ${plan.baseOpHash}`,
|
|
5645
|
+
` Snapshot head: ${plan.snapshotHead}`,
|
|
5646
|
+
` Ops to replay: ${plan.opsToReplay.length}`,
|
|
5647
|
+
` Safe ops: ${plan.safeOpCount}`
|
|
5648
|
+
];
|
|
5649
|
+
if (plan.blockingConflicts.length === 0) {
|
|
5650
|
+
lines.push("", plan.canPromote ? "\u2713 Ready to promote" : "No ops to replay");
|
|
5651
|
+
return lines.join(`
|
|
5652
|
+
`);
|
|
5653
|
+
}
|
|
5654
|
+
lines.push("", `Blocking conflicts (${plan.blockingConflicts.length}):`);
|
|
5655
|
+
for (const c of plan.blockingConflicts) {
|
|
5656
|
+
const where = c.filePath ?? (c.entityId && c.attribute ? `${c.entityId}.${c.attribute}` : c.entityId);
|
|
5657
|
+
lines.push(` [${c.class}] ${where ?? c.laneOpHash.slice(0, 24)}`);
|
|
5658
|
+
if (c.message)
|
|
5659
|
+
lines.push(` ${c.message}`);
|
|
5660
|
+
if (c.integrationValue !== undefined) {
|
|
5661
|
+
lines.push(` integration: ${String(c.integrationValue)}`);
|
|
5460
5662
|
}
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5663
|
+
if (c.laneValue !== undefined) {
|
|
5664
|
+
lines.push(` lane: ${String(c.laneValue)}`);
|
|
5665
|
+
}
|
|
5666
|
+
}
|
|
5667
|
+
return lines.join(`
|
|
5668
|
+
`);
|
|
5669
|
+
}
|
|
5670
|
+
var SKIP_PROMOTE_KINDS, FILE_OP_KINDS2;
|
|
5671
|
+
var init_lane_promote = __esm(() => {
|
|
5672
|
+
init_eav_store();
|
|
5673
|
+
init_decompose();
|
|
5674
|
+
init_diff();
|
|
5675
|
+
init_merge();
|
|
5676
|
+
init_ops();
|
|
5677
|
+
SKIP_PROMOTE_KINDS = new Set([
|
|
5678
|
+
"vcs:branchAdvance",
|
|
5679
|
+
"vcs:laneCreate",
|
|
5680
|
+
"vcs:laneDrop",
|
|
5681
|
+
"vcs:lanePromoteStart",
|
|
5682
|
+
"vcs:lanePromoteComplete",
|
|
5683
|
+
"vcs:lanePromoteAbort"
|
|
5684
|
+
]);
|
|
5685
|
+
FILE_OP_KINDS2 = new Set([
|
|
5686
|
+
"vcs:fileAdd",
|
|
5687
|
+
"vcs:fileModify",
|
|
5688
|
+
"vcs:fileDelete",
|
|
5689
|
+
"vcs:fileRename"
|
|
5690
|
+
]);
|
|
5691
|
+
});
|
|
5692
|
+
|
|
5693
|
+
// src/vcs/lane-materialize.ts
|
|
5694
|
+
function emptyMaterializationStats() {
|
|
5695
|
+
return {
|
|
5696
|
+
integrationOpsReplayed: 0,
|
|
5697
|
+
laneOpsReplayed: 0,
|
|
5698
|
+
integrationCacheHit: false
|
|
5699
|
+
};
|
|
5700
|
+
}
|
|
5701
|
+
function replayOpIntoStore2(store, op) {
|
|
5702
|
+
const d = decompose(op);
|
|
5703
|
+
if (d.deleteFacts.length > 0)
|
|
5704
|
+
store.deleteFacts(d.deleteFacts);
|
|
5705
|
+
if (d.deleteLinks.length > 0)
|
|
5706
|
+
store.deleteLinks(d.deleteLinks);
|
|
5707
|
+
if (d.addFacts.length > 0)
|
|
5708
|
+
store.addFacts(d.addFacts);
|
|
5709
|
+
if (d.addLinks.length > 0)
|
|
5710
|
+
store.addLinks(d.addLinks);
|
|
5711
|
+
}
|
|
5712
|
+
function cloneStore(source) {
|
|
5713
|
+
const clone = new EAVStore;
|
|
5714
|
+
clone.restore(source.snapshot());
|
|
5715
|
+
return clone;
|
|
5716
|
+
}
|
|
5717
|
+
function materializeIntegrationOps(ops, cache, tailHash) {
|
|
5718
|
+
if (cache && cache.tailHash === tailHash) {
|
|
5719
|
+
return {
|
|
5720
|
+
store: cache.store,
|
|
5721
|
+
cache,
|
|
5722
|
+
stats: {
|
|
5723
|
+
integrationOpsReplayed: 0,
|
|
5724
|
+
laneOpsReplayed: 0,
|
|
5725
|
+
integrationCacheHit: true,
|
|
5726
|
+
integrationTailHash: tailHash
|
|
5476
5727
|
}
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
writeFileSync3(filePath + ".corrupted", raw);
|
|
5492
|
-
writeFileSync3(filePath, fixed);
|
|
5493
|
-
return { recovered: ops.length, lost: 0 };
|
|
5494
|
-
} catch {
|
|
5495
|
-
writeFileSync3(filePath + ".corrupted", raw);
|
|
5496
|
-
writeFileSync3(filePath, "[]");
|
|
5497
|
-
return { recovered: 0, lost: -1 };
|
|
5728
|
+
};
|
|
5729
|
+
}
|
|
5730
|
+
const store = new EAVStore;
|
|
5731
|
+
for (const op of ops) {
|
|
5732
|
+
replayOpIntoStore2(store, op);
|
|
5733
|
+
}
|
|
5734
|
+
return {
|
|
5735
|
+
store,
|
|
5736
|
+
cache: { tailHash, store },
|
|
5737
|
+
stats: {
|
|
5738
|
+
integrationOpsReplayed: ops.length,
|
|
5739
|
+
laneOpsReplayed: 0,
|
|
5740
|
+
integrationCacheHit: false,
|
|
5741
|
+
integrationTailHash: tailHash
|
|
5498
5742
|
}
|
|
5743
|
+
};
|
|
5744
|
+
}
|
|
5745
|
+
function overlayLaneOps(integrationStore, laneOps) {
|
|
5746
|
+
const store = cloneStore(integrationStore);
|
|
5747
|
+
for (const op of laneOps) {
|
|
5748
|
+
replayOpIntoStore2(store, op);
|
|
5499
5749
|
}
|
|
5750
|
+
return { store, laneOpsReplayed: laneOps.length };
|
|
5500
5751
|
}
|
|
5752
|
+
function materializeChildForkEntry(integrationOps, baseOpHash, parentLaneOps, childLaneOps) {
|
|
5753
|
+
const store = new EAVStore;
|
|
5754
|
+
let integrationReplayed = 0;
|
|
5755
|
+
for (const op of integrationOps) {
|
|
5756
|
+
replayOpIntoStore2(store, op);
|
|
5757
|
+
integrationReplayed++;
|
|
5758
|
+
if (op.hash === baseOpHash)
|
|
5759
|
+
break;
|
|
5760
|
+
}
|
|
5761
|
+
for (const op of parentLaneOps) {
|
|
5762
|
+
replayOpIntoStore2(store, op);
|
|
5763
|
+
}
|
|
5764
|
+
for (const op of childLaneOps) {
|
|
5765
|
+
replayOpIntoStore2(store, op);
|
|
5766
|
+
}
|
|
5767
|
+
return {
|
|
5768
|
+
store,
|
|
5769
|
+
stats: {
|
|
5770
|
+
integrationOpsReplayed: integrationReplayed,
|
|
5771
|
+
laneOpsReplayed: parentLaneOps.length + childLaneOps.length,
|
|
5772
|
+
integrationCacheHit: false,
|
|
5773
|
+
integrationTailHash: integrationOps[integrationOps.length - 1]?.hash
|
|
5774
|
+
}
|
|
5775
|
+
};
|
|
5776
|
+
}
|
|
5777
|
+
var init_lane_materialize = __esm(() => {
|
|
5778
|
+
init_eav_store();
|
|
5779
|
+
init_decompose();
|
|
5780
|
+
});
|
|
5781
|
+
|
|
5782
|
+
// src/engine.ts
|
|
5783
|
+
import {
|
|
5784
|
+
existsSync as existsSync4,
|
|
5785
|
+
mkdirSync as mkdirSync3,
|
|
5786
|
+
readFileSync as readFileSync4,
|
|
5787
|
+
writeFileSync as writeFileSync3
|
|
5788
|
+
} from "fs";
|
|
5789
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
5790
|
+
import { join as join5 } from "path";
|
|
5501
5791
|
function parseIgnoreFile(filePath) {
|
|
5502
5792
|
if (!existsSync4(filePath))
|
|
5503
5793
|
return [];
|
|
@@ -5551,6 +5841,10 @@ class TrellisVcsEngine {
|
|
|
5551
5841
|
checkpointThreshold = 100;
|
|
5552
5842
|
_pendingAutoCheckpoint = false;
|
|
5553
5843
|
_blobStore = null;
|
|
5844
|
+
activeLaneId;
|
|
5845
|
+
activeLaneLog = null;
|
|
5846
|
+
integrationCache = null;
|
|
5847
|
+
materializationStats = emptyMaterializationStats();
|
|
5554
5848
|
constructor(opts) {
|
|
5555
5849
|
const gitignorePatterns = readIgnorePatterns(opts.rootPath);
|
|
5556
5850
|
const mergedIgnore = [
|
|
@@ -5595,7 +5889,7 @@ class TrellisVcsEngine {
|
|
|
5595
5889
|
branchName: this.config.defaultBranch
|
|
5596
5890
|
}
|
|
5597
5891
|
});
|
|
5598
|
-
this.applyOp(branchOp);
|
|
5892
|
+
await this.applyOp(branchOp);
|
|
5599
5893
|
const scanner = new FileWatcher({
|
|
5600
5894
|
rootPath: this.config.rootPath,
|
|
5601
5895
|
ignorePatterns: [...this.config.ignorePatterns, ".trellis"],
|
|
@@ -5639,7 +5933,7 @@ class TrellisVcsEngine {
|
|
|
5639
5933
|
size: event.size
|
|
5640
5934
|
}
|
|
5641
5935
|
});
|
|
5642
|
-
this.applyOp(op);
|
|
5936
|
+
await this.applyOp(op);
|
|
5643
5937
|
opsCreated++;
|
|
5644
5938
|
const scannedFiles = opsCreated - 1;
|
|
5645
5939
|
if (scannedFiles % 25 === 0 || scannedFiles === events.length) {
|
|
@@ -5688,16 +5982,19 @@ class TrellisVcsEngine {
|
|
|
5688
5982
|
this.config.defaultBranch = persisted.defaultBranch;
|
|
5689
5983
|
}
|
|
5690
5984
|
this.loadCurrentBranch();
|
|
5691
|
-
const
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5985
|
+
const integrationOps = this.opLog.readAll();
|
|
5986
|
+
const laneOps = this.activeLaneLog ? this.activeLaneLog.readAll() : undefined;
|
|
5987
|
+
const activeMeta = this.activeLaneId ? this.getLaneMeta(this.activeLaneId) : undefined;
|
|
5988
|
+
this.refreshMaterializedStore(integrationOps, laneOps, activeMeta);
|
|
5989
|
+
const laneReplayed = this.materializationStats.laneOpsReplayed;
|
|
5990
|
+
return {
|
|
5991
|
+
opsReplayed: this.materializationStats.integrationOpsReplayed + laneReplayed
|
|
5992
|
+
};
|
|
5696
5993
|
}
|
|
5697
5994
|
watch() {
|
|
5698
5995
|
this.ingestion = new Ingestion({
|
|
5699
5996
|
agentId: this.agentId,
|
|
5700
|
-
lastOpHash: this.
|
|
5997
|
+
lastOpHash: this.getActiveJournal().getLastOp()?.hash,
|
|
5701
5998
|
onOp: (op) => this.applyOp(op)
|
|
5702
5999
|
});
|
|
5703
6000
|
this.watcher = new FileWatcher({
|
|
@@ -5802,7 +6099,11 @@ class TrellisVcsEngine {
|
|
|
5802
6099
|
switchBranch(name) {
|
|
5803
6100
|
switchBranch(this._ctx(), name);
|
|
5804
6101
|
this.currentBranch = name;
|
|
5805
|
-
|
|
6102
|
+
const state = loadBranchState(this.config.rootPath);
|
|
6103
|
+
saveBranchState(this.config.rootPath, {
|
|
6104
|
+
...state,
|
|
6105
|
+
currentBranch: name
|
|
6106
|
+
});
|
|
5806
6107
|
}
|
|
5807
6108
|
listBranches() {
|
|
5808
6109
|
return listBranches(this._ctx(), this.currentBranch);
|
|
@@ -5815,6 +6116,344 @@ class TrellisVcsEngine {
|
|
|
5815
6116
|
getCurrentBranch() {
|
|
5816
6117
|
return this.currentBranch;
|
|
5817
6118
|
}
|
|
6119
|
+
getBranchHeadOpHash(branchName = this.currentBranch) {
|
|
6120
|
+
return getBranchHeadOpHash(this._ctx(), branchName);
|
|
6121
|
+
}
|
|
6122
|
+
getActiveLaneId() {
|
|
6123
|
+
return this.activeLaneId;
|
|
6124
|
+
}
|
|
6125
|
+
getMaterializationStats() {
|
|
6126
|
+
return { ...this.materializationStats };
|
|
6127
|
+
}
|
|
6128
|
+
listLanes() {
|
|
6129
|
+
return listLaneMetas(this.trellisDir());
|
|
6130
|
+
}
|
|
6131
|
+
getIntegrationOpCount() {
|
|
6132
|
+
return this.opLog.count();
|
|
6133
|
+
}
|
|
6134
|
+
getLaneOpCount(laneId) {
|
|
6135
|
+
const log = new LaneOpLog(laneDir(this.trellisDir(), laneId));
|
|
6136
|
+
log.load();
|
|
6137
|
+
return log.count();
|
|
6138
|
+
}
|
|
6139
|
+
getLaneMeta(laneId) {
|
|
6140
|
+
return loadLaneMeta(this.trellisDir(), laneId);
|
|
6141
|
+
}
|
|
6142
|
+
findLaneForIssue(issueId) {
|
|
6143
|
+
const normalized = issueId.startsWith("issue:") ? issueId : `issue:${issueId}`;
|
|
6144
|
+
return this.listLanes().find((lane) => lane.issueId === normalized && lane.status === "active");
|
|
6145
|
+
}
|
|
6146
|
+
async syncEnvLaneFromEnv() {
|
|
6147
|
+
const laneId = process.env.TRELLIS_LANE_ID?.trim();
|
|
6148
|
+
if (!laneId)
|
|
6149
|
+
return;
|
|
6150
|
+
if (this.activeLaneId === laneId)
|
|
6151
|
+
return;
|
|
6152
|
+
if (this.activeLaneId) {
|
|
6153
|
+
throw new Error(`TRELLIS_LANE_ID=${laneId} conflicts with active lane '${this.activeLaneId}'`);
|
|
6154
|
+
}
|
|
6155
|
+
await this.enterLane(laneId);
|
|
6156
|
+
}
|
|
6157
|
+
summarizeLane(laneId) {
|
|
6158
|
+
const meta = this.getLaneMeta(laneId);
|
|
6159
|
+
if (!meta) {
|
|
6160
|
+
throw new Error(`Lane not found: ${laneId}`);
|
|
6161
|
+
}
|
|
6162
|
+
const log = new LaneOpLog(laneDir(this.trellisDir(), laneId));
|
|
6163
|
+
log.load();
|
|
6164
|
+
const ops = log.readAll();
|
|
6165
|
+
const filePaths = [
|
|
6166
|
+
...new Set(ops.map((op) => op.vcs?.filePath ?? op.vcs?.oldFilePath).filter((p) => Boolean(p)))
|
|
6167
|
+
];
|
|
6168
|
+
return {
|
|
6169
|
+
meta,
|
|
6170
|
+
ops,
|
|
6171
|
+
filePaths,
|
|
6172
|
+
integrationHead: this.getBranchHeadOpHash(meta.targetBranch)
|
|
6173
|
+
};
|
|
6174
|
+
}
|
|
6175
|
+
async createLane(opts) {
|
|
6176
|
+
if (this.activeLaneId) {
|
|
6177
|
+
throw new Error(`Cannot create a lane while inside lane '${this.activeLaneId}' \u2014 leave first`);
|
|
6178
|
+
}
|
|
6179
|
+
const baseBranch = opts?.fromBranch ?? this.currentBranch;
|
|
6180
|
+
const baseOpHash = getBranchHeadOpHash(this._ctx(), baseBranch) ?? this.opLog.getLastOp()?.hash;
|
|
6181
|
+
if (!baseOpHash) {
|
|
6182
|
+
throw new Error(`No integration head on branch '${baseBranch}' to fork lane from`);
|
|
6183
|
+
}
|
|
6184
|
+
const meta = createLaneMeta(this.trellisDir(), {
|
|
6185
|
+
baseBranch,
|
|
6186
|
+
baseOpHash,
|
|
6187
|
+
targetBranch: opts?.targetBranch ?? baseBranch,
|
|
6188
|
+
agentId: this.agentId,
|
|
6189
|
+
issueId: opts?.issueId,
|
|
6190
|
+
sessionId: opts?.sessionId,
|
|
6191
|
+
worktreePath: opts?.worktreePath
|
|
6192
|
+
});
|
|
6193
|
+
const op = await createVcsOp("vcs:laneCreate", {
|
|
6194
|
+
agentId: this.agentId,
|
|
6195
|
+
previousHash: this.opLog.getLastOp()?.hash,
|
|
6196
|
+
vcs: {
|
|
6197
|
+
laneId: meta.id,
|
|
6198
|
+
baseBranch: meta.baseBranch,
|
|
6199
|
+
baseOpHash: meta.baseOpHash,
|
|
6200
|
+
targetBranch: meta.targetBranch,
|
|
6201
|
+
issueId: meta.issueId
|
|
6202
|
+
}
|
|
6203
|
+
});
|
|
6204
|
+
await this.applyOp(op);
|
|
6205
|
+
return meta;
|
|
6206
|
+
}
|
|
6207
|
+
async forkLane(parentLaneId, opts) {
|
|
6208
|
+
if (this.activeLaneId) {
|
|
6209
|
+
throw new Error(`Cannot fork a lane while inside lane '${this.activeLaneId}' \u2014 leave first`);
|
|
6210
|
+
}
|
|
6211
|
+
const parent = loadLaneMeta(this.trellisDir(), parentLaneId);
|
|
6212
|
+
if (!parent) {
|
|
6213
|
+
throw new Error(`Lane not found: ${parentLaneId}`);
|
|
6214
|
+
}
|
|
6215
|
+
if (parent.status !== "active") {
|
|
6216
|
+
throw new Error(`Lane '${parentLaneId}' is ${parent.status} \u2014 cannot fork`);
|
|
6217
|
+
}
|
|
6218
|
+
const forkKind = opts?.forkKind ?? "sibling";
|
|
6219
|
+
const forkedAt = new Date().toISOString();
|
|
6220
|
+
const parentLog = new LaneOpLog(laneDir(this.trellisDir(), parentLaneId));
|
|
6221
|
+
parentLog.load();
|
|
6222
|
+
const parentLaneOps = parentLog.readAll();
|
|
6223
|
+
const parentHead = resolveLaneHeadFromJournal(parent, parentLaneOps);
|
|
6224
|
+
if (forkKind === "child") {
|
|
6225
|
+
const meta2 = createLaneMeta(this.trellisDir(), {
|
|
6226
|
+
baseBranch: parent.baseBranch,
|
|
6227
|
+
baseOpHash: parent.baseOpHash,
|
|
6228
|
+
targetBranch: parent.targetBranch,
|
|
6229
|
+
agentId: this.agentId,
|
|
6230
|
+
issueId: opts?.issueId ?? parent.issueId,
|
|
6231
|
+
sessionId: opts?.sessionId,
|
|
6232
|
+
worktreePath: opts?.worktreePath,
|
|
6233
|
+
parentLaneId: parent.id,
|
|
6234
|
+
forkKind: "child",
|
|
6235
|
+
forkedAt,
|
|
6236
|
+
virtualBaseOpHash: parentHead
|
|
6237
|
+
});
|
|
6238
|
+
const op2 = await createVcsOp("vcs:laneCreate", {
|
|
6239
|
+
agentId: this.agentId,
|
|
6240
|
+
previousHash: this.opLog.getLastOp()?.hash,
|
|
6241
|
+
vcs: {
|
|
6242
|
+
laneId: meta2.id,
|
|
6243
|
+
baseBranch: meta2.baseBranch,
|
|
6244
|
+
baseOpHash: meta2.baseOpHash,
|
|
6245
|
+
targetBranch: meta2.targetBranch,
|
|
6246
|
+
issueId: meta2.issueId,
|
|
6247
|
+
sessionId: meta2.sessionId,
|
|
6248
|
+
parentLaneId: parent.id,
|
|
6249
|
+
forkKind: "child",
|
|
6250
|
+
virtualBaseOpHash: parentHead
|
|
6251
|
+
}
|
|
6252
|
+
});
|
|
6253
|
+
await this.applyOp(op2);
|
|
6254
|
+
return meta2;
|
|
6255
|
+
}
|
|
6256
|
+
const meta = createLaneMeta(this.trellisDir(), {
|
|
6257
|
+
baseBranch: parent.baseBranch,
|
|
6258
|
+
baseOpHash: parent.baseOpHash,
|
|
6259
|
+
targetBranch: parent.targetBranch,
|
|
6260
|
+
agentId: this.agentId,
|
|
6261
|
+
issueId: opts?.issueId ?? parent.issueId,
|
|
6262
|
+
sessionId: opts?.sessionId,
|
|
6263
|
+
worktreePath: opts?.worktreePath,
|
|
6264
|
+
parentLaneId: parent.id,
|
|
6265
|
+
forkKind: "sibling",
|
|
6266
|
+
forkedAt
|
|
6267
|
+
});
|
|
6268
|
+
const op = await createVcsOp("vcs:laneCreate", {
|
|
6269
|
+
agentId: this.agentId,
|
|
6270
|
+
previousHash: this.opLog.getLastOp()?.hash,
|
|
6271
|
+
vcs: {
|
|
6272
|
+
laneId: meta.id,
|
|
6273
|
+
baseBranch: meta.baseBranch,
|
|
6274
|
+
baseOpHash: meta.baseOpHash,
|
|
6275
|
+
targetBranch: meta.targetBranch,
|
|
6276
|
+
issueId: meta.issueId,
|
|
6277
|
+
sessionId: meta.sessionId,
|
|
6278
|
+
parentLaneId: parent.id,
|
|
6279
|
+
forkKind: "sibling"
|
|
6280
|
+
}
|
|
6281
|
+
});
|
|
6282
|
+
await this.applyOp(op);
|
|
6283
|
+
return meta;
|
|
6284
|
+
}
|
|
6285
|
+
async enterLane(laneId) {
|
|
6286
|
+
if (this.activeLaneId) {
|
|
6287
|
+
throw new Error(`Already in lane '${this.activeLaneId}' \u2014 leave before entering another`);
|
|
6288
|
+
}
|
|
6289
|
+
const meta = loadLaneMeta(this.trellisDir(), laneId);
|
|
6290
|
+
if (!meta) {
|
|
6291
|
+
throw new Error(`Lane not found: ${laneId}`);
|
|
6292
|
+
}
|
|
6293
|
+
if (meta.status !== "active") {
|
|
6294
|
+
throw new Error(`Lane '${laneId}' is ${meta.status} \u2014 cannot enter`);
|
|
6295
|
+
}
|
|
6296
|
+
this.activeLaneId = laneId;
|
|
6297
|
+
this.activeLaneLog = new LaneOpLog(laneDir(this.trellisDir(), laneId));
|
|
6298
|
+
this.activeLaneLog.load();
|
|
6299
|
+
this.refreshMaterializedStore(this.opLog.readAll(), this.activeLaneLog.readAll(), meta);
|
|
6300
|
+
saveBranchState(this.config.rootPath, {
|
|
6301
|
+
currentBranch: this.currentBranch,
|
|
6302
|
+
activeLaneId: laneId
|
|
6303
|
+
});
|
|
6304
|
+
this.syncIngestionLastOpHash();
|
|
6305
|
+
return meta;
|
|
6306
|
+
}
|
|
6307
|
+
async leaveLane() {
|
|
6308
|
+
if (!this.activeLaneId)
|
|
6309
|
+
return;
|
|
6310
|
+
this.activeLaneId = undefined;
|
|
6311
|
+
this.activeLaneLog = null;
|
|
6312
|
+
saveBranchState(this.config.rootPath, {
|
|
6313
|
+
currentBranch: this.currentBranch
|
|
6314
|
+
});
|
|
6315
|
+
this.restoreIntegrationOnlyStore();
|
|
6316
|
+
this.syncIngestionLastOpHash();
|
|
6317
|
+
}
|
|
6318
|
+
async dropLane(laneId) {
|
|
6319
|
+
if (this.activeLaneId === laneId) {
|
|
6320
|
+
await this.leaveLane();
|
|
6321
|
+
}
|
|
6322
|
+
const meta = loadLaneMeta(this.trellisDir(), laneId);
|
|
6323
|
+
if (!meta) {
|
|
6324
|
+
throw new Error(`Lane not found: ${laneId}`);
|
|
6325
|
+
}
|
|
6326
|
+
if (meta.status === "dropped")
|
|
6327
|
+
return;
|
|
6328
|
+
meta.status = "dropped";
|
|
6329
|
+
meta.updatedAt = new Date().toISOString();
|
|
6330
|
+
saveLaneMeta(this.trellisDir(), meta);
|
|
6331
|
+
const op = await createVcsOp("vcs:laneDrop", {
|
|
6332
|
+
agentId: this.agentId,
|
|
6333
|
+
previousHash: this.opLog.getLastOp()?.hash,
|
|
6334
|
+
vcs: {
|
|
6335
|
+
laneId: meta.id,
|
|
6336
|
+
laneStatus: "dropped"
|
|
6337
|
+
}
|
|
6338
|
+
});
|
|
6339
|
+
await this.applyOp(op);
|
|
6340
|
+
}
|
|
6341
|
+
async promoteLane(laneId, opts) {
|
|
6342
|
+
const meta = this.getLaneMeta(laneId);
|
|
6343
|
+
if (!meta) {
|
|
6344
|
+
throw new Error(`Lane not found: ${laneId}`);
|
|
6345
|
+
}
|
|
6346
|
+
if (meta.status !== "active") {
|
|
6347
|
+
throw new Error(`Lane '${laneId}' is ${meta.status} \u2014 cannot promote`);
|
|
6348
|
+
}
|
|
6349
|
+
if (this.activeLaneId === laneId) {
|
|
6350
|
+
await this.leaveLane();
|
|
6351
|
+
} else if (this.activeLaneId) {
|
|
6352
|
+
throw new Error(`Cannot promote while inside lane '${this.activeLaneId}' \u2014 leave first`);
|
|
6353
|
+
}
|
|
6354
|
+
const targetBranch = opts?.toBranch ?? meta.targetBranch;
|
|
6355
|
+
const integrationOps = this.opLog.readAll();
|
|
6356
|
+
const snapshotHead = resolveBranchHeadFromOps(integrationOps, targetBranch) ?? getBranchHeadOpHash(this._ctx(), targetBranch);
|
|
6357
|
+
if (!snapshotHead) {
|
|
6358
|
+
throw new Error(`No head on branch '${targetBranch}' to promote onto`);
|
|
6359
|
+
}
|
|
6360
|
+
const laneLog = new LaneOpLog(laneDir(this.trellisDir(), laneId));
|
|
6361
|
+
laneLog.load();
|
|
6362
|
+
const laneOps = laneLog.readAll();
|
|
6363
|
+
let parentLaneOps;
|
|
6364
|
+
if (meta.forkKind === "child" && meta.parentLaneId) {
|
|
6365
|
+
parentLaneOps = this.loadLaneJournalOps(meta.parentLaneId);
|
|
6366
|
+
}
|
|
6367
|
+
const plan = await planLanePromote({
|
|
6368
|
+
laneId,
|
|
6369
|
+
meta,
|
|
6370
|
+
targetBranch,
|
|
6371
|
+
snapshotHead,
|
|
6372
|
+
integrationOps: this.opLog.readAll(),
|
|
6373
|
+
laneOps,
|
|
6374
|
+
parentLaneOps,
|
|
6375
|
+
blobStore: this._blobStore
|
|
6376
|
+
});
|
|
6377
|
+
if (opts?.dryRun || !plan.canPromote) {
|
|
6378
|
+
return { ...plan, promoted: false };
|
|
6379
|
+
}
|
|
6380
|
+
meta.status = "promoting";
|
|
6381
|
+
meta.updatedAt = new Date().toISOString();
|
|
6382
|
+
saveLaneMeta(this.trellisDir(), meta);
|
|
6383
|
+
const startOp = await createVcsOp("vcs:lanePromoteStart", {
|
|
6384
|
+
agentId: this.agentId,
|
|
6385
|
+
previousHash: this.opLog.getLastOp()?.hash,
|
|
6386
|
+
vcs: {
|
|
6387
|
+
laneId,
|
|
6388
|
+
targetBranch,
|
|
6389
|
+
baseOpHash: meta.baseOpHash
|
|
6390
|
+
}
|
|
6391
|
+
});
|
|
6392
|
+
await this.applyOp(startOp, { skipBranchAdvance: true });
|
|
6393
|
+
const currentHead = resolveBranchHeadFromOps(this.opLog.readAll(), targetBranch) ?? getBranchHeadOpHash(this._ctx(), targetBranch);
|
|
6394
|
+
if (currentHead !== snapshotHead) {
|
|
6395
|
+
meta.status = "active";
|
|
6396
|
+
meta.updatedAt = new Date().toISOString();
|
|
6397
|
+
saveLaneMeta(this.trellisDir(), meta);
|
|
6398
|
+
const abortOp = await createVcsOp("vcs:lanePromoteAbort", {
|
|
6399
|
+
agentId: this.agentId,
|
|
6400
|
+
previousHash: this.opLog.getLastOp()?.hash,
|
|
6401
|
+
vcs: { laneId }
|
|
6402
|
+
});
|
|
6403
|
+
await this.applyOp(abortOp, { skipBranchAdvance: true });
|
|
6404
|
+
throw new Error(`Integration head moved during promote \u2014 retry after integration settles`);
|
|
6405
|
+
}
|
|
6406
|
+
let previousHash = this.opLog.getLastOp()?.hash;
|
|
6407
|
+
let lastReplayedHash;
|
|
6408
|
+
let opsAppended = 0;
|
|
6409
|
+
for (const action of plan.opsToReplay) {
|
|
6410
|
+
let opToApply;
|
|
6411
|
+
if (action.mergedContent !== undefined && action.sourceOp.vcs?.filePath) {
|
|
6412
|
+
const contentHash = await this._blobStore.put(Buffer.from(action.mergedContent, "utf-8"));
|
|
6413
|
+
opToApply = await createVcsOp("vcs:fileModify", {
|
|
6414
|
+
agentId: action.sourceOp.agentId,
|
|
6415
|
+
previousHash,
|
|
6416
|
+
vcs: {
|
|
6417
|
+
filePath: action.sourceOp.vcs.filePath,
|
|
6418
|
+
contentHash,
|
|
6419
|
+
laneId: action.sourceOp.vcs.laneId ?? laneId
|
|
6420
|
+
}
|
|
6421
|
+
});
|
|
6422
|
+
} else {
|
|
6423
|
+
opToApply = await rechainOpForIntegration(action.sourceOp, previousHash);
|
|
6424
|
+
}
|
|
6425
|
+
await this.applyOp(opToApply, { skipBranchAdvance: true });
|
|
6426
|
+
previousHash = opToApply.hash;
|
|
6427
|
+
lastReplayedHash = opToApply.hash;
|
|
6428
|
+
opsAppended++;
|
|
6429
|
+
}
|
|
6430
|
+
if (lastReplayedHash) {
|
|
6431
|
+
await this.appendBranchAdvance(lastReplayedHash);
|
|
6432
|
+
}
|
|
6433
|
+
const completeOp = await createVcsOp("vcs:lanePromoteComplete", {
|
|
6434
|
+
agentId: this.agentId,
|
|
6435
|
+
previousHash: this.opLog.getLastOp()?.hash,
|
|
6436
|
+
vcs: {
|
|
6437
|
+
laneId,
|
|
6438
|
+
targetBranch,
|
|
6439
|
+
laneStatus: "promoted"
|
|
6440
|
+
}
|
|
6441
|
+
});
|
|
6442
|
+
await this.applyOp(completeOp, { skipBranchAdvance: true });
|
|
6443
|
+
meta.status = "promoted";
|
|
6444
|
+
meta.headOpHash = lastReplayedHash ?? meta.headOpHash;
|
|
6445
|
+
meta.updatedAt = new Date().toISOString();
|
|
6446
|
+
saveLaneMeta(this.trellisDir(), meta);
|
|
6447
|
+
this.invalidateIntegrationCache();
|
|
6448
|
+
this.refreshMaterializedStore(this.opLog.readAll());
|
|
6449
|
+
this.syncIngestionLastOpHash();
|
|
6450
|
+
return {
|
|
6451
|
+
...plan,
|
|
6452
|
+
promoted: true,
|
|
6453
|
+
integrationOpsAppended: opsAppended + 2,
|
|
6454
|
+
completeOpHash: completeOp.hash
|
|
6455
|
+
};
|
|
6456
|
+
}
|
|
5818
6457
|
async createMilestone(message, opts) {
|
|
5819
6458
|
const op = await createMilestone(this._ctx(), message, opts);
|
|
5820
6459
|
await this.flushAutoCheckpoint();
|
|
@@ -5947,7 +6586,10 @@ class TrellisVcsEngine {
|
|
|
5947
6586
|
await this.flushAutoCheckpoint();
|
|
5948
6587
|
return op;
|
|
5949
6588
|
}
|
|
5950
|
-
async startIssue(id) {
|
|
6589
|
+
async startIssue(id, opts) {
|
|
6590
|
+
if (this.activeLaneId) {
|
|
6591
|
+
await this.leaveLane();
|
|
6592
|
+
}
|
|
5951
6593
|
const issue = getIssue(this._ctx(), id);
|
|
5952
6594
|
if (!issue)
|
|
5953
6595
|
throw new Error(`Issue ${id} not found.`);
|
|
@@ -5956,16 +6598,27 @@ class TrellisVcsEngine {
|
|
|
5956
6598
|
await this.createBranch(branchName);
|
|
5957
6599
|
const op = await startIssue(this._ctx(), id, branchName);
|
|
5958
6600
|
this.switchBranch(branchName);
|
|
6601
|
+
if (opts?.lane !== false) {
|
|
6602
|
+
const issueKey = id.startsWith("issue:") ? id : `issue:${id}`;
|
|
6603
|
+
let lane = this.findLaneForIssue(issueKey);
|
|
6604
|
+
if (!lane) {
|
|
6605
|
+
lane = await this.createLane({ issueId: issueKey });
|
|
6606
|
+
}
|
|
6607
|
+
await this.enterLane(lane.id);
|
|
6608
|
+
}
|
|
5959
6609
|
await this.flushAutoCheckpoint();
|
|
5960
6610
|
return op;
|
|
5961
6611
|
}
|
|
5962
6612
|
async pauseIssue(id, note) {
|
|
6613
|
+
if (this.activeLaneId) {
|
|
6614
|
+
await this.leaveLane();
|
|
6615
|
+
}
|
|
5963
6616
|
const op = await pauseIssue(this._ctx(), id, note);
|
|
5964
6617
|
this.switchBranch(this.config.defaultBranch);
|
|
5965
6618
|
await this.flushAutoCheckpoint();
|
|
5966
6619
|
return op;
|
|
5967
6620
|
}
|
|
5968
|
-
async resumeIssue(id) {
|
|
6621
|
+
async resumeIssue(id, opts) {
|
|
5969
6622
|
const issue = getIssue(this._ctx(), id);
|
|
5970
6623
|
if (!issue)
|
|
5971
6624
|
throw new Error(`Issue ${id} not found.`);
|
|
@@ -5973,10 +6626,20 @@ class TrellisVcsEngine {
|
|
|
5973
6626
|
throw new Error(`Issue ${id} has no tracked branch.`);
|
|
5974
6627
|
const op = await resumeIssue(this._ctx(), id);
|
|
5975
6628
|
this.switchBranch(issue.branchName);
|
|
6629
|
+
if (opts?.lane !== false) {
|
|
6630
|
+
const issueKey = id.startsWith("issue:") ? id : `issue:${id}`;
|
|
6631
|
+
const lane = this.findLaneForIssue(issueKey);
|
|
6632
|
+
if (lane) {
|
|
6633
|
+
await this.enterLane(lane.id);
|
|
6634
|
+
}
|
|
6635
|
+
}
|
|
5976
6636
|
await this.flushAutoCheckpoint();
|
|
5977
6637
|
return op;
|
|
5978
6638
|
}
|
|
5979
6639
|
async closeIssue(id, opts) {
|
|
6640
|
+
if (this.activeLaneId) {
|
|
6641
|
+
await this.leaveLane();
|
|
6642
|
+
}
|
|
5980
6643
|
const result = await closeIssue(this._ctx(), id, opts);
|
|
5981
6644
|
if (result.op) {
|
|
5982
6645
|
await this.flushAutoCheckpoint();
|
|
@@ -6051,13 +6714,106 @@ class TrellisVcsEngine {
|
|
|
6051
6714
|
return {
|
|
6052
6715
|
store: this.store,
|
|
6053
6716
|
agentId: this.agentId,
|
|
6054
|
-
readAllOps: () => this.
|
|
6055
|
-
getLastOp: () => this.
|
|
6056
|
-
applyOp: (op) => this.applyOp(op)
|
|
6717
|
+
readAllOps: () => this.getActiveJournal().readAll(),
|
|
6718
|
+
getLastOp: () => this.getActiveJournal().getLastOp(),
|
|
6719
|
+
applyOp: (op, opts) => this.applyOp(op, opts)
|
|
6057
6720
|
};
|
|
6058
6721
|
}
|
|
6059
|
-
|
|
6060
|
-
|
|
6722
|
+
trellisDir() {
|
|
6723
|
+
return join5(this.config.rootPath, ".trellis");
|
|
6724
|
+
}
|
|
6725
|
+
getActiveJournal() {
|
|
6726
|
+
if (this.activeLaneId && this.activeLaneLog) {
|
|
6727
|
+
return this.activeLaneLog;
|
|
6728
|
+
}
|
|
6729
|
+
return this.opLog;
|
|
6730
|
+
}
|
|
6731
|
+
invalidateIntegrationCache() {
|
|
6732
|
+
this.integrationCache = null;
|
|
6733
|
+
}
|
|
6734
|
+
loadLaneJournalOps(laneId) {
|
|
6735
|
+
const log = new LaneOpLog(laneDir(this.trellisDir(), laneId));
|
|
6736
|
+
log.load();
|
|
6737
|
+
return log.readAll();
|
|
6738
|
+
}
|
|
6739
|
+
refreshMaterializedStore(integrationOps, laneOps, meta) {
|
|
6740
|
+
const laneMeta = meta ?? (this.activeLaneId ? this.getLaneMeta(this.activeLaneId) : undefined);
|
|
6741
|
+
if (laneMeta?.forkKind === "child" && laneMeta.parentLaneId) {
|
|
6742
|
+
const parentLaneOps = this.loadLaneJournalOps(laneMeta.parentLaneId);
|
|
6743
|
+
const { store: store2, stats: stats2 } = materializeChildForkEntry(integrationOps, laneMeta.baseOpHash, parentLaneOps, laneOps ?? []);
|
|
6744
|
+
this.store = store2;
|
|
6745
|
+
this.materializationStats = stats2;
|
|
6746
|
+
return;
|
|
6747
|
+
}
|
|
6748
|
+
const tailHash = integrationOps[integrationOps.length - 1]?.hash;
|
|
6749
|
+
const { store, cache, stats } = materializeIntegrationOps(integrationOps, this.integrationCache, tailHash);
|
|
6750
|
+
this.integrationCache = cache;
|
|
6751
|
+
if (laneOps !== undefined) {
|
|
6752
|
+
const overlay = overlayLaneOps(store, laneOps);
|
|
6753
|
+
this.store = overlay.store;
|
|
6754
|
+
this.materializationStats = {
|
|
6755
|
+
...stats,
|
|
6756
|
+
laneOpsReplayed: overlay.laneOpsReplayed
|
|
6757
|
+
};
|
|
6758
|
+
return;
|
|
6759
|
+
}
|
|
6760
|
+
this.store = store;
|
|
6761
|
+
this.materializationStats = stats;
|
|
6762
|
+
}
|
|
6763
|
+
restoreIntegrationOnlyStore() {
|
|
6764
|
+
const integrationOps = this.opLog.readAll();
|
|
6765
|
+
const tailHash = integrationOps[integrationOps.length - 1]?.hash;
|
|
6766
|
+
const { store, cache, stats } = materializeIntegrationOps(integrationOps, this.integrationCache, tailHash);
|
|
6767
|
+
this.integrationCache = cache;
|
|
6768
|
+
this.store = store;
|
|
6769
|
+
this.materializationStats = {
|
|
6770
|
+
...stats,
|
|
6771
|
+
laneOpsReplayed: 0
|
|
6772
|
+
};
|
|
6773
|
+
}
|
|
6774
|
+
rebuildStore(ops) {
|
|
6775
|
+
this.store = new EAVStore;
|
|
6776
|
+
for (const op of ops) {
|
|
6777
|
+
this.replayOp(op);
|
|
6778
|
+
}
|
|
6779
|
+
}
|
|
6780
|
+
syncIngestionLastOpHash() {
|
|
6781
|
+
if (this.ingestion) {
|
|
6782
|
+
this.ingestion.setLastOpHash(this.getActiveJournal().getLastOp()?.hash);
|
|
6783
|
+
}
|
|
6784
|
+
}
|
|
6785
|
+
stampLaneId(op) {
|
|
6786
|
+
if (!this.activeLaneId)
|
|
6787
|
+
return;
|
|
6788
|
+
op.vcs = { ...op.vcs, laneId: this.activeLaneId };
|
|
6789
|
+
}
|
|
6790
|
+
requireActiveLaneLog() {
|
|
6791
|
+
if (!this.activeLaneId || !this.activeLaneLog) {
|
|
6792
|
+
throw new Error("No active lane journal");
|
|
6793
|
+
}
|
|
6794
|
+
return this.activeLaneLog;
|
|
6795
|
+
}
|
|
6796
|
+
isIssueIntegrationOp(kind) {
|
|
6797
|
+
return ISSUE_INTEGRATION_KINDS.has(kind);
|
|
6798
|
+
}
|
|
6799
|
+
async applyOp(op, opts) {
|
|
6800
|
+
const inLane = Boolean(this.activeLaneId);
|
|
6801
|
+
const forceIntegration = Boolean(opts?.allowIntegrationWrite) || inLane && this.isIssueIntegrationOp(op.kind);
|
|
6802
|
+
let opToApply = op;
|
|
6803
|
+
if (inLane && forceIntegration) {
|
|
6804
|
+
const intLast = this.opLog.getLastOp();
|
|
6805
|
+
if (intLast?.hash !== op.previousHash && isVcsOpKind(op.kind)) {
|
|
6806
|
+
opToApply = await createVcsOp(op.kind, {
|
|
6807
|
+
agentId: op.agentId,
|
|
6808
|
+
previousHash: intLast?.hash,
|
|
6809
|
+
vcs: op.vcs ?? {}
|
|
6810
|
+
});
|
|
6811
|
+
}
|
|
6812
|
+
}
|
|
6813
|
+
if (inLane && !forceIntegration) {
|
|
6814
|
+
this.stampLaneId(opToApply);
|
|
6815
|
+
}
|
|
6816
|
+
const decomposed = decompose(opToApply);
|
|
6061
6817
|
if (decomposed.deleteFacts.length > 0) {
|
|
6062
6818
|
this.store.deleteFacts(decomposed.deleteFacts);
|
|
6063
6819
|
}
|
|
@@ -6070,13 +6826,55 @@ class TrellisVcsEngine {
|
|
|
6070
6826
|
if (decomposed.addLinks.length > 0) {
|
|
6071
6827
|
this.store.addLinks(decomposed.addLinks);
|
|
6072
6828
|
}
|
|
6073
|
-
|
|
6074
|
-
|
|
6829
|
+
if (inLane && !forceIntegration) {
|
|
6830
|
+
const laneLog = this.requireActiveLaneLog();
|
|
6831
|
+
laneLog.append(opToApply);
|
|
6832
|
+
updateLaneHead(this.trellisDir(), this.activeLaneId, opToApply.hash);
|
|
6833
|
+
if (opToApply.kind !== "vcs:checkpointCreate" && this.checkpointThreshold > 0) {
|
|
6834
|
+
this.checkpointOpCount++;
|
|
6835
|
+
if (this.checkpointOpCount >= this.checkpointThreshold) {
|
|
6836
|
+
this._pendingAutoCheckpoint = true;
|
|
6837
|
+
}
|
|
6838
|
+
}
|
|
6839
|
+
return;
|
|
6840
|
+
}
|
|
6841
|
+
this.opLog.append(opToApply);
|
|
6842
|
+
if (inLane && forceIntegration) {
|
|
6843
|
+
const meta = this.getLaneMeta(this.activeLaneId);
|
|
6844
|
+
this.refreshMaterializedStore(this.opLog.readAll(), this.activeLaneLog.readAll(), meta);
|
|
6845
|
+
} else if (!inLane) {
|
|
6846
|
+
if (!this.integrationCache) {
|
|
6847
|
+
this.integrationCache = {
|
|
6848
|
+
tailHash: opToApply.hash,
|
|
6849
|
+
store: this.store
|
|
6850
|
+
};
|
|
6851
|
+
} else {
|
|
6852
|
+
this.integrationCache.tailHash = opToApply.hash;
|
|
6853
|
+
}
|
|
6854
|
+
}
|
|
6855
|
+
if (opToApply.kind !== "vcs:checkpointCreate" && this.checkpointThreshold > 0) {
|
|
6075
6856
|
this.checkpointOpCount++;
|
|
6076
6857
|
if (this.checkpointOpCount >= this.checkpointThreshold) {
|
|
6077
6858
|
this._pendingAutoCheckpoint = true;
|
|
6078
6859
|
}
|
|
6079
6860
|
}
|
|
6861
|
+
if (!opts?.skipBranchAdvance && shouldAdvanceBranchHead(opToApply.kind)) {
|
|
6862
|
+
await this.appendBranchAdvance(opToApply.hash);
|
|
6863
|
+
}
|
|
6864
|
+
}
|
|
6865
|
+
async appendBranchAdvance(targetOpHash) {
|
|
6866
|
+
const advanceOp = await createVcsOp("vcs:branchAdvance", {
|
|
6867
|
+
agentId: this.agentId,
|
|
6868
|
+
previousHash: this.opLog.getLastOp()?.hash,
|
|
6869
|
+
vcs: {
|
|
6870
|
+
branchName: this.currentBranch,
|
|
6871
|
+
targetOpHash
|
|
6872
|
+
}
|
|
6873
|
+
});
|
|
6874
|
+
await this.applyOp(advanceOp, {
|
|
6875
|
+
skipBranchAdvance: true,
|
|
6876
|
+
allowIntegrationWrite: true
|
|
6877
|
+
});
|
|
6080
6878
|
}
|
|
6081
6879
|
async flushAutoCheckpoint() {
|
|
6082
6880
|
if (this._pendingAutoCheckpoint) {
|
|
@@ -6087,6 +6885,17 @@ class TrellisVcsEngine {
|
|
|
6087
6885
|
loadCurrentBranch() {
|
|
6088
6886
|
const state = loadBranchState(this.config.rootPath);
|
|
6089
6887
|
this.currentBranch = state.currentBranch;
|
|
6888
|
+
this.activeLaneId = state.activeLaneId;
|
|
6889
|
+
this.activeLaneLog = null;
|
|
6890
|
+
if (this.activeLaneId) {
|
|
6891
|
+
const meta = loadLaneMeta(this.trellisDir(), this.activeLaneId);
|
|
6892
|
+
if (meta && meta.status === "active") {
|
|
6893
|
+
this.activeLaneLog = new LaneOpLog(laneDir(this.trellisDir(), this.activeLaneId));
|
|
6894
|
+
this.activeLaneLog.load();
|
|
6895
|
+
} else {
|
|
6896
|
+
this.activeLaneId = undefined;
|
|
6897
|
+
}
|
|
6898
|
+
}
|
|
6090
6899
|
}
|
|
6091
6900
|
replayOp(op) {
|
|
6092
6901
|
const decomposed = decompose(op);
|
|
@@ -6104,7 +6913,7 @@ class TrellisVcsEngine {
|
|
|
6104
6913
|
}
|
|
6105
6914
|
}
|
|
6106
6915
|
}
|
|
6107
|
-
var TRELLIS_GITIGNORE_ENTRY = ".trellis/";
|
|
6916
|
+
var TRELLIS_GITIGNORE_ENTRY = ".trellis/", ISSUE_INTEGRATION_KINDS;
|
|
6108
6917
|
var init_engine = __esm(() => {
|
|
6109
6918
|
init_eav_store();
|
|
6110
6919
|
init_fs_watcher();
|
|
@@ -6125,6 +6934,21 @@ var init_engine = __esm(() => {
|
|
|
6125
6934
|
init_infer();
|
|
6126
6935
|
init_profile();
|
|
6127
6936
|
init_write();
|
|
6937
|
+
init_op_log();
|
|
6938
|
+
init_lane();
|
|
6939
|
+
init_lane_promote();
|
|
6940
|
+
init_lane_materialize();
|
|
6941
|
+
ISSUE_INTEGRATION_KINDS = new Set([
|
|
6942
|
+
"vcs:issueCreate",
|
|
6943
|
+
"vcs:issueStart",
|
|
6944
|
+
"vcs:issuePause",
|
|
6945
|
+
"vcs:issueResume",
|
|
6946
|
+
"vcs:issueClose",
|
|
6947
|
+
"vcs:issueReopen",
|
|
6948
|
+
"vcs:criterionUpdate",
|
|
6949
|
+
"vcs:issueBlock",
|
|
6950
|
+
"vcs:issueUnblock"
|
|
6951
|
+
]);
|
|
6128
6952
|
});
|
|
6129
6953
|
|
|
6130
|
-
export { FileWatcher, init_fs_watcher, Ingestion, init_ingestion, inferProjectContext, init_infer, loadProfile, saveProfile, hasProfile, promptForProfile, updateProfile, init_profile, writeAgentScaffold, writeIdeScaffold, init_write, TrellisVcsEngine, init_engine };
|
|
6954
|
+
export { FileWatcher, init_fs_watcher, Ingestion, init_ingestion, inferProjectContext, init_infer, loadProfile, saveProfile, hasProfile, promptForProfile, updateProfile, init_profile, writeAgentScaffold, writeIdeScaffold, init_write, formatPromoteExplain, init_lane_promote, TrellisVcsEngine, init_engine };
|