create-principles-disciple 1.146.8 → 1.147.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_release/product-identity.json +1 -1
- package/codex-adapter/dist/ingestion/explicit-owner-statement.d.ts +6 -0
- package/codex-adapter/dist/ingestion/explicit-owner-statement.js +6 -0
- package/codex-adapter/dist/pd-hook.js +5 -2
- package/codex-adapter/dist/worker/workspace-worker.d.ts +13 -1
- package/codex-adapter/dist/worker/workspace-worker.js +26 -3
- package/codex-adapter/package.json +1 -1
- package/console/dist/server/models/ExaminerEvidenceModel.d.ts +159 -0
- package/console/dist/server/models/ExaminerEvidenceModel.js +274 -0
- package/console/dist/server/models/ReceiptsConsoleModel.js +15 -1
- package/console/dist/server/routes/examiner-executor.d.ts +40 -4
- package/console/dist/server/routes/examiner-executor.js +54 -45
- package/console/dist/server/routes/examiner-runs.d.ts +196 -0
- package/console/dist/server/routes/examiner-runs.js +938 -0
- package/console/dist/server/routes/examiner-study-inputs.d.ts +147 -0
- package/console/dist/server/routes/examiner-study-inputs.js +263 -0
- package/console/dist/server/routes/examiner.d.ts +26 -17
- package/console/dist/server/routes/examiner.js +261 -12
- package/console/dist/types.d.ts +1 -0
- package/console/dist/web/assets/app.css +92 -0
- package/console/dist/web/assets/app.js +38 -32
- package/console/lab/lib/challenge-openclaw-host.mjs +17 -0
- package/console/lab/lib/challenge-runner.mjs +9 -0
- package/console/lab/lib/examiner-study.mjs +55 -0
- package/core/dist/runtime-v2/index.d.ts +2 -2
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +1 -1
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/pain-ingress-payload.d.ts +13 -1
- package/core/dist/runtime-v2/pain-ingress-payload.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-ingress-payload.js +21 -2
- package/core/dist/runtime-v2/pain-ingress-payload.js.map +1 -1
- package/core/dist/runtime-v2/pain-ingress.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-ingress.js +3 -0
- package/core/dist/runtime-v2/pain-ingress.js.map +1 -1
- package/core/dist/runtime-v2/signal-collector/confidence-gate.d.ts +35 -0
- package/core/dist/runtime-v2/signal-collector/confidence-gate.d.ts.map +1 -0
- package/core/dist/runtime-v2/signal-collector/confidence-gate.js +48 -0
- package/core/dist/runtime-v2/signal-collector/confidence-gate.js.map +1 -0
- package/core/dist/runtime-v2/signal-collector/index.d.ts +1 -0
- package/core/dist/runtime-v2/signal-collector/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/signal-collector/index.js +1 -0
- package/core/dist/runtime-v2/signal-collector/index.js.map +1 -1
- package/core/dist/runtime-v2/signal-collector/keyword-stage.d.ts +4 -6
- package/core/dist/runtime-v2/signal-collector/keyword-stage.d.ts.map +1 -1
- package/core/dist/runtime-v2/signal-collector/keyword-stage.js +100 -9
- package/core/dist/runtime-v2/signal-collector/keyword-stage.js.map +1 -1
- package/core/dist/runtime-v2/store/context/sqlite-context-assembler.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/context/sqlite-context-assembler.js +8 -4
- package/core/dist/runtime-v2/store/context/sqlite-context-assembler.js.map +1 -1
- package/core/package.json +1 -1
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +52 -1
- package/dist/installer.js.map +1 -1
- package/dist/installers/mcp-registration-conflict.d.ts +35 -0
- package/dist/installers/mcp-registration-conflict.d.ts.map +1 -0
- package/dist/installers/mcp-registration-conflict.js +559 -0
- package/dist/installers/mcp-registration-conflict.js.map +1 -0
- package/dist/installers/openclaw-host-installer.d.ts.map +1 -1
- package/dist/installers/openclaw-host-installer.js +20 -0
- package/dist/installers/openclaw-host-installer.js.map +1 -1
- package/dist/installers/workbuddy-host-installer.d.ts.map +1 -1
- package/dist/installers/workbuddy-host-installer.js +124 -2
- package/dist/installers/workbuddy-host-installer.js.map +1 -1
- package/dist/mcp-server-layout-compat.d.ts +18 -0
- package/dist/mcp-server-layout-compat.d.ts.map +1 -0
- package/dist/mcp-server-layout-compat.js +22 -0
- package/dist/mcp-server-layout-compat.js.map +1 -0
- package/host-runtime/dist/governance-signal-admission.d.ts +147 -4
- package/host-runtime/dist/governance-signal-admission.js +1065 -60
- package/host-runtime/dist/index.d.ts +2 -0
- package/host-runtime/dist/index.js +2 -0
- package/host-runtime/dist/signal-classifier.d.ts +46 -0
- package/host-runtime/dist/signal-classifier.js +167 -0
- package/host-runtime/dist/signal-stage2-cycle.d.ts +42 -0
- package/host-runtime/dist/signal-stage2-cycle.js +184 -0
- package/host-runtime/package.json +1 -1
- package/install-layout/dist/index.d.ts +15 -0
- package/install-layout/dist/index.js +10 -0
- package/install-layout/package.json +1 -1
- package/package.json +9 -7
- package/pd-cli/dist/commands/health-codex.d.ts.map +1 -1
- package/pd-cli/dist/commands/health-codex.js +12 -0
- package/pd-cli/dist/commands/health-codex.js.map +1 -1
- package/pd-cli/dist/commands/health-mcp.d.ts +74 -0
- package/pd-cli/dist/commands/health-mcp.d.ts.map +1 -0
- package/pd-cli/dist/commands/health-mcp.js +723 -0
- package/pd-cli/dist/commands/health-mcp.js.map +1 -0
- package/pd-cli/dist/commands/health.d.ts +2 -0
- package/pd-cli/dist/commands/health.d.ts.map +1 -1
- package/pd-cli/dist/commands/health.js +11 -6
- package/pd-cli/dist/commands/health.js.map +1 -1
- package/pd-mcp-server/dist/budget.d.ts +15 -0
- package/pd-mcp-server/dist/budget.js +19 -0
- package/pd-mcp-server/dist/deadline.d.ts +75 -0
- package/pd-mcp-server/dist/deadline.js +136 -0
- package/pd-mcp-server/dist/envelope.d.ts +52 -0
- package/pd-mcp-server/dist/envelope.js +63 -0
- package/pd-mcp-server/dist/index.d.ts +10 -0
- package/pd-mcp-server/dist/index.js +76 -0
- package/pd-mcp-server/dist/logging.d.ts +12 -0
- package/pd-mcp-server/dist/logging.js +42 -0
- package/pd-mcp-server/dist/server.d.ts +90 -0
- package/pd-mcp-server/dist/server.js +178 -0
- package/pd-mcp-server/dist/version.d.ts +13 -0
- package/pd-mcp-server/dist/version.js +13 -0
- package/pd-mcp-server/dist/workspace.d.ts +23 -0
- package/pd-mcp-server/dist/workspace.js +77 -0
- package/pd-mcp-server/package.json +49 -0
- package/plugin/dist/bundle.js +414 -412
- package/plugin/dist/core/mcp-binding.d.ts +41 -0
- package/plugin/dist/index.d.ts +33 -0
- package/plugin/dist/openclaw.plugin.json +10 -1
- package/plugin/dist/utils/workspace-resolver.d.ts +17 -0
- package/plugin/openclaw.plugin.json +10 -1
- package/plugin/package.json +2 -1
- package/release-manager/dist/installer.js +52 -1
- package/release-manager/dist/installers/mcp-registration-conflict.js +559 -0
- package/release-manager/dist/installers/openclaw-host-installer.js +20 -0
- package/release-manager/dist/installers/workbuddy-host-installer.js +124 -2
- package/release-manager/dist/mcp-server-layout-compat.js +22 -0
- package/release-manager/package.json +7 -5
- package/workbuddy-adapter/dist/pd-hook.js +28 -5
- package/workbuddy-adapter/dist/worker/workspace-worker.d.ts +6 -1
- package/workbuddy-adapter/dist/worker/workspace-worker.js +24 -3
- package/workbuddy-adapter/package.json +1 -1
- package/workbuddy-adapter/plugin/.codebuddy-plugin/plugin.json +2 -2
- package/workbuddy-adapter/plugin/.mcp.json +10 -0
- package/workbuddy-adapter/plugin/mcp/launcher.cjs +347 -0
|
@@ -149,8 +149,6 @@ export declare function writableRootProblem(roots: StudyRoots): string | null;
|
|
|
149
149
|
* mutation — a check done only at startup is not evidence about now.
|
|
150
150
|
*/
|
|
151
151
|
export declare function writableStudyDirProblem(roots: StudyRoots, studyDir: string): string | null;
|
|
152
|
-
/** Study status projection (manifest + ledger settlement counts + report status). */
|
|
153
|
-
export declare function studyStatusPayload(studyDir: string): Record<string, unknown>;
|
|
154
152
|
/** Read-only Report V2 payload + replay verification. */
|
|
155
153
|
export declare function studyReportPayload(modules: LabModules, studyDir: string): Record<string, unknown> | null;
|
|
156
154
|
export declare class ExaminerPreflightBlockedError extends Error {
|
|
@@ -179,7 +177,9 @@ export declare function preflightCheck(modules: LabModules, studyDir: string, co
|
|
|
179
177
|
* before RUN_STARTED — but a blocker here means the runner is not called at
|
|
180
178
|
* all, so no attempt is reserved and no actor is created.
|
|
181
179
|
*/
|
|
182
|
-
export declare function offlineStudyPass(modules: LabModules, studyDir: string, context?: StudyDirContext
|
|
180
|
+
export declare function offlineStudyPass(modules: LabModules, studyDir: string, context?: StudyDirContext, admissionCheck?: (input: {
|
|
181
|
+
studyDir: string;
|
|
182
|
+
}) => void | Promise<void>): Promise<{
|
|
183
183
|
units: Record<string, unknown>[];
|
|
184
184
|
}>;
|
|
185
185
|
export declare function reportMaterialize(modules: LabModules, studyDir: string, now: string, context: StudyDirContext | undefined, canWrite: () => {
|
|
@@ -217,6 +217,26 @@ export type CreateStudyInputs = {
|
|
|
217
217
|
}[];
|
|
218
218
|
repetitionsPerCase: number;
|
|
219
219
|
};
|
|
220
|
+
/**
|
|
221
|
+
* v0.10.1 host-backed inputs (SPEC §4/§6/§7): the frozen production snapshot,
|
|
222
|
+
* the runtime-derived RuleCode facts and the real pain lineage. All gathered
|
|
223
|
+
* by the caller (examiner-study-inputs) from existing authorities; this layer
|
|
224
|
+
* only freezes them into the study directory. Suite CASES are compiled here
|
|
225
|
+
* with the minted study handle (case ids are study-scoped).
|
|
226
|
+
*/
|
|
227
|
+
export type HostBackedStudyInputs = {
|
|
228
|
+
productionSnapshot: Record<string, unknown>;
|
|
229
|
+
ruleCodeFacts: {
|
|
230
|
+
ruleCodes: {
|
|
231
|
+
ruleCodeId: string;
|
|
232
|
+
principleId: string;
|
|
233
|
+
baselineState: 'ON' | 'OFF';
|
|
234
|
+
}[];
|
|
235
|
+
principleRuleCodeIndependence: boolean | null;
|
|
236
|
+
targetRuleCodeCount: number;
|
|
237
|
+
};
|
|
238
|
+
painRefs: string[];
|
|
239
|
+
};
|
|
220
240
|
/** Server-minted study handle + directory (crypto-random; never request-derived). */
|
|
221
241
|
export declare function mintStudyHandle(roots: StudyRoots): {
|
|
222
242
|
studyId: string;
|
|
@@ -241,6 +261,10 @@ export declare function materializeConsoleStudy(modules: LabModules, labRoot: st
|
|
|
241
261
|
ruleCount?: number;
|
|
242
262
|
}[];
|
|
243
263
|
repetitionsPerCase: number;
|
|
264
|
+
/** v0.10.1: catalog suite key (validated by the caller against the fixed list). */
|
|
265
|
+
suiteKey?: string;
|
|
266
|
+
/** v0.10.1: host-backed inputs (production snapshot / RuleCode facts / pain refs). */
|
|
267
|
+
hostBacked?: HostBackedStudyInputs;
|
|
244
268
|
}): Promise<{
|
|
245
269
|
studyId: string;
|
|
246
270
|
targetPrincipleId: string;
|
|
@@ -253,8 +277,20 @@ export declare function materializeConsoleStudy(modules: LabModules, labRoot: st
|
|
|
253
277
|
* receives already-validated server data only. Synthetic calibration cases
|
|
254
278
|
* with SYNTHETIC_OFFLINE gold provenance — offline contract validation,
|
|
255
279
|
* never real-host behavior.
|
|
280
|
+
*
|
|
281
|
+
* v0.10.1: when `hostBacked` inputs are supplied, the study freezes the
|
|
282
|
+
* caller-selected suite cases, the RUNTIME-derived RuleCode facts and the
|
|
283
|
+
* real pain lineage, and `production-snapshot.json` records where the A/B
|
|
284
|
+
* starting point differs from production (SPEC §4.4/§6.4). The frozen
|
|
285
|
+
* binding files the lab verifies are unchanged — the snapshot is an
|
|
286
|
+
* additional frozen fact, hashed by nothing that the runner recomputes, so
|
|
287
|
+
* appending it after writeExaminerStudyArtifacts keeps every existing
|
|
288
|
+
* verification intact (events.jsonl rides the same discipline).
|
|
256
289
|
*/
|
|
257
|
-
export declare function materializeOfflineStudy(modules: LabModules, labRoot: string, inputs: CreateStudyInputs
|
|
290
|
+
export declare function materializeOfflineStudy(modules: LabModules, labRoot: string, inputs: CreateStudyInputs & {
|
|
291
|
+
suiteKey?: string;
|
|
292
|
+
hostBacked?: HostBackedStudyInputs;
|
|
293
|
+
}): Promise<{
|
|
258
294
|
manifest: Record<string, unknown>;
|
|
259
295
|
pairAssignment: Record<string, unknown>;
|
|
260
296
|
}>;
|
|
@@ -277,34 +277,6 @@ export function writableStudyDirProblem(roots, studyDir) {
|
|
|
277
277
|
return STUDY_PATH_PROBLEM.notInside;
|
|
278
278
|
return null;
|
|
279
279
|
}
|
|
280
|
-
/** Study status projection (manifest + ledger settlement counts + report status). */
|
|
281
|
-
export function studyStatusPayload(studyDir) {
|
|
282
|
-
const manifest = readStudyJson(studyDir, 'examiner-manifest.json');
|
|
283
|
-
const eventsFile = path.join(studyDir, 'events.jsonl');
|
|
284
|
-
const events = fs.existsSync(eventsFile)
|
|
285
|
-
? fs.readFileSync(eventsFile, 'utf8').trim().split('\n').filter(Boolean).map((line) => asJsonObject(JSON.parse(line), eventsFile))
|
|
286
|
-
: [];
|
|
287
|
-
const counts = {};
|
|
288
|
-
for (const event of events) {
|
|
289
|
-
if (typeof event.type !== 'string' || !event.type.startsWith('UNIT_FINISHED'))
|
|
290
|
-
continue;
|
|
291
|
-
const result = String(event.result ?? 'UNKNOWN');
|
|
292
|
-
counts[result] = (counts[result] ?? 0) + 1;
|
|
293
|
-
}
|
|
294
|
-
const reportExists = fs.existsSync(path.join(studyDir, 'behavior-report-v2.json'));
|
|
295
|
-
const report = reportExists ? readStudyJson(studyDir, 'behavior-report-v2.json') : null;
|
|
296
|
-
return {
|
|
297
|
-
studyId: path.basename(studyDir),
|
|
298
|
-
experimentMode: manifest.experimentMode,
|
|
299
|
-
interventionType: manifest.interventionType,
|
|
300
|
-
targetPrincipleId: stringField(manifest.targetPrinciple, 'id'),
|
|
301
|
-
plannedPairs: manifest.plannedPairs,
|
|
302
|
-
runStarted: events.some((event) => event.type === 'RUN_STARTED'),
|
|
303
|
-
runFinished: events.some((event) => event.type === 'RUN_FINISHED'),
|
|
304
|
-
unitStates: counts,
|
|
305
|
-
reportStatus: report ? (report.status) : null,
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
280
|
/** Read-only Report V2 payload + replay verification. */
|
|
309
281
|
export function studyReportPayload(modules, studyDir) {
|
|
310
282
|
if (!fs.existsSync(path.join(studyDir, 'behavior-report-v2.json')))
|
|
@@ -409,7 +381,7 @@ export function preflightCheck(modules, studyDir, context) {
|
|
|
409
381
|
* before RUN_STARTED — but a blocker here means the runner is not called at
|
|
410
382
|
* all, so no attempt is reserved and no actor is created.
|
|
411
383
|
*/
|
|
412
|
-
export async function offlineStudyPass(modules, studyDir, context) {
|
|
384
|
+
export async function offlineStudyPass(modules, studyDir, context, admissionCheck) {
|
|
413
385
|
// The write boundary is re-evaluated here (not only at route start): a
|
|
414
386
|
// directory swapped for a symlink/junction after access resolution must not
|
|
415
387
|
// reach the runner.
|
|
@@ -435,6 +407,9 @@ export async function offlineStudyPass(modules, studyDir, context) {
|
|
|
435
407
|
},
|
|
436
408
|
actorFactory: ({ armId }) => modules.actor.createScriptedStub({ actions: didacticActions(armId) }),
|
|
437
409
|
execution: { mode: 'offline-stub' },
|
|
410
|
+
// P1-3: the runner invokes this UNDER the study write lock, before
|
|
411
|
+
// RUN_STARTED / reservation / actor creation. Supplied by the route layer.
|
|
412
|
+
...(admissionCheck ? { admissionCheck } : {}),
|
|
438
413
|
});
|
|
439
414
|
}
|
|
440
415
|
export function reportMaterialize(modules, studyDir, now, context, canWrite) {
|
|
@@ -554,6 +529,8 @@ export async function materializeConsoleStudy(modules, labRoot, roots, selection
|
|
|
554
529
|
target,
|
|
555
530
|
ledgerPrinciples: selection.ledgerPrinciples,
|
|
556
531
|
repetitionsPerCase: selection.repetitionsPerCase,
|
|
532
|
+
suiteKey: selection.suiteKey,
|
|
533
|
+
hostBacked: selection.hostBacked,
|
|
557
534
|
});
|
|
558
535
|
// The lab created the directory tree; verify the REAL location before the
|
|
559
536
|
// ownership marker (the trust anchor) is written into it.
|
|
@@ -575,40 +552,67 @@ export async function materializeConsoleStudy(modules, labRoot, roots, selection
|
|
|
575
552
|
* receives already-validated server data only. Synthetic calibration cases
|
|
576
553
|
* with SYNTHETIC_OFFLINE gold provenance — offline contract validation,
|
|
577
554
|
* never real-host behavior.
|
|
555
|
+
*
|
|
556
|
+
* v0.10.1: when `hostBacked` inputs are supplied, the study freezes the
|
|
557
|
+
* caller-selected suite cases, the RUNTIME-derived RuleCode facts and the
|
|
558
|
+
* real pain lineage, and `production-snapshot.json` records where the A/B
|
|
559
|
+
* starting point differs from production (SPEC §4.4/§6.4). The frozen
|
|
560
|
+
* binding files the lab verifies are unchanged — the snapshot is an
|
|
561
|
+
* additional frozen fact, hashed by nothing that the runner recomputes, so
|
|
562
|
+
* appending it after writeExaminerStudyArtifacts keeps every existing
|
|
563
|
+
* verification intact (events.jsonl rides the same discipline).
|
|
578
564
|
*/
|
|
579
565
|
export async function materializeOfflineStudy(modules, labRoot, inputs) {
|
|
580
566
|
const fixtureUrl = pathToFileURL(path.join(labRoot, 'lib', 'examiner-v07-cases.mjs')).href;
|
|
581
567
|
const fixture = (await import(fixtureUrl));
|
|
582
568
|
const freezeUrl = pathToFileURL(path.join(labRoot, 'lib', 'challenge-freeze.mjs')).href;
|
|
583
569
|
const freeze = (await import(freezeUrl));
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
570
|
+
// v0.10.1 §9: a catalog suite key compiles the selected variants under the
|
|
571
|
+
// MINTED study handle (case ids are study-scoped); absent key = v0.9 default.
|
|
572
|
+
let caseDefs;
|
|
573
|
+
if (typeof inputs.suiteKey === 'string') {
|
|
574
|
+
const { suiteCaseDefs, isSuiteKey } = await import('./examiner-study-inputs.js');
|
|
575
|
+
if (!isSuiteKey(inputs.suiteKey))
|
|
576
|
+
throw new Error(`materializeOfflineStudy: unknown suite key '${inputs.suiteKey}'`);
|
|
577
|
+
caseDefs = await suiteCaseDefs(labRoot, inputs.studyId, inputs.suiteKey);
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
caseDefs = [
|
|
581
|
+
fixture.makeExaminerV3Case({ caseId: `${inputs.studyId}-CASE-01` }),
|
|
582
|
+
fixture.makeExaminerV3Case({ caseId: `${inputs.studyId}-CASE-02`, boundary: true }),
|
|
583
|
+
];
|
|
584
|
+
}
|
|
588
585
|
const principleFrozen = { contentJson: { principleDraft: { statement: inputs.target.text } } };
|
|
586
|
+
const painRefs = inputs.hostBacked?.painRefs ?? [];
|
|
589
587
|
const mapping = {
|
|
590
588
|
schema: 'PainMechanismMappingV1',
|
|
591
589
|
mappingId: `PMM-${inputs.studyId}`,
|
|
592
590
|
principleContentHash: freeze.hashOfCanonical(principleFrozen.contentJson),
|
|
593
|
-
observedFailure:
|
|
591
|
+
observedFailure: painRefs.length > 0
|
|
592
|
+
? `console-originated offline calibration mapped to the target principle's real pain lineage (${painRefs.join(', ')})`
|
|
593
|
+
: 'console-originated offline calibration (synthetic)',
|
|
594
594
|
failureMechanism: 'assertion_without_observation',
|
|
595
595
|
ownerGroundedGoal: 'offline contract validation for the console entry',
|
|
596
|
+
painRefs,
|
|
596
597
|
};
|
|
597
598
|
mapping.mappingHash = freeze.freezeMapping(mapping).mappingHash;
|
|
598
|
-
|
|
599
|
+
const ruleCodeFacts = inputs.hostBacked?.ruleCodeFacts ?? {
|
|
600
|
+
// P1-5 fallback (pre-v0.10.1 shape): ledger rule count as the only fact.
|
|
601
|
+
ruleCodes: [],
|
|
602
|
+
principleRuleCodeIndependence: (inputs.target.ruleCount ?? 0) === 0 ? true : null,
|
|
603
|
+
targetRuleCodeCount: inputs.target.ruleCount ?? 0,
|
|
604
|
+
};
|
|
605
|
+
const writeResult = modules.study.writeExaminerStudyArtifacts({
|
|
599
606
|
studyId: inputs.studyId,
|
|
600
607
|
experimentMode: inputs.experimentMode,
|
|
601
608
|
hepsPrinciples: { principles: inputs.ledgerPrinciples, targetPrincipleId: inputs.target.id, host: 'offline-stub' },
|
|
602
|
-
// P1-5: RuleCode facts are EVIDENCE, never a default.
|
|
603
|
-
//
|
|
604
|
-
//
|
|
605
|
-
//
|
|
606
|
-
//
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
principleRuleCodeIndependence: (inputs.target.ruleCount ?? 0) === 0 ? true : null,
|
|
610
|
-
targetRuleCodeCount: inputs.target.ruleCount ?? 0,
|
|
611
|
-
},
|
|
609
|
+
// P1-5: RuleCode facts are EVIDENCE, never a default. With hostBacked
|
|
610
|
+
// inputs the facts come from RUNTIME activation rows cross-checked against
|
|
611
|
+
// the ledger count (examiner-study-inputs.deriveStudyRuleCodeFacts); an
|
|
612
|
+
// unresolvable attribution still lands as INTERVENTION_NOT_IDENTIFIABLE,
|
|
613
|
+
// which makes the creation refuse (422). A zero count on BOTH sources is
|
|
614
|
+
// a real, evidenced fact: no RuleCode exists to couple.
|
|
615
|
+
ruleCodeFacts,
|
|
612
616
|
caseDefs,
|
|
613
617
|
principleFrozen,
|
|
614
618
|
mapping,
|
|
@@ -624,5 +628,10 @@ export async function materializeOfflineStudy(modules, labRoot, inputs) {
|
|
|
624
628
|
invalidationPolicy: { hard: ['preregistration tampered', 'gold leak', 'shared memory across arms'], infrastructure: 'single failures stay UNKNOWN' },
|
|
625
629
|
goldProvenance: 'SYNTHETIC_OFFLINE',
|
|
626
630
|
studyDir: inputs.studyDir,
|
|
631
|
+
// P1-1: the production baseline freezes WITH the study — hash-bound into
|
|
632
|
+
// the manifest, the binding and the anchor; post-freeze modification is
|
|
633
|
+
// drift that preflight/runner refuse.
|
|
634
|
+
...(inputs.hostBacked?.productionSnapshot ? { productionSnapshot: inputs.hostBacked.productionSnapshot } : {}),
|
|
627
635
|
});
|
|
636
|
+
return writeResult;
|
|
628
637
|
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { type LabModules, type StudyRoots } from './examiner-executor.js';
|
|
2
|
+
export declare const CONSOLE_RUN_SCHEMA = "examiner-console-run.v1";
|
|
3
|
+
export declare const CONSOLE_RUN_EVENT_TYPES: readonly ['CONSOLE_RUN_ACCEPTED', 'CONSOLE_RUN_FINISHED', 'CONSOLE_RUN_FAILED', 'CONSOLE_RUN_ABANDONED'];
|
|
4
|
+
export type ConsoleRunMode = 'host-dry-run' | 'f2-real-host';
|
|
5
|
+
export type ConsoleRunEventType = (typeof CONSOLE_RUN_EVENT_TYPES)[number];
|
|
6
|
+
export declare const CONSOLE_RUN_MODES: readonly ConsoleRunMode[];
|
|
7
|
+
export interface ConsoleRunEvent {
|
|
8
|
+
type: ConsoleRunEventType;
|
|
9
|
+
schema: typeof CONSOLE_RUN_SCHEMA;
|
|
10
|
+
runId: string;
|
|
11
|
+
studyId: string;
|
|
12
|
+
mode: ConsoleRunMode;
|
|
13
|
+
acceptedAt: string;
|
|
14
|
+
pid: number;
|
|
15
|
+
owner: 'pd-console';
|
|
16
|
+
finishedAt?: string;
|
|
17
|
+
outcome?: string;
|
|
18
|
+
reason?: string;
|
|
19
|
+
checks?: unknown[];
|
|
20
|
+
}
|
|
21
|
+
export interface ConsoleRunState {
|
|
22
|
+
runId: string;
|
|
23
|
+
mode: ConsoleRunMode;
|
|
24
|
+
status: 'RUNNING' | 'FINISHED' | 'FAILED' | 'ABANDONED';
|
|
25
|
+
acceptedAt: string;
|
|
26
|
+
finishedAt: string | null;
|
|
27
|
+
outcome: string | null;
|
|
28
|
+
reason: string | null;
|
|
29
|
+
/** The dry-run checklist carried by the terminal event (never re-invented). */
|
|
30
|
+
checks?: unknown[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* P1-4: the study's event ledger is UNREADABLE for a reason the Owner must
|
|
34
|
+
* see — a corrupt or permission-blocked ledger is never silently treated as
|
|
35
|
+
* "no runs". Only a genuinely absent file (first-run condition) yields [].
|
|
36
|
+
*/
|
|
37
|
+
export declare class ConsoleLedgerUnreadableError extends Error {
|
|
38
|
+
readonly code: 'ledger_unreadable' | 'ledger_corrupt';
|
|
39
|
+
constructor(code: 'ledger_unreadable' | 'ledger_corrupt', message: string);
|
|
40
|
+
}
|
|
41
|
+
export interface OwnedProbeDir {
|
|
42
|
+
path: string;
|
|
43
|
+
dev: bigint;
|
|
44
|
+
ino: bigint;
|
|
45
|
+
}
|
|
46
|
+
export interface StudyDirPin {
|
|
47
|
+
real: string;
|
|
48
|
+
dev: bigint;
|
|
49
|
+
ino: bigint;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Remove the dry-run probe directory ONLY when it is still the exact plain
|
|
53
|
+
* directory this run created (identity dev+ino), still resolves inside the
|
|
54
|
+
* same study directory, and the study directory itself has not been swapped.
|
|
55
|
+
* Any unprovable case keeps the residue and reports why — an unknown path is
|
|
56
|
+
* never deleted.
|
|
57
|
+
*/
|
|
58
|
+
export declare function removeProbeDirIfStillOwned(probe: OwnedProbeDir, study: StudyDirPin): {
|
|
59
|
+
removed: boolean;
|
|
60
|
+
reason?: string;
|
|
61
|
+
};
|
|
62
|
+
/** Append one console-run lifecycle event to the study's events ledger. */
|
|
63
|
+
export declare function appendConsoleRunEvent(roots: StudyRoots, studyDir: string, event: ConsoleRunEvent): void;
|
|
64
|
+
export declare class OfflineRunInFlightError extends Error {
|
|
65
|
+
constructor();
|
|
66
|
+
}
|
|
67
|
+
export declare function mintRunId(): string;
|
|
68
|
+
/** Derive the console-run states from the study ledger (never stored separately). */
|
|
69
|
+
export declare function readConsoleRuns(roots: StudyRoots, studyDir: string): ConsoleRunState[];
|
|
70
|
+
export declare function currentConsoleRun(runs: ConsoleRunState[]): ConsoleRunState | null;
|
|
71
|
+
/**
|
|
72
|
+
* Study status projection (manifest + ledger settlement counts + report status).
|
|
73
|
+
* The DISPLAY projection tolerates single corrupt lines — the authoritative
|
|
74
|
+
* console-run reader refuses them and the route reports `ledgerProblem` — but
|
|
75
|
+
* it reads the ledger through the SAME secure fd boundary (never by path).
|
|
76
|
+
*/
|
|
77
|
+
export declare function studyStatusPayload(roots: StudyRoots, studyDir: string): Record<string, unknown>;
|
|
78
|
+
/**
|
|
79
|
+
* P1-4: the whole acceptance transaction — read current runs, check the
|
|
80
|
+
* single-live constraint (async AND offline in-flight), verify the study is
|
|
81
|
+
* still writable, append CONSOLE_RUN_ACCEPTED — happens under the EXISTING
|
|
82
|
+
* lab study write lock, so two concurrent requests can never both pass the
|
|
83
|
+
* check. The lock guards ONLY the acceptance; the async execution itself
|
|
84
|
+
* runs unlocked (settlement appends race-free through the same lock the
|
|
85
|
+
* runner already takes for real executions).
|
|
86
|
+
*/
|
|
87
|
+
export declare class ConsoleRunInProgressError extends Error {
|
|
88
|
+
readonly runId: string;
|
|
89
|
+
readonly mode: string;
|
|
90
|
+
readonly acceptedAt: string;
|
|
91
|
+
constructor(running: ConsoleRunState);
|
|
92
|
+
}
|
|
93
|
+
export interface ConsoleRunAcceptance {
|
|
94
|
+
runId: string;
|
|
95
|
+
acceptedAt: string;
|
|
96
|
+
}
|
|
97
|
+
export declare function acceptConsoleRun(roots: StudyRoots, studyDir: string, inputs: {
|
|
98
|
+
studyId: string;
|
|
99
|
+
mode: ConsoleRunMode;
|
|
100
|
+
labRoot: string;
|
|
101
|
+
}): Promise<ConsoleRunAcceptance>;
|
|
102
|
+
/**
|
|
103
|
+
* Append an async run's TERMINAL event under the study write lock. Recovery
|
|
104
|
+
* (which also holds the lock) therefore either sees the terminal event and
|
|
105
|
+
* skips, or appends ABANDONED first and the later terminal event wins — an
|
|
106
|
+
* already-settled run can never be reported as ABANDONED.
|
|
107
|
+
*/
|
|
108
|
+
export declare function appendConsoleRunEventLocked(input: {
|
|
109
|
+
labRoot: string;
|
|
110
|
+
roots: StudyRoots;
|
|
111
|
+
studyDir: string;
|
|
112
|
+
}, event: ConsoleRunEvent): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Offline→async half of the single-active contract (P1-3).
|
|
115
|
+
*
|
|
116
|
+
* The lab runner calls this UNDER the study write lock, BEFORE RUN_STARTED,
|
|
117
|
+
* attempt reservation or actor creation. The caller (route layer) supplies it
|
|
118
|
+
* so `examiner-runs` and `examiner-executor` never import each other.
|
|
119
|
+
*/
|
|
120
|
+
export declare function assertOfflineAdmissionAllowed(roots: StudyRoots, studyDir: string): void;
|
|
121
|
+
/** Best-effort liveness probe for the process that accepted a run. */
|
|
122
|
+
export declare function processAlive(pid: number): boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Startup recovery: walk the WRITABLE root only (historical assets are never
|
|
125
|
+
* console-run targets) and abandon every unfinished console run whose
|
|
126
|
+
* accepting process is gone. An accepting process that is still alive keeps
|
|
127
|
+
* its run (another console server may legitimately be serving the workspace).
|
|
128
|
+
*
|
|
129
|
+
* Path safety: only names the server itself enumerated that pass the
|
|
130
|
+
* study-handle charset allowlist, each directory must resolve canonically
|
|
131
|
+
* INSIDE the writable root, and every ledger access re-proves containment at
|
|
132
|
+
* the point of use — a symlinked/junctioned entry is skipped, never followed.
|
|
133
|
+
*/
|
|
134
|
+
export declare function recoverUnfinishedConsoleRuns(roots: StudyRoots, labRoot: string): Promise<{
|
|
135
|
+
studyId: string;
|
|
136
|
+
runId: string;
|
|
137
|
+
}[]>;
|
|
138
|
+
export interface StageRRead {
|
|
139
|
+
present: boolean;
|
|
140
|
+
record: Record<string, unknown> | null;
|
|
141
|
+
problem: string | null;
|
|
142
|
+
}
|
|
143
|
+
export interface DryRunCheck {
|
|
144
|
+
id: string;
|
|
145
|
+
severity: 'blocker' | 'warning' | 'info';
|
|
146
|
+
ok: boolean;
|
|
147
|
+
detail: string;
|
|
148
|
+
}
|
|
149
|
+
export interface HostDryRunResult {
|
|
150
|
+
ok: boolean;
|
|
151
|
+
checks: DryRunCheck[];
|
|
152
|
+
authorization: {
|
|
153
|
+
present: boolean;
|
|
154
|
+
status: string | null;
|
|
155
|
+
executionMode: string | null;
|
|
156
|
+
model: string | null;
|
|
157
|
+
note: string;
|
|
158
|
+
};
|
|
159
|
+
hostProbe: {
|
|
160
|
+
cliVersion: string | null;
|
|
161
|
+
note: string;
|
|
162
|
+
};
|
|
163
|
+
modelCalls: 0;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The host dry-run: everything Gate 2 can check WITHOUT a model call. It is
|
|
167
|
+
* deliberately a checklist, not a pass/fail black box — the Owner sees exactly
|
|
168
|
+
* which prerequisite is missing before real execution can be requested.
|
|
169
|
+
*/
|
|
170
|
+
export declare function hostDryRunPass(input: {
|
|
171
|
+
modules: LabModules;
|
|
172
|
+
labRoot: string;
|
|
173
|
+
studyDir: string;
|
|
174
|
+
context: {
|
|
175
|
+
labRoot: string;
|
|
176
|
+
roots: StudyRoots;
|
|
177
|
+
} | undefined;
|
|
178
|
+
}): Promise<HostDryRunResult>;
|
|
179
|
+
export interface F2GateResult {
|
|
180
|
+
authorized: boolean;
|
|
181
|
+
reasons: string[];
|
|
182
|
+
record: Record<string, unknown> | null;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* P1-5: the console gate REUSES the lab's authoritative Stage R verifier
|
|
186
|
+
* (verifyStageRAuthorization) instead of a hand-rolled subset — schema,
|
|
187
|
+
* stage/status, the full study binding (studyId/suiteHash/mappingHash/
|
|
188
|
+
* graderHash/studyPlanHash), arms/cases/repetitions coverage, attemptCap,
|
|
189
|
+
* requestCap, the validity window, the retirement registry, revocation/
|
|
190
|
+
* halted, requestedActorIdentity and the F2 real-host probe evidence are ALL
|
|
191
|
+
* enforced. A record that is merely status=APPROVED fails every missing
|
|
192
|
+
* binding it does not carry. The runner re-verifies the same authority per
|
|
193
|
+
* unit under the study lock — this gate is a necessary condition, the runner
|
|
194
|
+
* is the authority.
|
|
195
|
+
*/
|
|
196
|
+
export declare function f2AuthorizationGate(labRoot: string, studyDir: string): Promise<F2GateResult>;
|