superlab 0.1.42 → 0.1.43
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/lib/auto_runner.cjs +264 -8
- package/lib/auto_state.cjs +114 -0
- package/lib/i18n.cjs +41 -2
- package/lib/install.cjs +1 -0
- package/package-assets/claude/commands/lab-auto.md +1 -1
- package/package-assets/claude/commands/lab.md +2 -1
- package/package-assets/codex/prompts/lab-auto.md +1 -1
- package/package-assets/codex/prompts/lab.md +2 -1
- package/package-assets/shared/lab/context/auto-ledger.md +35 -0
- package/package-assets/shared/lab/context/auto-mode.md +2 -0
- package/package-assets/shared/skills/lab/SKILL.md +3 -2
- package/package-assets/shared/skills/lab/stages/auto.md +9 -0
- package/package.json +1 -1
package/lib/auto_runner.cjs
CHANGED
|
@@ -21,10 +21,12 @@ const {
|
|
|
21
21
|
verifyStageContract,
|
|
22
22
|
} = require("./auto_contracts.cjs");
|
|
23
23
|
const {
|
|
24
|
+
parseAutoLedger,
|
|
24
25
|
parseAutoMode,
|
|
25
26
|
parseAutoStatus,
|
|
26
27
|
readWorkflowLanguage,
|
|
27
28
|
resolveRequiredArtifact,
|
|
29
|
+
writeAutoLedger,
|
|
28
30
|
writeAutoOutcome,
|
|
29
31
|
writeAutoStatus,
|
|
30
32
|
} = require("./auto_state.cjs");
|
|
@@ -33,6 +35,10 @@ function normalizeTransition(value) {
|
|
|
33
35
|
return (value || "").trim();
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
function normalizeObservedState(value) {
|
|
39
|
+
return (value || "").trim().toLowerCase();
|
|
40
|
+
}
|
|
41
|
+
|
|
36
42
|
function isSuccessTransition(value) {
|
|
37
43
|
return ["success", "terminal-success", "campaign-success"].includes((value || "").trim().toLowerCase());
|
|
38
44
|
}
|
|
@@ -41,6 +47,101 @@ function isStopTransition(value) {
|
|
|
41
47
|
return ["stop", "campaign-stop", "terminal-stop"].includes((value || "").trim().toLowerCase());
|
|
42
48
|
}
|
|
43
49
|
|
|
50
|
+
function isLocalProcessAlive(ownerId) {
|
|
51
|
+
const pid = parseInteger(ownerId, null);
|
|
52
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
process.kill(pid, 0);
|
|
57
|
+
return true;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (error && error.code === "EPERM") {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function resolveResumePlan({ mode, evalProtocol, status, ledger, now }) {
|
|
67
|
+
const hasLedgerState = [
|
|
68
|
+
ledger.campaignId,
|
|
69
|
+
ledger.observedState,
|
|
70
|
+
ledger.activeRung,
|
|
71
|
+
ledger.nextTransition,
|
|
72
|
+
ledger.ownerId,
|
|
73
|
+
].some((value) => isMeaningful(value));
|
|
74
|
+
if (!hasLedgerState) {
|
|
75
|
+
return { blockingIssue: "", resumePlan: null };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if ((ledger.ownerType || "").trim().toLowerCase() === "local-process" && isLocalProcessAlive(ledger.ownerId)) {
|
|
79
|
+
return {
|
|
80
|
+
blockingIssue: `auto campaign already has a live local owner: ${ledger.ownerId}`,
|
|
81
|
+
resumePlan: null,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const startedAt = isMeaningful(status.startedAt) ? status.startedAt : now.toISOString();
|
|
86
|
+
const campaignId = isMeaningful(ledger.campaignId)
|
|
87
|
+
? ledger.campaignId
|
|
88
|
+
: `auto-${startedAt.replace(/[:.]/g, "-")}`;
|
|
89
|
+
const iterationCount = parseInteger(status.iterationCount, 0);
|
|
90
|
+
const observedState = normalizeObservedState(ledger.observedState);
|
|
91
|
+
|
|
92
|
+
if (evalProtocol.experimentRungs.length > 0) {
|
|
93
|
+
const rungMap = new Map(evalProtocol.experimentRungs.map((rung) => [rung.id, rung]));
|
|
94
|
+
const nextTransition = normalizeTransition(ledger.nextTransition || status.nextRung);
|
|
95
|
+
if (
|
|
96
|
+
isMeaningful(nextTransition) &&
|
|
97
|
+
!isSuccessTransition(nextTransition) &&
|
|
98
|
+
!isStopTransition(nextTransition) &&
|
|
99
|
+
rungMap.has(nextTransition)
|
|
100
|
+
) {
|
|
101
|
+
const rung = rungMap.get(nextTransition);
|
|
102
|
+
return {
|
|
103
|
+
blockingIssue: "",
|
|
104
|
+
resumePlan: {
|
|
105
|
+
kind: "ladder",
|
|
106
|
+
rungId: nextTransition,
|
|
107
|
+
stage: rung.stage,
|
|
108
|
+
watchTarget: rung.watch,
|
|
109
|
+
campaignId,
|
|
110
|
+
startedAt,
|
|
111
|
+
iterationsCompleted: iterationCount,
|
|
112
|
+
lastCheckpoint: ledger.lastCheckpoint || status.lastCheckpoint || "",
|
|
113
|
+
reason: `resuming at next rung ${nextTransition}`,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const activeRung = normalizeTransition(ledger.activeRung || status.currentRung);
|
|
119
|
+
if (
|
|
120
|
+
["running", "retrying"].includes(observedState) &&
|
|
121
|
+
isMeaningful(activeRung) &&
|
|
122
|
+
rungMap.has(activeRung)
|
|
123
|
+
) {
|
|
124
|
+
const rung = rungMap.get(activeRung);
|
|
125
|
+
return {
|
|
126
|
+
blockingIssue: "",
|
|
127
|
+
resumePlan: {
|
|
128
|
+
kind: "ladder",
|
|
129
|
+
rungId: activeRung,
|
|
130
|
+
stage: rung.stage,
|
|
131
|
+
watchTarget: rung.watch,
|
|
132
|
+
campaignId,
|
|
133
|
+
startedAt,
|
|
134
|
+
iterationsCompleted: Math.max(0, iterationCount - 1),
|
|
135
|
+
lastCheckpoint: ledger.lastCheckpoint || status.lastCheckpoint || "",
|
|
136
|
+
reason: `restarting active rung ${activeRung} after owner exit`,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return { blockingIssue: "", resumePlan: null };
|
|
143
|
+
}
|
|
144
|
+
|
|
44
145
|
async function runCommandWithPolling({
|
|
45
146
|
targetDir,
|
|
46
147
|
stage,
|
|
@@ -53,6 +154,8 @@ async function runCommandWithPolling({
|
|
|
53
154
|
rungId = "",
|
|
54
155
|
watchTarget = "",
|
|
55
156
|
nextRung = "",
|
|
157
|
+
ownerInfo = null,
|
|
158
|
+
updateLedger = null,
|
|
56
159
|
}) {
|
|
57
160
|
const child = spawn(command, {
|
|
58
161
|
cwd: targetDir,
|
|
@@ -106,6 +209,20 @@ async function runCommandWithPolling({
|
|
|
106
209
|
},
|
|
107
210
|
{ lang }
|
|
108
211
|
);
|
|
212
|
+
if (typeof updateLedger === "function") {
|
|
213
|
+
updateLedger({
|
|
214
|
+
ownerType: ownerInfo?.ownerType || "local-process",
|
|
215
|
+
ownerId: String(child.pid || ownerInfo?.ownerId || ""),
|
|
216
|
+
command,
|
|
217
|
+
watchTarget,
|
|
218
|
+
activeStage: stage,
|
|
219
|
+
activeRung: rungId,
|
|
220
|
+
startedAt,
|
|
221
|
+
lastObservedAt: new Date().toISOString(),
|
|
222
|
+
observedState: "running",
|
|
223
|
+
nextTransition: nextRung || "",
|
|
224
|
+
});
|
|
225
|
+
}
|
|
109
226
|
await sleep(pollIntervalMs);
|
|
110
227
|
}
|
|
111
228
|
|
|
@@ -217,6 +334,8 @@ async function evaluateTerminalGoal({ mode, iteration, targetDir, deadlineMs })
|
|
|
217
334
|
|
|
218
335
|
async function startAutoMode({ targetDir, now = new Date() }) {
|
|
219
336
|
const mode = parseAutoMode(targetDir);
|
|
337
|
+
const existingStatus = parseAutoStatus(targetDir);
|
|
338
|
+
const existingLedger = parseAutoLedger(targetDir);
|
|
220
339
|
const evalProtocol = parseEvalProtocol(targetDir);
|
|
221
340
|
const issues = validateAutoMode(mode, null, evalProtocol);
|
|
222
341
|
if (issues.length > 0) {
|
|
@@ -229,20 +348,30 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
229
348
|
if (mode.approvalStatus !== "approved") {
|
|
230
349
|
throw new Error(`approval status must be approved before auto mode can start (current: ${mode.approvalStatus || "missing"})`);
|
|
231
350
|
}
|
|
351
|
+
const { blockingIssue, resumePlan } = resolveResumePlan({
|
|
352
|
+
mode,
|
|
353
|
+
evalProtocol,
|
|
354
|
+
status: existingStatus,
|
|
355
|
+
ledger: existingLedger,
|
|
356
|
+
now,
|
|
357
|
+
});
|
|
358
|
+
if (blockingIssue) {
|
|
359
|
+
throw new Error(blockingIssue);
|
|
360
|
+
}
|
|
232
361
|
|
|
233
362
|
const lang = readWorkflowLanguage(targetDir);
|
|
234
363
|
const timestamp = now.toISOString();
|
|
235
364
|
const status = {
|
|
236
365
|
status: "running",
|
|
237
|
-
currentStage: mode.allowedStages[0] || "run",
|
|
366
|
+
currentStage: resumePlan?.stage || mode.allowedStages[0] || "run",
|
|
238
367
|
currentCommand: "",
|
|
239
368
|
activeRunId: "",
|
|
240
|
-
iterationCount:
|
|
241
|
-
startedAt: timestamp,
|
|
369
|
+
iterationCount: String(resumePlan?.iterationsCompleted || 0),
|
|
370
|
+
startedAt: resumePlan?.startedAt || timestamp,
|
|
242
371
|
lastHeartbeat: timestamp,
|
|
243
|
-
lastCheckpoint: "",
|
|
244
|
-
lastSummary: "",
|
|
245
|
-
decision: "armed for bounded auto orchestration",
|
|
372
|
+
lastCheckpoint: resumePlan?.lastCheckpoint || "",
|
|
373
|
+
lastSummary: resumePlan?.reason || "",
|
|
374
|
+
decision: resumePlan?.reason || "armed for bounded auto orchestration",
|
|
246
375
|
};
|
|
247
376
|
writeAutoStatus(targetDir, status, { lang });
|
|
248
377
|
|
|
@@ -257,13 +386,36 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
257
386
|
const { loopStages, finalStages } = splitAutoStages(mode.allowedStages);
|
|
258
387
|
const executedStages = [];
|
|
259
388
|
let failureCount = 0;
|
|
260
|
-
let iterationsCompleted = 0;
|
|
389
|
+
let iterationsCompleted = resumePlan?.iterationsCompleted || 0;
|
|
261
390
|
let currentStatus = { ...status };
|
|
262
391
|
let successReached = false;
|
|
263
392
|
let stopMatched = false;
|
|
264
393
|
let promotionApplied = false;
|
|
265
394
|
let stopReason = "";
|
|
266
395
|
let finalRung = "";
|
|
396
|
+
const campaignId = resumePlan?.campaignId || `auto-${startedAt.replace(/[:.]/g, "-")}`;
|
|
397
|
+
let currentLedger = {
|
|
398
|
+
campaignId,
|
|
399
|
+
objective: mode.objective,
|
|
400
|
+
activeStage: status.currentStage,
|
|
401
|
+
activeRung: resumePlan?.rungId || "",
|
|
402
|
+
ownerType: "",
|
|
403
|
+
ownerId: "",
|
|
404
|
+
command: "",
|
|
405
|
+
watchTarget: resumePlan?.watchTarget || "",
|
|
406
|
+
startedAt,
|
|
407
|
+
lastObservedAt: timestamp,
|
|
408
|
+
observedState: resumePlan ? "resuming" : "armed",
|
|
409
|
+
lastCheckpoint: resumePlan?.lastCheckpoint || "",
|
|
410
|
+
checkpointSummary: resumePlan?.reason || "auto loop armed and waiting for the first owned command",
|
|
411
|
+
nextTransition: resumePlan?.rungId || "",
|
|
412
|
+
continueBoundary: "Continue while the active owner is still running and no stop condition has matched.",
|
|
413
|
+
stopBoundary: mode.stopConditions,
|
|
414
|
+
escalationBoundary: mode.escalationConditions,
|
|
415
|
+
requiredReadSet: ".lab/context/eval-protocol.md, .lab/context/auto-mode.md, .lab/context/auto-status.md, .lab/context/auto-ledger.md, .lab/context/auto-outcome.md",
|
|
416
|
+
resumeCommand: "",
|
|
417
|
+
};
|
|
418
|
+
writeAutoLedger(targetDir, currentLedger, { lang });
|
|
267
419
|
const outcomeProtocolFields = {
|
|
268
420
|
primaryMetrics: evalProtocol.primaryMetrics,
|
|
269
421
|
secondaryMetrics: evalProtocol.secondaryMetrics,
|
|
@@ -305,6 +457,22 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
305
457
|
writeAutoStatus(targetDir, currentStatus, { lang });
|
|
306
458
|
};
|
|
307
459
|
|
|
460
|
+
const writeLedger = (overrides = {}) => {
|
|
461
|
+
currentLedger = {
|
|
462
|
+
...currentLedger,
|
|
463
|
+
activeStage: currentStatus.currentStage || currentLedger.activeStage,
|
|
464
|
+
activeRung: currentStatus.currentRung || currentLedger.activeRung,
|
|
465
|
+
watchTarget: currentStatus.watchTarget || currentLedger.watchTarget,
|
|
466
|
+
lastCheckpoint: currentStatus.lastCheckpoint || currentLedger.lastCheckpoint,
|
|
467
|
+
checkpointSummary: currentStatus.lastSummary || currentLedger.checkpointSummary,
|
|
468
|
+
lastObservedAt: new Date().toISOString(),
|
|
469
|
+
stopBoundary: mode.stopConditions,
|
|
470
|
+
escalationBoundary: mode.escalationConditions,
|
|
471
|
+
...overrides,
|
|
472
|
+
};
|
|
473
|
+
writeAutoLedger(targetDir, currentLedger, { lang });
|
|
474
|
+
};
|
|
475
|
+
|
|
308
476
|
const failAutoMode = (message) => {
|
|
309
477
|
currentStatus = {
|
|
310
478
|
...currentStatus,
|
|
@@ -313,6 +481,13 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
313
481
|
decision: message,
|
|
314
482
|
};
|
|
315
483
|
writeAutoStatus(targetDir, currentStatus, { lang });
|
|
484
|
+
writeLedger({
|
|
485
|
+
observedState: "failed",
|
|
486
|
+
ownerType: currentLedger.ownerType || "local-process",
|
|
487
|
+
checkpointSummary: message,
|
|
488
|
+
nextTransition: "terminal-failure",
|
|
489
|
+
resumeCommand: "",
|
|
490
|
+
});
|
|
316
491
|
writeAutoOutcome(
|
|
317
492
|
targetDir,
|
|
318
493
|
{
|
|
@@ -366,6 +541,8 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
366
541
|
rungId,
|
|
367
542
|
watchTarget,
|
|
368
543
|
nextRung,
|
|
544
|
+
ownerInfo: { ownerType: "local-process" },
|
|
545
|
+
updateLedger: writeLedger,
|
|
369
546
|
});
|
|
370
547
|
verifyStageContract({ stage, snapshot: contract.snapshot });
|
|
371
548
|
executedStages.push(stage);
|
|
@@ -378,6 +555,18 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
378
555
|
nextRung,
|
|
379
556
|
decision: rungId ? `completed rung ${rungId}` : `completed stage ${stage}`,
|
|
380
557
|
});
|
|
558
|
+
writeLedger({
|
|
559
|
+
ownerType: "local-process",
|
|
560
|
+
observedState: "checkpointed",
|
|
561
|
+
command,
|
|
562
|
+
watchTarget,
|
|
563
|
+
activeStage: stage,
|
|
564
|
+
activeRung: rungId || currentStatus.currentRung,
|
|
565
|
+
ownerId: currentLedger.ownerId,
|
|
566
|
+
checkpointSummary: rungId ? `completed rung ${rungId}` : `completed stage ${stage}`,
|
|
567
|
+
nextTransition: nextRung || "",
|
|
568
|
+
resumeCommand: command,
|
|
569
|
+
});
|
|
381
570
|
const frozenCoreChanges = detectFrozenCoreChanges(frozenCoreSnapshot);
|
|
382
571
|
if (frozenCoreChanges.length > 0) {
|
|
383
572
|
failAutoMode(`frozen core changed: ${frozenCoreChanges.join(", ")}`);
|
|
@@ -412,6 +601,17 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
412
601
|
nextRung,
|
|
413
602
|
decision: `retrying ${rungId || stage} after failure ${failureCount}`,
|
|
414
603
|
});
|
|
604
|
+
writeLedger({
|
|
605
|
+
ownerType: "local-process",
|
|
606
|
+
observedState: "retrying",
|
|
607
|
+
command,
|
|
608
|
+
watchTarget,
|
|
609
|
+
activeStage: stage,
|
|
610
|
+
activeRung: rungId || currentStatus.currentRung,
|
|
611
|
+
checkpointSummary: `retrying ${rungId || stage} after failure ${failureCount}`,
|
|
612
|
+
nextTransition: rungId || stage,
|
|
613
|
+
resumeCommand: command,
|
|
614
|
+
});
|
|
415
615
|
}
|
|
416
616
|
}
|
|
417
617
|
};
|
|
@@ -451,6 +651,14 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
451
651
|
currentCommand: mode.promotionCommand,
|
|
452
652
|
decision: `promotion policy matched after ${label}`,
|
|
453
653
|
});
|
|
654
|
+
writeLedger({
|
|
655
|
+
ownerType: "local-process",
|
|
656
|
+
command: mode.promotionCommand,
|
|
657
|
+
observedState: "checkpointed",
|
|
658
|
+
checkpointSummary: `promotion policy matched after ${label}`,
|
|
659
|
+
nextTransition: "post-promotion refresh",
|
|
660
|
+
resumeCommand: mode.promotionCommand,
|
|
661
|
+
});
|
|
454
662
|
promotionApplied = true;
|
|
455
663
|
const frozenCoreChangesAfterPromotion = detectFrozenCoreChanges(frozenCoreSnapshot);
|
|
456
664
|
if (frozenCoreChangesAfterPromotion.length > 0) {
|
|
@@ -463,7 +671,12 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
463
671
|
|
|
464
672
|
if (evalProtocol.experimentRungs.length > 0) {
|
|
465
673
|
const rungMap = new Map(evalProtocol.experimentRungs.map((rung) => [rung.id, rung]));
|
|
466
|
-
let currentRung =
|
|
674
|
+
let currentRung = resumePlan?.kind === "ladder"
|
|
675
|
+
? rungMap.get(resumePlan.rungId)
|
|
676
|
+
: evalProtocol.experimentRungs[0];
|
|
677
|
+
if (!currentRung) {
|
|
678
|
+
failAutoMode(`resume rung is missing from the current experiment ladder: ${resumePlan?.rungId || ""}`);
|
|
679
|
+
}
|
|
467
680
|
|
|
468
681
|
while (currentRung && iterationsCompleted < Math.max(1, maxIterations)) {
|
|
469
682
|
if (!mode.allowedStages.includes(currentRung.stage)) {
|
|
@@ -618,6 +831,12 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
618
831
|
decision: stopReason || "stopped by stop condition",
|
|
619
832
|
};
|
|
620
833
|
writeAutoStatus(targetDir, currentStatus, { lang });
|
|
834
|
+
writeLedger({
|
|
835
|
+
observedState: "stopped",
|
|
836
|
+
checkpointSummary: stopReason || "stopped by stop condition",
|
|
837
|
+
nextTransition: "terminal-stop",
|
|
838
|
+
resumeCommand: "",
|
|
839
|
+
});
|
|
621
840
|
writeAutoOutcome(
|
|
622
841
|
targetDir,
|
|
623
842
|
{
|
|
@@ -670,6 +889,12 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
670
889
|
decision: stopReason || "stopped by stop condition",
|
|
671
890
|
};
|
|
672
891
|
writeAutoStatus(targetDir, currentStatus, { lang });
|
|
892
|
+
writeLedger({
|
|
893
|
+
observedState: "stopped",
|
|
894
|
+
checkpointSummary: stopReason || "stopped by stop condition",
|
|
895
|
+
nextTransition: "terminal-stop",
|
|
896
|
+
resumeCommand: "",
|
|
897
|
+
});
|
|
673
898
|
writeAutoOutcome(
|
|
674
899
|
targetDir,
|
|
675
900
|
{
|
|
@@ -724,6 +949,12 @@ async function startAutoMode({ targetDir, now = new Date() }) {
|
|
|
724
949
|
decision: successReached ? "completed configured auto goal" : "completed configured stages",
|
|
725
950
|
};
|
|
726
951
|
writeAutoStatus(targetDir, currentStatus, { lang });
|
|
952
|
+
writeLedger({
|
|
953
|
+
observedState: "completed",
|
|
954
|
+
checkpointSummary: successReached ? "completed configured auto goal" : "completed configured stages",
|
|
955
|
+
nextTransition: "terminal-success",
|
|
956
|
+
resumeCommand: "",
|
|
957
|
+
});
|
|
727
958
|
writeAutoOutcome(
|
|
728
959
|
targetDir,
|
|
729
960
|
{
|
|
@@ -805,6 +1036,31 @@ function stopAutoMode({ targetDir, now = new Date() }) {
|
|
|
805
1036
|
decision: "stopped by operator",
|
|
806
1037
|
};
|
|
807
1038
|
writeAutoStatus(targetDir, status, { lang });
|
|
1039
|
+
writeAutoLedger(
|
|
1040
|
+
targetDir,
|
|
1041
|
+
{
|
|
1042
|
+
campaignId: existing.startedAt ? `auto-${existing.startedAt.replace(/[:.]/g, "-")}` : `auto-${now.toISOString().replace(/[:.]/g, "-")}`,
|
|
1043
|
+
objective: mode.objective,
|
|
1044
|
+
activeStage: existing.currentStage || "",
|
|
1045
|
+
activeRung: existing.currentRung || "",
|
|
1046
|
+
ownerType: "local-process",
|
|
1047
|
+
ownerId: "",
|
|
1048
|
+
command: existing.currentCommand || "",
|
|
1049
|
+
watchTarget: existing.watchTarget || "",
|
|
1050
|
+
startedAt: existing.startedAt || now.toISOString(),
|
|
1051
|
+
lastObservedAt: now.toISOString(),
|
|
1052
|
+
observedState: "stopped",
|
|
1053
|
+
lastCheckpoint: existing.lastCheckpoint || "",
|
|
1054
|
+
checkpointSummary: "stopped by operator",
|
|
1055
|
+
nextTransition: "terminal-stop",
|
|
1056
|
+
continueBoundary: "No further automatic progress is allowed until a new approved auto run starts.",
|
|
1057
|
+
stopBoundary: mode.stopConditions,
|
|
1058
|
+
escalationBoundary: mode.escalationConditions,
|
|
1059
|
+
requiredReadSet: ".lab/context/eval-protocol.md, .lab/context/auto-mode.md, .lab/context/auto-status.md, .lab/context/auto-ledger.md, .lab/context/auto-outcome.md",
|
|
1060
|
+
resumeCommand: "",
|
|
1061
|
+
},
|
|
1062
|
+
{ lang }
|
|
1063
|
+
);
|
|
808
1064
|
writeAutoOutcome(
|
|
809
1065
|
targetDir,
|
|
810
1066
|
{
|
package/lib/auto_state.cjs
CHANGED
|
@@ -68,6 +68,33 @@ function parseAutoStatus(targetDir) {
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
function parseAutoLedger(targetDir) {
|
|
72
|
+
const text = readFileIfExists(contextFile(targetDir, "auto-ledger.md"));
|
|
73
|
+
return {
|
|
74
|
+
path: contextFile(targetDir, "auto-ledger.md"),
|
|
75
|
+
text,
|
|
76
|
+
campaignId: extractValue(text, ["Campaign id", "Campaign ID", "活动 id"]),
|
|
77
|
+
objective: extractValue(text, ["Objective", "目标"]),
|
|
78
|
+
activeStage: extractValue(text, ["Active stage", "当前阶段"]),
|
|
79
|
+
activeRung: extractValue(text, ["Active rung", "当前 rung"]),
|
|
80
|
+
ownerType: extractValue(text, ["Owner type", "Owner 类型"]),
|
|
81
|
+
ownerId: extractValue(text, ["Owner id", "Owner ID"]),
|
|
82
|
+
command: extractValue(text, ["Command", "命令"]),
|
|
83
|
+
watchTarget: extractValue(text, ["Watch target", "监视目标"]),
|
|
84
|
+
startedAt: extractValue(text, ["Started at", "开始时间"]),
|
|
85
|
+
lastObservedAt: extractValue(text, ["Last observed at", "最近观察时间"]),
|
|
86
|
+
observedState: extractValue(text, ["Observed state", "观察状态"]),
|
|
87
|
+
lastCheckpoint: extractValue(text, ["Last checkpoint", "最近 checkpoint"]),
|
|
88
|
+
checkpointSummary: extractValue(text, ["Checkpoint summary", "Checkpoint 摘要"]),
|
|
89
|
+
nextTransition: extractValue(text, ["Next transition", "下一转换"]),
|
|
90
|
+
continueBoundary: extractValue(text, ["Continue boundary", "继续边界"]),
|
|
91
|
+
stopBoundary: extractValue(text, ["Stop boundary", "停止边界"]),
|
|
92
|
+
escalationBoundary: extractValue(text, ["Escalation boundary", "升级边界"]),
|
|
93
|
+
requiredReadSet: extractValue(text, ["Required read set", "必要读取集合"]),
|
|
94
|
+
resumeCommand: extractValue(text, ["Resume command", "恢复命令"]),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
71
98
|
function renderAutoStatus(status, { lang = "en" } = {}) {
|
|
72
99
|
if (lang === "zh") {
|
|
73
100
|
return `# 自动模式状态
|
|
@@ -240,12 +267,96 @@ function renderAutoOutcome(outcome, { lang = "en" } = {}) {
|
|
|
240
267
|
`;
|
|
241
268
|
}
|
|
242
269
|
|
|
270
|
+
function renderAutoLedger(ledger, { lang = "en" } = {}) {
|
|
271
|
+
if (lang === "zh") {
|
|
272
|
+
return `# 自动运行账本
|
|
273
|
+
|
|
274
|
+
## Campaign
|
|
275
|
+
|
|
276
|
+
- Campaign id: ${ledger.campaignId || ""}
|
|
277
|
+
- Objective: ${ledger.objective || ""}
|
|
278
|
+
- Active stage: ${ledger.activeStage || ""}
|
|
279
|
+
- Active rung: ${ledger.activeRung || ""}
|
|
280
|
+
|
|
281
|
+
## Owner
|
|
282
|
+
|
|
283
|
+
- Owner type: ${ledger.ownerType || ""}
|
|
284
|
+
- Owner id: ${ledger.ownerId || ""}
|
|
285
|
+
- Command: ${ledger.command || ""}
|
|
286
|
+
- Watch target: ${ledger.watchTarget || ""}
|
|
287
|
+
- Started at: ${ledger.startedAt || ""}
|
|
288
|
+
- Last observed at: ${ledger.lastObservedAt || ""}
|
|
289
|
+
- Observed state: ${ledger.observedState || ""}
|
|
290
|
+
|
|
291
|
+
## Checkpoints
|
|
292
|
+
|
|
293
|
+
- Last checkpoint: ${ledger.lastCheckpoint || ""}
|
|
294
|
+
- Checkpoint summary: ${ledger.checkpointSummary || ""}
|
|
295
|
+
- Next transition: ${ledger.nextTransition || ""}
|
|
296
|
+
|
|
297
|
+
## Boundaries
|
|
298
|
+
|
|
299
|
+
- Continue boundary: ${ledger.continueBoundary || ""}
|
|
300
|
+
- Stop boundary: ${ledger.stopBoundary || ""}
|
|
301
|
+
- Escalation boundary: ${ledger.escalationBoundary || ""}
|
|
302
|
+
|
|
303
|
+
## Resume
|
|
304
|
+
|
|
305
|
+
- Required read set: ${ledger.requiredReadSet || ""}
|
|
306
|
+
- Resume command: ${ledger.resumeCommand || ""}
|
|
307
|
+
`;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return `# Auto Runtime Ledger
|
|
311
|
+
|
|
312
|
+
## Campaign
|
|
313
|
+
|
|
314
|
+
- Campaign id: ${ledger.campaignId || ""}
|
|
315
|
+
- Objective: ${ledger.objective || ""}
|
|
316
|
+
- Active stage: ${ledger.activeStage || ""}
|
|
317
|
+
- Active rung: ${ledger.activeRung || ""}
|
|
318
|
+
|
|
319
|
+
## Owner
|
|
320
|
+
|
|
321
|
+
- Owner type: ${ledger.ownerType || ""}
|
|
322
|
+
- Owner id: ${ledger.ownerId || ""}
|
|
323
|
+
- Command: ${ledger.command || ""}
|
|
324
|
+
- Watch target: ${ledger.watchTarget || ""}
|
|
325
|
+
- Started at: ${ledger.startedAt || ""}
|
|
326
|
+
- Last observed at: ${ledger.lastObservedAt || ""}
|
|
327
|
+
- Observed state: ${ledger.observedState || ""}
|
|
328
|
+
|
|
329
|
+
## Checkpoints
|
|
330
|
+
|
|
331
|
+
- Last checkpoint: ${ledger.lastCheckpoint || ""}
|
|
332
|
+
- Checkpoint summary: ${ledger.checkpointSummary || ""}
|
|
333
|
+
- Next transition: ${ledger.nextTransition || ""}
|
|
334
|
+
|
|
335
|
+
## Boundaries
|
|
336
|
+
|
|
337
|
+
- Continue boundary: ${ledger.continueBoundary || ""}
|
|
338
|
+
- Stop boundary: ${ledger.stopBoundary || ""}
|
|
339
|
+
- Escalation boundary: ${ledger.escalationBoundary || ""}
|
|
340
|
+
|
|
341
|
+
## Resume
|
|
342
|
+
|
|
343
|
+
- Required read set: ${ledger.requiredReadSet || ""}
|
|
344
|
+
- Resume command: ${ledger.resumeCommand || ""}
|
|
345
|
+
`;
|
|
346
|
+
}
|
|
347
|
+
|
|
243
348
|
function writeAutoOutcome(targetDir, outcome, { lang = "en" } = {}) {
|
|
244
349
|
const filePath = contextFile(targetDir, "auto-outcome.md");
|
|
245
350
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
246
351
|
fs.writeFileSync(filePath, renderAutoOutcome(outcome, { lang }).trimEnd() + "\n");
|
|
247
352
|
}
|
|
248
353
|
|
|
354
|
+
function writeAutoLedger(targetDir, ledger, { lang = "en" } = {}) {
|
|
355
|
+
const filePath = contextFile(targetDir, "auto-ledger.md");
|
|
356
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
357
|
+
fs.writeFileSync(filePath, renderAutoLedger(ledger, { lang }).trimEnd() + "\n");
|
|
358
|
+
}
|
|
359
|
+
|
|
249
360
|
function resolveRequiredArtifact(targetDir, configuredPath) {
|
|
250
361
|
if (!isMeaningful(configuredPath)) {
|
|
251
362
|
return { relativePath: "", absolutePath: "" };
|
|
@@ -258,12 +369,15 @@ function resolveRequiredArtifact(targetDir, configuredPath) {
|
|
|
258
369
|
}
|
|
259
370
|
|
|
260
371
|
module.exports = {
|
|
372
|
+
parseAutoLedger,
|
|
261
373
|
parseAutoMode,
|
|
262
374
|
parseAutoStatus,
|
|
263
375
|
readWorkflowLanguage,
|
|
376
|
+
renderAutoLedger,
|
|
264
377
|
renderAutoOutcome,
|
|
265
378
|
renderAutoStatus,
|
|
266
379
|
resolveRequiredArtifact,
|
|
380
|
+
writeAutoLedger,
|
|
267
381
|
writeAutoOutcome,
|
|
268
382
|
writeAutoStatus,
|
|
269
383
|
};
|
package/lib/i18n.cjs
CHANGED
|
@@ -1563,6 +1563,7 @@ const ZH_SKILL_FILES = {
|
|
|
1563
1563
|
`# 自动模式契约
|
|
1564
1564
|
|
|
1565
1565
|
用这个文件定义 \`/lab:auto\` 的有边界自治执行范围。
|
|
1566
|
+
把 \`.lab/context/auto-ledger.md\` 当成运行时账本,记录 owner、checkpoint、resume 和 stop 边界。
|
|
1566
1567
|
|
|
1567
1568
|
## 目标
|
|
1568
1569
|
|
|
@@ -1602,6 +1603,7 @@ const ZH_SKILL_FILES = {
|
|
|
1602
1603
|
- Rung 的 \`Command\` 应该绑定真实的长任务命令,由它产出最终实验结果。
|
|
1603
1604
|
- 短 watcher 只用于查看进度;当真实实验还在运行时,不要把短 watcher 当成 stage 或 rung 的主命令。
|
|
1604
1605
|
- 当真实实验进程还活着时,只记录进度更新并继续等待。
|
|
1606
|
+
- 当 loop 处于运行态时,把当前 owner、命令和 watch target 写进 \`.lab/context/auto-ledger.md\`。
|
|
1605
1607
|
- Run command:
|
|
1606
1608
|
- Iterate command:
|
|
1607
1609
|
- Review command:
|
|
@@ -1634,6 +1636,43 @@ const ZH_SKILL_FILES = {
|
|
|
1634
1636
|
- Stop conditions:
|
|
1635
1637
|
- Escalation conditions:
|
|
1636
1638
|
- Canonical promotion writeback: update \`.lab/context/data-decisions.md\`、\`.lab/context/decisions.md\` 和 \`.lab/context/workflow-state.md\`,然后刷新 \`state.md\` 等派生视图。
|
|
1639
|
+
`,
|
|
1640
|
+
[path.join(".lab", "context", "auto-ledger.md")]:
|
|
1641
|
+
`# 自动运行账本
|
|
1642
|
+
|
|
1643
|
+
## Campaign
|
|
1644
|
+
|
|
1645
|
+
- Campaign id:
|
|
1646
|
+
- Objective:
|
|
1647
|
+
- Active stage:
|
|
1648
|
+
- Active rung:
|
|
1649
|
+
|
|
1650
|
+
## Owner
|
|
1651
|
+
|
|
1652
|
+
- Owner type:
|
|
1653
|
+
- Owner id:
|
|
1654
|
+
- Command:
|
|
1655
|
+
- Watch target:
|
|
1656
|
+
- Started at:
|
|
1657
|
+
- Last observed at:
|
|
1658
|
+
- Observed state:
|
|
1659
|
+
|
|
1660
|
+
## Checkpoints
|
|
1661
|
+
|
|
1662
|
+
- Last checkpoint:
|
|
1663
|
+
- Checkpoint summary:
|
|
1664
|
+
- Next transition:
|
|
1665
|
+
|
|
1666
|
+
## Boundaries
|
|
1667
|
+
|
|
1668
|
+
- Continue boundary:
|
|
1669
|
+
- Stop boundary:
|
|
1670
|
+
- Escalation boundary:
|
|
1671
|
+
|
|
1672
|
+
## Resume
|
|
1673
|
+
|
|
1674
|
+
- Required read set:
|
|
1675
|
+
- Resume command:
|
|
1637
1676
|
`,
|
|
1638
1677
|
[path.join(".lab", "context", "auto-outcome.md")]:
|
|
1639
1678
|
`# 自动结果
|
|
@@ -2141,7 +2180,7 @@ ZH_CONTENT[path.join(".codex", "prompts", "lab-data.md")] = codexPrompt(
|
|
|
2141
2180
|
ZH_CONTENT[path.join(".codex", "prompts", "lab-auto.md")] = codexPrompt(
|
|
2142
2181
|
"在已批准边界内编排自动实验循环",
|
|
2143
2182
|
"auto mode objective",
|
|
2144
|
-
"使用已安装的 `lab` 技能:`.codex/skills/lab/SKILL.md`。\n\n立刻针对用户当前给出的参数执行 `/lab:auto`,不要只推荐别的 `/lab` 阶段。只有在缺少阻塞性前提时,才明确指出缺什么,并且一次最多追问一个问题。\n\n本命令运行 `/lab:auto` 阶段。它必须读取 `.lab/context/eval-protocol.md`、`.lab/context/auto-mode.md`、`.lab/context/auto-status.md` 与 `.lab/context/auto-outcome.md`,先确认 autonomy level、approval status、terminal goal schema,以及 primary gate、secondary guard、promotion condition、stop reason、escalation reason,再把 eval-protocol 里的指标释义、主表计划、来源约束与结构化实验阶梯当作执行依据,在不修改 mission、framing 和核心 claims 的前提下编排已批准的 `run`、`iterate`、`review`、`report`,轮询长任务完成情况;如果声明了 rung,就保持会话活着并按 rung 转移继续推进。\n首个可见输出块必须是 `Auto preflight`。这个块必须列出已读取文件,并回显 `Autonomy level`、`Approval status`、`Allowed stages`、`Terminal goal`、`Primary gate` 和 `Secondary guard`,然后才能进入执行摘要或动作计划。\n如果 preflight 所需字段缺失、过期或彼此冲突,就必须在执行前停下,并明确指出到底是哪一个字段阻止了 loop 启动。\n如果仓库的 workflow language 是中文,摘要、清单条目、任务标签和进度更新都必须使用中文,除非某个文件路径、代码标识符或字面指标名必须保持原样。\n把 `Layer 3`、`Phase 1`、`Table 2` 这类表达视为论文范围目标;只有显式写成 `Autonomy level L3` 或 `自治级别 L3` 时,才把它当成执行权限级别。\n不要用 `sleep 30`、单次 `pgrep` 或一次性的 `metrics.json` 探针来代替真实长任务命令;当真实实验进程还活着时,只允许发进度更新并继续等待。"
|
|
2183
|
+
"使用已安装的 `lab` 技能:`.codex/skills/lab/SKILL.md`。\n\n立刻针对用户当前给出的参数执行 `/lab:auto`,不要只推荐别的 `/lab` 阶段。只有在缺少阻塞性前提时,才明确指出缺什么,并且一次最多追问一个问题。\n\n本命令运行 `/lab:auto` 阶段。它必须读取 `.lab/context/eval-protocol.md`、`.lab/context/auto-mode.md`、`.lab/context/auto-status.md`、`.lab/context/auto-ledger.md` 与 `.lab/context/auto-outcome.md`,先确认 autonomy level、approval status、terminal goal schema,以及 primary gate、secondary guard、promotion condition、stop reason、escalation reason,再把 eval-protocol 里的指标释义、主表计划、来源约束与结构化实验阶梯当作执行依据,在不修改 mission、framing 和核心 claims 的前提下编排已批准的 `run`、`iterate`、`review`、`report`,轮询长任务完成情况;如果声明了 rung,就保持会话活着并按 rung 转移继续推进。\n首个可见输出块必须是 `Auto preflight`。这个块必须列出已读取文件,并回显 `Autonomy level`、`Approval status`、`Allowed stages`、`Terminal goal`、`Primary gate` 和 `Secondary guard`,然后才能进入执行摘要或动作计划。\n如果 preflight 所需字段缺失、过期或彼此冲突,就必须在执行前停下,并明确指出到底是哪一个字段阻止了 loop 启动。\n当 loop 活着时,必须把当前 owner、观察状态、checkpoint 摘要、继续边界、停止边界和恢复读取集合写进 `.lab/context/auto-ledger.md`。\n如果仓库的 workflow language 是中文,摘要、清单条目、任务标签和进度更新都必须使用中文,除非某个文件路径、代码标识符或字面指标名必须保持原样。\n把 `Layer 3`、`Phase 1`、`Table 2` 这类表达视为论文范围目标;只有显式写成 `Autonomy level L3` 或 `自治级别 L3` 时,才把它当成执行权限级别。\n不要用 `sleep 30`、单次 `pgrep` 或一次性的 `metrics.json` 探针来代替真实长任务命令;当真实实验进程还活着时,只允许发进度更新并继续等待。"
|
|
2145
2184
|
);
|
|
2146
2185
|
|
|
2147
2186
|
ZH_CONTENT[path.join(".claude", "commands", "lab.md")] = claudeCommand(
|
|
@@ -2162,7 +2201,7 @@ ZH_CONTENT[path.join(".claude", "commands", "lab-auto.md")] = claudeCommand(
|
|
|
2162
2201
|
"lab-auto",
|
|
2163
2202
|
"在已批准边界内编排自动实验循环",
|
|
2164
2203
|
"auto mode objective",
|
|
2165
|
-
"使用已安装的 `lab` 技能:`.claude/skills/lab/SKILL.md`。\n\n立刻针对用户当前给出的参数执行 `auto` 阶段,不要只推荐别的 lab 阶段。只有在缺少阻塞性前提时,才明确指出缺什么,并且一次最多追问一个问题。\n\n本命令运行 lab workflow 的 `auto` 阶段。它必须读取 `.lab/context/eval-protocol.md`、`.lab/context/auto-mode.md`、`.lab/context/auto-status.md` 与 `.lab/context/auto-outcome.md`,先确认 autonomy level、approval status、terminal goal schema,以及 primary gate、secondary guard、promotion condition、stop reason、escalation reason,再把 eval-protocol 里的指标释义、主表计划、来源约束与结构化实验阶梯当作执行依据,在不修改 mission、framing 和核心 claims 的前提下编排已批准的 `run`、`iterate`、`review`、`report`,轮询长任务完成情况;如果声明了 rung,就保持会话活着并按 rung 转移继续推进。\n首个可见输出块必须是 `Auto preflight`。这个块必须列出已读取文件,并回显 `Autonomy level`、`Approval status`、`Allowed stages`、`Terminal goal`、`Primary gate` 和 `Secondary guard`,然后才能进入执行摘要或动作计划。\n如果 preflight 所需字段缺失、过期或彼此冲突,就必须在执行前停下,并明确指出到底是哪一个字段阻止了 loop 启动。\n如果仓库的 workflow language 是中文,摘要、清单条目、任务标签和进度更新都必须使用中文,除非某个文件路径、代码标识符或字面指标名必须保持原样。\n把 `Layer 3`、`Phase 1`、`Table 2` 这类表达视为论文范围目标;只有显式写成 `Autonomy level L3` 或 `自治级别 L3` 时,才把它当成执行权限级别。\n不要用 `sleep 30`、单次 `pgrep` 或一次性的 `metrics.json` 探针来代替真实长任务命令;当真实实验进程还活着时,只允许发进度更新并继续等待。"
|
|
2204
|
+
"使用已安装的 `lab` 技能:`.claude/skills/lab/SKILL.md`。\n\n立刻针对用户当前给出的参数执行 `auto` 阶段,不要只推荐别的 lab 阶段。只有在缺少阻塞性前提时,才明确指出缺什么,并且一次最多追问一个问题。\n\n本命令运行 lab workflow 的 `auto` 阶段。它必须读取 `.lab/context/eval-protocol.md`、`.lab/context/auto-mode.md`、`.lab/context/auto-status.md`、`.lab/context/auto-ledger.md` 与 `.lab/context/auto-outcome.md`,先确认 autonomy level、approval status、terminal goal schema,以及 primary gate、secondary guard、promotion condition、stop reason、escalation reason,再把 eval-protocol 里的指标释义、主表计划、来源约束与结构化实验阶梯当作执行依据,在不修改 mission、framing 和核心 claims 的前提下编排已批准的 `run`、`iterate`、`review`、`report`,轮询长任务完成情况;如果声明了 rung,就保持会话活着并按 rung 转移继续推进。\n首个可见输出块必须是 `Auto preflight`。这个块必须列出已读取文件,并回显 `Autonomy level`、`Approval status`、`Allowed stages`、`Terminal goal`、`Primary gate` 和 `Secondary guard`,然后才能进入执行摘要或动作计划。\n如果 preflight 所需字段缺失、过期或彼此冲突,就必须在执行前停下,并明确指出到底是哪一个字段阻止了 loop 启动。\n当 loop 活着时,必须把当前 owner、观察状态、checkpoint 摘要、继续边界、停止边界和恢复读取集合写进 `.lab/context/auto-ledger.md`。\n如果仓库的 workflow language 是中文,摘要、清单条目、任务标签和进度更新都必须使用中文,除非某个文件路径、代码标识符或字面指标名必须保持原样。\n把 `Layer 3`、`Phase 1`、`Table 2` 这类表达视为论文范围目标;只有显式写成 `Autonomy level L3` 或 `自治级别 L3` 时,才把它当成执行权限级别。\n不要用 `sleep 30`、单次 `pgrep` 或一次性的 `metrics.json` 探针来代替真实长任务命令;当真实实验进程还活着时,只允许发进度更新并继续等待。"
|
|
2166
2205
|
);
|
|
2167
2206
|
|
|
2168
2207
|
const zhRecipeQuickPathLine =
|
package/lib/install.cjs
CHANGED
|
@@ -44,6 +44,7 @@ const PROJECT_OWNED_LOCALIZED_PATHS = [
|
|
|
44
44
|
path.join(".lab", "context", "eval-protocol.md"),
|
|
45
45
|
path.join(".lab", "context", "auto-mode.md"),
|
|
46
46
|
path.join(".lab", "context", "auto-status.md"),
|
|
47
|
+
path.join(".lab", "context", "auto-ledger.md"),
|
|
47
48
|
path.join(".lab", "context", "auto-outcome.md"),
|
|
48
49
|
path.join(".lab", "context", "terminology-lock.md"),
|
|
49
50
|
path.join(".lab", "context", "summary.md"),
|
|
@@ -7,7 +7,7 @@ argument-hint: autonomous campaign target
|
|
|
7
7
|
Use the installed `lab` skill at `.claude/skills/lab/SKILL.md`.
|
|
8
8
|
|
|
9
9
|
Execute the requested `/lab-auto` command against the user's argument now. Do not only recommend another lab stage. If a blocking prerequisite is missing, say exactly what is missing and ask at most one clarifying question.
|
|
10
|
-
This command runs the `auto` stage of the lab workflow. It must read `.lab/context/eval-protocol.md`, `.lab/context/auto-mode.md`, `.lab/context/auto-status.md`, and `.lab/context/auto-outcome.md`, enforce the declared terminal goal schema, make the primary gate, secondary guard, promotion condition, stop reason, and escalation reason explicit, orchestrate approved run, iterate, review, and report stages inside that contract, poll long-running work until completion or stop conditions, and write
|
|
10
|
+
This command runs the `auto` stage of the lab workflow. It must read `.lab/context/eval-protocol.md`, `.lab/context/auto-mode.md`, `.lab/context/auto-status.md`, `.lab/context/auto-ledger.md`, and `.lab/context/auto-outcome.md`, enforce the declared terminal goal schema, make the primary gate, secondary guard, promotion condition, stop reason, and escalation reason explicit, orchestrate approved run, iterate, review, and report stages inside that contract, poll long-running work until completion or stop conditions, and write live owner state plus progress and the final outcome back into `.lab/context/auto-status.md`, `.lab/context/auto-ledger.md`, and `.lab/context/auto-outcome.md`.
|
|
11
11
|
The first visible block must be `Auto preflight`. That first visible block must list the files read and echo `Autonomy level`, `Approval status`, `Allowed stages`, `Terminal goal`, `Primary gate`, and `Secondary guard` before any execution summary or action plan.
|
|
12
12
|
If the preflight block cannot be completed because any required field is missing, stale, or inconsistent, stop before execution and say exactly which field blocked arming the loop.
|
|
13
13
|
When the repository workflow language is Chinese, summaries, checklist items, task labels, and progress updates should be written in Chinese unless a literal identifier must stay unchanged.
|
|
@@ -72,7 +72,8 @@ Use the same repository artifacts and stage boundaries every time.
|
|
|
72
72
|
- If the request omits the level or mixes it with a paper layer, phase, or table target, `/lab auto` should stop and ask for an explicit autonomy level before arming the loop.
|
|
73
73
|
- The first visible output of a real `/lab auto` run must be `Auto preflight`.
|
|
74
74
|
- That first visible output must show files read plus `Autonomy level`, `Allowed stages`, `Terminal goal`, `Primary gate`, and `Secondary guard`.
|
|
75
|
-
- If the preflight block cannot be completed from `.lab/context/eval-protocol.md`, `.lab/context/auto-mode.md`, `.lab/context/auto-status.md`, and `.lab/context/auto-outcome.md`, `/lab auto` should stop instead of acting like the loop is armed.
|
|
75
|
+
- If the preflight block cannot be completed from `.lab/context/eval-protocol.md`, `.lab/context/auto-mode.md`, `.lab/context/auto-status.md`, `.lab/context/auto-ledger.md`, and `.lab/context/auto-outcome.md`, `/lab auto` should stop instead of acting like the loop is armed.
|
|
76
|
+
- While the loop is alive, `/lab auto` should keep `.lab/context/auto-ledger.md` updated with the active owner, observed state, and resume boundary.
|
|
76
77
|
|
|
77
78
|
- Treat `Autonomy level L1/L2/L3` as the execution privilege level, not as a paper layer, phase, or table number.
|
|
78
79
|
- Treat `paper layer`, `phase`, and `table` as experiment targets. For example, `paper layer 3` or `Phase 1` should not be interpreted as `Autonomy level L3`.
|
|
@@ -6,7 +6,7 @@ argument-hint: autonomous campaign target
|
|
|
6
6
|
Use the installed `lab` skill at `.codex/skills/lab/SKILL.md`.
|
|
7
7
|
|
|
8
8
|
Execute the requested `/lab:auto` stage against the user's argument now. Do not only recommend another lab stage. If a blocking prerequisite is missing, say exactly what is missing and ask at most one clarifying question.
|
|
9
|
-
This command runs the `/lab:auto` stage. It must read `.lab/context/eval-protocol.md`, `.lab/context/auto-mode.md`, `.lab/context/auto-status.md`, and `.lab/context/auto-outcome.md`, enforce the declared terminal goal schema, make the primary gate, secondary guard, promotion condition, stop reason, and escalation reason explicit, orchestrate approved run, iterate, review, and report stages inside that contract, poll long-running work until completion or stop conditions, and write
|
|
9
|
+
This command runs the `/lab:auto` stage. It must read `.lab/context/eval-protocol.md`, `.lab/context/auto-mode.md`, `.lab/context/auto-status.md`, `.lab/context/auto-ledger.md`, and `.lab/context/auto-outcome.md`, enforce the declared terminal goal schema, make the primary gate, secondary guard, promotion condition, stop reason, and escalation reason explicit, orchestrate approved run, iterate, review, and report stages inside that contract, poll long-running work until completion or stop conditions, and write live owner state plus progress and the final outcome back into `.lab/context/auto-status.md`, `.lab/context/auto-ledger.md`, and `.lab/context/auto-outcome.md`.
|
|
10
10
|
The first visible block must be `Auto preflight`. That first visible block must list the files read and echo `Autonomy level`, `Approval status`, `Allowed stages`, `Terminal goal`, `Primary gate`, and `Secondary guard` before any execution summary or action plan.
|
|
11
11
|
If the preflight block cannot be completed because any required field is missing, stale, or inconsistent, stop before execution and say exactly which field blocked arming the loop.
|
|
12
12
|
When the repository workflow language is Chinese, summaries, checklist items, task labels, and progress updates should be written in Chinese unless a literal identifier must stay unchanged.
|
|
@@ -66,7 +66,8 @@ argument-hint: workflow question or stage choice
|
|
|
66
66
|
- If the request omits the level or mixes it with a paper layer, phase, or table target, `/lab:auto` should stop and ask for an explicit autonomy level before arming the loop.
|
|
67
67
|
- The first visible output of a real `/lab:auto` run must be `Auto preflight`.
|
|
68
68
|
- That first visible output must show files read plus `Autonomy level`, `Allowed stages`, `Terminal goal`, `Primary gate`, and `Secondary guard`.
|
|
69
|
-
- If the preflight block cannot be completed from `.lab/context/eval-protocol.md`, `.lab/context/auto-mode.md`, `.lab/context/auto-status.md`, and `.lab/context/auto-outcome.md`, `/lab:auto` should stop instead of acting like the loop is armed.
|
|
69
|
+
- If the preflight block cannot be completed from `.lab/context/eval-protocol.md`, `.lab/context/auto-mode.md`, `.lab/context/auto-status.md`, `.lab/context/auto-ledger.md`, and `.lab/context/auto-outcome.md`, `/lab:auto` should stop instead of acting like the loop is armed.
|
|
70
|
+
- While the loop is alive, `/lab:auto` should keep `.lab/context/auto-ledger.md` updated with the active owner, observed state, and resume boundary.
|
|
70
71
|
|
|
71
72
|
- Treat `Autonomy level L1/L2/L3` as the execution privilege level, not as a paper layer, phase, or table number.
|
|
72
73
|
- Treat `paper layer`, `phase`, and `table` as experiment targets. For example, `paper layer 3` or `Phase 1` should not be interpreted as `Autonomy level L3`.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Auto Runtime Ledger
|
|
2
|
+
|
|
3
|
+
## Campaign
|
|
4
|
+
|
|
5
|
+
- Campaign id:
|
|
6
|
+
- Objective:
|
|
7
|
+
- Active stage:
|
|
8
|
+
- Active rung:
|
|
9
|
+
|
|
10
|
+
## Owner
|
|
11
|
+
|
|
12
|
+
- Owner type:
|
|
13
|
+
- Owner id:
|
|
14
|
+
- Command:
|
|
15
|
+
- Watch target:
|
|
16
|
+
- Started at:
|
|
17
|
+
- Last observed at:
|
|
18
|
+
- Observed state:
|
|
19
|
+
|
|
20
|
+
## Checkpoints
|
|
21
|
+
|
|
22
|
+
- Last checkpoint:
|
|
23
|
+
- Checkpoint summary:
|
|
24
|
+
- Next transition:
|
|
25
|
+
|
|
26
|
+
## Boundaries
|
|
27
|
+
|
|
28
|
+
- Continue boundary:
|
|
29
|
+
- Stop boundary:
|
|
30
|
+
- Escalation boundary:
|
|
31
|
+
|
|
32
|
+
## Resume
|
|
33
|
+
|
|
34
|
+
- Required read set:
|
|
35
|
+
- Resume command:
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Use this file to define the bounded autonomous execution envelope for `/lab:auto`.
|
|
4
4
|
Pair it with `.lab/context/eval-protocol.md`, which defines the paper-facing metrics, tables, gates, and benchmark ladder that auto mode should optimize against.
|
|
5
5
|
If `eval-protocol.md` declares structured rung entries, auto mode follows those rung transitions first and uses the stage commands here as per-stage fallbacks.
|
|
6
|
+
Use `.lab/context/auto-ledger.md` as the live runtime ledger for ownership, checkpoints, resume, and stop boundaries.
|
|
6
7
|
|
|
7
8
|
## Objective
|
|
8
9
|
|
|
@@ -42,6 +43,7 @@ If `eval-protocol.md` declares structured rung entries, auto mode follows those
|
|
|
42
43
|
- Rung `Command` should be the real long-running command that owns the experiment result.
|
|
43
44
|
- A short watcher is only a progress probe. Do not use a short watcher as the stage or rung command when the real experiment is still running.
|
|
44
45
|
- While the real experiment process is still alive, only record a progress update and keep waiting.
|
|
46
|
+
- Record the active owner, command, and watch target in `.lab/context/auto-ledger.md` while the loop is alive.
|
|
45
47
|
- Run command:
|
|
46
48
|
- Iterate command:
|
|
47
49
|
- Review command:
|
|
@@ -108,12 +108,13 @@ Use this skill when the user invokes `/lab:*` or asks for the structured researc
|
|
|
108
108
|
### `/lab:auto`
|
|
109
109
|
|
|
110
110
|
- Use this stage to orchestrate approved execution stages with bounded autonomy.
|
|
111
|
-
- Read `.lab/config/workflow.json`, `.lab/context/mission.md`, `.lab/context/state.md`, `.lab/context/workflow-state.md`, `.lab/context/decisions.md`, `.lab/context/data-decisions.md`, `.lab/context/evidence-index.md`, `.lab/context/terminology-lock.md`, `.lab/context/auto-mode.md`, and `.lab/context/auto-
|
|
112
|
-
- Treat `.lab/context/auto-mode.md` as the control contract
|
|
111
|
+
- Read `.lab/config/workflow.json`, `.lab/context/mission.md`, `.lab/context/state.md`, `.lab/context/workflow-state.md`, `.lab/context/decisions.md`, `.lab/context/data-decisions.md`, `.lab/context/evidence-index.md`, `.lab/context/terminology-lock.md`, `.lab/context/auto-mode.md`, `.lab/context/auto-status.md`, and `.lab/context/auto-ledger.md` before acting.
|
|
112
|
+
- Treat `.lab/context/auto-mode.md` as the control contract, `.lab/context/auto-status.md` as the live summary, and `.lab/context/auto-ledger.md` as the runtime ledger.
|
|
113
113
|
- Require `.lab/context/auto-mode.md` to expose `Primary gate`, `Secondary guard`, `Promotion condition`, `Stop reason`, and `Escalation reason` before execution.
|
|
114
114
|
- Require `Autonomy level` and `Approval status` in `.lab/context/auto-mode.md` before execution.
|
|
115
115
|
- Start every `/lab:auto` run with a visible `Auto preflight` summary that reports files read plus `Autonomy level`, `Approval status`, `Allowed stages`, `Terminal goal`, `Primary gate`, and `Secondary guard`.
|
|
116
116
|
- If any required preflight field is missing or inconsistent, stop before any loop action. Do not present a fake auto summary as if the loop were armed.
|
|
117
|
+
- Keep `.lab/context/auto-ledger.md` updated with the active owner, observed state, and resume boundary while the loop is live.
|
|
117
118
|
- Treat `L1` as safe-run validation, `L2` as bounded iteration, and `L3` as aggressive campaign mode.
|
|
118
119
|
- Surface the level guide every time `/lab:auto` starts, and make the detailed guide mandatory when the user omits the level or mixes it with a paper layer, phase, or table target.
|
|
119
120
|
- Reuse `/lab:run`, `/lab:iterate`, `/lab:review`, `/lab:report`, and optional `/lab:write` instead of inventing a second workflow.
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
- `.lab/context/terminology-lock.md`
|
|
23
23
|
- `.lab/context/auto-mode.md`
|
|
24
24
|
- `.lab/context/auto-status.md`
|
|
25
|
+
- `.lab/context/auto-ledger.md`
|
|
25
26
|
- `.lab/context/auto-outcome.md`
|
|
26
27
|
|
|
27
28
|
## Context Write Set
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
- `.lab/context/summary.md`
|
|
37
38
|
- `.lab/context/session-brief.md`
|
|
38
39
|
- `.lab/context/auto-status.md`
|
|
40
|
+
- `.lab/context/auto-ledger.md`
|
|
39
41
|
- `.lab/context/auto-outcome.md`
|
|
40
42
|
|
|
41
43
|
## Boundary Rules
|
|
@@ -48,6 +50,7 @@
|
|
|
48
50
|
- Treat `Sanity and Alternative-Explanation Checks` as the anomaly gate for automation. When a rung yields all-null outputs, suspiciously identical runs, no-op deltas, or impl/result mismatches, pause promotion logic until implementation reality checks, alternative explanations, and at least one cross-check are recorded.
|
|
49
51
|
- Treat paper-template selection as an explicit write-time gate, not as a silent fallback, when the loop is about to create `.tex` deliverables for the first time.
|
|
50
52
|
- Treat `.lab/context/auto-mode.md` as a visible control plane. The contract should make the primary gate, secondary guard, promotion condition, stop reason, and escalation reason explicit before execution starts.
|
|
53
|
+
- Treat `.lab/context/auto-ledger.md` as the live runtime ledger for owner identity, observed state, checkpoint progress, continue boundary, stop boundary, escalation boundary, and resume read set.
|
|
51
54
|
- The contract must declare `Autonomy level` and `Approval status`, and execution starts only when approval is explicitly set to `approved`.
|
|
52
55
|
- The contract must also declare a concrete terminal goal:
|
|
53
56
|
- `rounds`
|
|
@@ -72,6 +75,11 @@
|
|
|
72
75
|
- Keep a poll-based waiting loop instead of sleeping blindly.
|
|
73
76
|
- Do not treat a short watcher such as `sleep 30`, a one-shot `pgrep`, or a single `metrics.json` probe as the rung command when the real experiment is still running.
|
|
74
77
|
- Bind each rung to the real long-running command or process that owns the experiment result.
|
|
78
|
+
- Record the active owner as one of:
|
|
79
|
+
- `local-process`
|
|
80
|
+
- `local-runner`
|
|
81
|
+
- `remote-runner`
|
|
82
|
+
- Every nonterminal `/lab:auto` state must remain resumable from `.lab/context/auto-ledger.md` plus `.lab/context/auto-status.md`.
|
|
75
83
|
- Start every real `/lab:auto` run with a visible `Auto preflight` block before any execution summary or action plan. That first visible output should list:
|
|
76
84
|
- files read
|
|
77
85
|
- `Autonomy level`
|
|
@@ -82,6 +90,7 @@
|
|
|
82
90
|
- `Secondary guard`
|
|
83
91
|
- If any of those preflight fields are missing, stale, or inconsistent, stop before execution and report the blocking field directly.
|
|
84
92
|
- Always write a canonical `.lab/context/auto-outcome.md` when the run completes, stops, or fails.
|
|
93
|
+
- Always keep `.lab/context/auto-ledger.md` in sync with the current active owner while the loop is live.
|
|
85
94
|
- Keep handoff wording stable across auto outcomes and downstream report or write handoffs: record completed work, frozen scope, allowed next action, required read set for the next owner, and the accept or revise or reject boundary.
|
|
86
95
|
- When the evaluation protocol declares structured ladder rungs, execute them as a foreground rung state machine:
|
|
87
96
|
- each rung must declare `Stage`, `Goal`, `Command`, `Watch`, `Gate`, `On pass`, `On fail`, and `On stop`
|