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
|
@@ -11,6 +11,12 @@ export type RecordOwnerStatementResult = {
|
|
|
11
11
|
* rollout to promote from). The receipt discloses it instead of failing.
|
|
12
12
|
*/
|
|
13
13
|
promotionDegraded?: string;
|
|
14
|
+
/**
|
|
15
|
+
* PainIngress warning when the Owner statement legitimately degraded
|
|
16
|
+
* (e.g. ingestion OFF → no persisted observation to use as behavior
|
|
17
|
+
* evidence). The Pain + task still exist; the receipt discloses the basis.
|
|
18
|
+
*/
|
|
19
|
+
ingressWarning?: string;
|
|
14
20
|
} | {
|
|
15
21
|
status: 'rejected';
|
|
16
22
|
reason: 'statement_empty' | 'statement_too_long' | 'transcript_unavailable' | 'lineage_invalid' | 'rate_limited' | 'continuation_failed';
|
|
@@ -84,6 +84,11 @@ export async function recordExplicitOwnerStatement(args) {
|
|
|
84
84
|
if (outcome.disposition === 'rate_limited') {
|
|
85
85
|
return { status: 'rejected', reason: 'rate_limited', nextAction: 'The per-root-session correction quota (5/hour) is exhausted; resubmit later — the statement was NOT recorded.' };
|
|
86
86
|
}
|
|
87
|
+
if (outcome.disposition !== 'admitted' && outcome.disposition !== 'already_admitted') {
|
|
88
|
+
// An explicit Owner statement bypasses keyword detection entirely, so it
|
|
89
|
+
// can never legitimately land in the Stage-2 review state machine.
|
|
90
|
+
return { status: 'rejected', reason: 'continuation_failed', nextAction: `unexpected admission disposition for an explicit statement: ${outcome.disposition}` };
|
|
91
|
+
}
|
|
87
92
|
// Promotion runs BEFORE the task enqueue (deliberate): the durable task is
|
|
88
93
|
// the receipt's hard guarantee, and promoting first closes the window in
|
|
89
94
|
// which the background worker could start the first diagnosis before the
|
|
@@ -126,5 +131,6 @@ export async function recordExplicitOwnerStatement(args) {
|
|
|
126
131
|
disposition: outcome.disposition === 'already_admitted' ? 'already_admitted' : 'admitted',
|
|
127
132
|
logicalObservationKey: candidate.logicalObservationKey,
|
|
128
133
|
...(promotionDegraded !== undefined ? { promotionDegraded } : {}),
|
|
134
|
+
...(ensured.ingressWarning !== undefined ? { ingressWarning: ensured.ingressWarning } : {}),
|
|
129
135
|
};
|
|
130
136
|
}
|
|
@@ -362,8 +362,11 @@ export async function processHookInvocation(rawStdin, _env = process.env, cwd =
|
|
|
362
362
|
const promotionNotice = receipt.promotionDegraded === 'conversation_ingestion_disabled'
|
|
363
363
|
? '会话证据提升已跳过:会话摄取未启用(按隐私边界未读取会话内容,也未写入会话观察)'
|
|
364
364
|
: `会话证据提升暂不可用:${receipt.promotionDegraded}`;
|
|
365
|
-
|
|
366
|
-
|
|
365
|
+
const ingressNotice = receipt.ingressWarning !== undefined
|
|
366
|
+
? `行为证据缺失,已按 Owner 陈述降级准入:${receipt.ingressWarning}`
|
|
367
|
+
: undefined;
|
|
368
|
+
explicitReceiptContext = `[PD] 已收到痛苦反馈(painId=${receipt.painId}, taskId=${receipt.taskId}${receipt.disposition === 'already_admitted' ? ', 重复提交已合并为同一信号' : ''})。后台将自动诊断;请在回复中如实转述本回执,不要声称仍在等待用户输入,也不要虚构诊断结论。${receipt.promotionDegraded !== undefined ? `(注意:${promotionNotice};诊断仍会基于你提交的陈述进行。)` : ''}${ingressNotice !== undefined ? `(注意:${ingressNotice};诊断仍会基于你提交的陈述进行。)` : ''}`;
|
|
369
|
+
explicitDiagnostics.push(diagnostic(`owner_statement_received:${receipt.painId}:${receipt.taskId}${receipt.promotionDegraded !== undefined ? `:promotion_degraded:${receipt.promotionDegraded}` : ''}${receipt.ingressWarning !== undefined ? `:ingress_degraded:${receipt.ingressWarning}` : ''}`, 'Durable enqueue complete; the workspace worker continues diagnosis in the background.'));
|
|
367
370
|
}
|
|
368
371
|
else {
|
|
369
372
|
explicitDiagnostics.push(diagnostic(`owner_statement_${receipt.reason}`, receipt.nextAction));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type InternalizationConsumerCycleOutcome, type ConsumerCycleLogger, type ReconcileGovernanceContinuationResult } from '@principles/host-runtime';
|
|
1
|
+
import { type InternalizationConsumerCycleOutcome, type ConsumerCycleLogger, type ReconcileGovernanceContinuationResult, type SignalLlmClassifier, type Stage2CorrectionCycleReport } from '@principles/host-runtime';
|
|
2
2
|
import { type CodexCatchUpResult } from '../ingestion/catch-up.js';
|
|
3
3
|
import type { TranscriptPort } from '../ingestion/transcript-decoder.js';
|
|
4
4
|
export type CodexWorkerMode = 'ready' | 'manual_action_required' | 'paused' | 'degraded';
|
|
@@ -34,6 +34,11 @@ export interface CodexWorkerCycleStepReport {
|
|
|
34
34
|
readonly reuseReviewRequiredCandidateIds?: readonly string[];
|
|
35
35
|
} | null;
|
|
36
36
|
readonly downstream: InternalizationConsumerCycleOutcome | null;
|
|
37
|
+
/**
|
|
38
|
+
* Phase B Stage-2 review step. Always the LAST step so it can never starve
|
|
39
|
+
* the Diagnostician (bounded to ≤2 candidates / ≤60s aggregate).
|
|
40
|
+
*/
|
|
41
|
+
readonly stage2?: Stage2CorrectionCycleReport;
|
|
37
42
|
}
|
|
38
43
|
export interface CodexWorkerCycleResult {
|
|
39
44
|
readonly workspaceDir: string;
|
|
@@ -53,6 +58,13 @@ export interface CodexWorkerCycleOptions {
|
|
|
53
58
|
readonly port?: TranscriptPort;
|
|
54
59
|
/** Cap on diagnostician candidates inspected per cycle (default 5). */
|
|
55
60
|
readonly diagnosticianCandidateLimit?: number;
|
|
61
|
+
/**
|
|
62
|
+
* Stage-2 classifier injection (tests). `undefined` = resolve the
|
|
63
|
+
* signalCollector profile from the workspace config; `null` = explicitly
|
|
64
|
+
* unavailable (candidates with a durable review result still settle).
|
|
65
|
+
*/
|
|
66
|
+
readonly stage2Classifier?: SignalLlmClassifier | null;
|
|
67
|
+
readonly stage2AggregateBudgetMs?: number;
|
|
56
68
|
}
|
|
57
69
|
export declare function readCodexWorkerLiveness(workspaceDir: string): Record<string, unknown> | null;
|
|
58
70
|
/**
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
import fs from 'node:fs';
|
|
29
29
|
import path from 'node:path';
|
|
30
30
|
import { computeFeatureFlagsFromConfig, createRuntimeStateHandle, createPainSignalBridge, isRetryWaitBackoffElapsed, PrincipleTreeLedgerAdapter, resolveDiagnosticianCapability, } from '@principles/core/runtime-v2';
|
|
31
|
-
import { createWorkspaceBehaviorEvidencePort, createCodexArtificerTrustedFactsPort, loadPdConfigForPlugin, loadFeatureFlagFromConfig, reconcileGovernanceContinuation, runInternalizationConsumerCycle, saveHostToolDeclaration, } from '@principles/host-runtime';
|
|
31
|
+
import { createWorkspaceBehaviorEvidencePort, createCodexArtificerTrustedFactsPort, loadPdConfigForPlugin, loadFeatureFlagFromConfig, reconcileGovernanceContinuation, runInternalizationConsumerCycle, runStage2CorrectionCycle, createSignalClassifierForWorkspace, saveHostToolDeclaration, } from '@principles/host-runtime';
|
|
32
32
|
import { catchUpCodexIngestion } from '../ingestion/catch-up.js';
|
|
33
33
|
import { CODEX_TOOL_SEMANTIC_MAPPINGS, CODEX_TOOL_SEMANTICS } from '../tool-semantics.js';
|
|
34
34
|
const WORKER_OWNER = 'companion-worker';
|
|
@@ -419,6 +419,28 @@ export async function runCodexWorkspaceWorkerCycle(options) {
|
|
|
419
419
|
// No hostToolCatalog: PD has not declared a Codex tool catalog; a wrong
|
|
420
420
|
// (OpenClaw) catalog would be worse than none (PRI-630 follow-up).
|
|
421
421
|
});
|
|
422
|
+
// Step 8 (SPEC v0.3.1 §2) — bounded Stage-2 correction review, ALWAYS last:
|
|
423
|
+
// the Diagnostician has already run, so this step cannot starve it. ≤2
|
|
424
|
+
// candidates, ≤60s aggregate, single 60s classification timeout.
|
|
425
|
+
const stage2Resolution = options.stage2Classifier === undefined
|
|
426
|
+
? createSignalClassifierForWorkspace(workspaceDir)
|
|
427
|
+
: null;
|
|
428
|
+
const stage2Classifier = options.stage2Classifier !== undefined
|
|
429
|
+
? options.stage2Classifier
|
|
430
|
+
: (stage2Resolution?.ok === true ? stage2Resolution.classifier : null);
|
|
431
|
+
const stage2ClassifierState = options.stage2Classifier === null
|
|
432
|
+
? 'disabled'
|
|
433
|
+
: stage2Classifier === null
|
|
434
|
+
? (stage2Resolution !== null && !stage2Resolution.ok && stage2Resolution.readiness === 'disabled' ? 'disabled' : 'unavailable')
|
|
435
|
+
: 'used';
|
|
436
|
+
const stage2 = await runStage2CorrectionCycle({
|
|
437
|
+
workspaceDir,
|
|
438
|
+
classifier: stage2Classifier,
|
|
439
|
+
classifierState: stage2ClassifierState,
|
|
440
|
+
...(options.stage2AggregateBudgetMs !== undefined ? { aggregateBudgetMs: options.stage2AggregateBudgetMs } : {}),
|
|
441
|
+
...(options.now !== undefined ? { now: options.now } : {}),
|
|
442
|
+
onEvent: emitEvent,
|
|
443
|
+
});
|
|
422
444
|
// Aggregated mode: degraded overrides all (review P1). Any component
|
|
423
445
|
// degrading — catch-up, reconciliation, diagnostician or downstream —
|
|
424
446
|
// surfaces the workspace as degraded, with the highest-priority reason.
|
|
@@ -429,7 +451,8 @@ export async function runCodexWorkspaceWorkerCycle(options) {
|
|
|
429
451
|
: diagnostician?.status === 'failed' && diagnostician.errorCategory !== 'lease_conflict' ? `diagnostician_failed:${diagnostician.message ?? 'max_attempts_exceeded'}`
|
|
430
452
|
: diagnostician?.status === 'degraded' ? `diagnostician_degraded:${diagnostician.message ?? 'unknown'}`
|
|
431
453
|
: downstream.skipReason === 'runtime_config_error' || downstream.skipReason === 'cycle_error' || downstream.skipReason === 'config_malformed' ? `downstream:${downstream.skipReason}`
|
|
432
|
-
:
|
|
454
|
+
: !stage2.ok ? `stage2:${stage2.reason ?? 'failed'}`
|
|
455
|
+
: undefined;
|
|
433
456
|
writeLiveness(workspaceDir, {
|
|
434
457
|
runningStage: null,
|
|
435
458
|
stageDeadline: null,
|
|
@@ -442,7 +465,7 @@ export async function runCodexWorkspaceWorkerCycle(options) {
|
|
|
442
465
|
...base,
|
|
443
466
|
mode: degradedReason !== undefined ? 'degraded' : 'ready',
|
|
444
467
|
...(degradedReason !== undefined ? { reason: degradedReason, nextAction: 'Inspect the Workspace .pd/config.yaml runtime profile and the per-step report above; the worker retries automatically on the next cycle.' } : {}),
|
|
445
|
-
report: { catchUp, reconcile, postProcessing, diagnostician, downstream },
|
|
468
|
+
report: { catchUp, reconcile, postProcessing, diagnostician, downstream, stage2 },
|
|
446
469
|
};
|
|
447
470
|
}
|
|
448
471
|
// Slice D (PRI-625): the mode authority moved to host-runtime so the CLI and
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principles/codex-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Codex CLI host adapter for Principles Disciple — implements HostAdapter interface for OpenAI Codex CLI's stdin/stdout JSON hook model (ADR-0020).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { ReceiptEvidenceCoverage } from '@principles/core/runtime-v2';
|
|
2
|
+
import type { PromptInjectionRouteSummaryWire } from '../../shared/prompt-injection-contract.js';
|
|
3
|
+
import type { PrincipleReceipts } from './ReceiptsConsoleModel.js';
|
|
4
|
+
export interface ExaminerEvidenceDeliverySample {
|
|
5
|
+
evidenceId: string;
|
|
6
|
+
recordedAt: string;
|
|
7
|
+
sourceKind: string;
|
|
8
|
+
confirmation: string;
|
|
9
|
+
outcome: string;
|
|
10
|
+
targetKind: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ExaminerEvidenceApplicationSample {
|
|
13
|
+
evidenceId: string;
|
|
14
|
+
recordedAt: string;
|
|
15
|
+
sourceKind: string;
|
|
16
|
+
proofMethod: string;
|
|
17
|
+
action: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ExaminerEvidenceRecordSample {
|
|
20
|
+
evidenceId: string;
|
|
21
|
+
recordedAt: string;
|
|
22
|
+
sourceKind: string;
|
|
23
|
+
summary: string;
|
|
24
|
+
}
|
|
25
|
+
/** P1-3: per-kind pagination truth, passed through verbatim from the audit. */
|
|
26
|
+
export interface ExaminerEvidenceKindPage {
|
|
27
|
+
hasMore: boolean;
|
|
28
|
+
nextCursor: {
|
|
29
|
+
recordedAt: string;
|
|
30
|
+
evidenceId: string;
|
|
31
|
+
} | null;
|
|
32
|
+
}
|
|
33
|
+
export interface ExaminerEvidenceAuditProjection {
|
|
34
|
+
status: 'ok' | 'degraded';
|
|
35
|
+
reason?: string;
|
|
36
|
+
nextAction?: string;
|
|
37
|
+
/** P1-3: LOADED counts (the audit page window) — never presented as totals. */
|
|
38
|
+
counts: {
|
|
39
|
+
deliveries: number;
|
|
40
|
+
applications: number;
|
|
41
|
+
episodes: number;
|
|
42
|
+
effects: number;
|
|
43
|
+
outcomes: number;
|
|
44
|
+
};
|
|
45
|
+
/** P1-3: pagination truth per kind — hasMore=true means MORE records exist. */
|
|
46
|
+
pages: Record<'delivery' | 'application' | 'behavior_episode' | 'effect' | 'outcome', ExaminerEvidenceKindPage>;
|
|
47
|
+
/** True when ANY kind reports hasMore — the UI shows a "more exist" band. */
|
|
48
|
+
anyHasMore: boolean;
|
|
49
|
+
/** Delivery confirmations observed in the audit window (never promoted). */
|
|
50
|
+
deliveryConfirmations: {
|
|
51
|
+
prepared: number;
|
|
52
|
+
submitted: number;
|
|
53
|
+
hostAccepted: number;
|
|
54
|
+
contextPresent: number;
|
|
55
|
+
runtimeLoaded: number;
|
|
56
|
+
};
|
|
57
|
+
/** Application proof split — agent claims are NOT runtime verification. */
|
|
58
|
+
applicationProof: {
|
|
59
|
+
agentClaimed: number;
|
|
60
|
+
runtimeVerified: number;
|
|
61
|
+
};
|
|
62
|
+
unresolvedReferences: number;
|
|
63
|
+
capabilityDeclarations: {
|
|
64
|
+
hostKind: string;
|
|
65
|
+
capability: string;
|
|
66
|
+
status: string;
|
|
67
|
+
maxConfirmation?: string;
|
|
68
|
+
note?: string;
|
|
69
|
+
}[];
|
|
70
|
+
samples: {
|
|
71
|
+
deliveries: ExaminerEvidenceDeliverySample[];
|
|
72
|
+
applications: ExaminerEvidenceApplicationSample[];
|
|
73
|
+
episodes: ExaminerEvidenceRecordSample[];
|
|
74
|
+
effects: ExaminerEvidenceRecordSample[];
|
|
75
|
+
outcomes: ExaminerEvidenceRecordSample[];
|
|
76
|
+
};
|
|
77
|
+
asOf: string | null;
|
|
78
|
+
coverage: ReceiptEvidenceCoverage | null;
|
|
79
|
+
}
|
|
80
|
+
export interface ExaminerPromptActivationRow {
|
|
81
|
+
activationId: string;
|
|
82
|
+
action: string;
|
|
83
|
+
status: string;
|
|
84
|
+
activatedAt: string | null;
|
|
85
|
+
deactivatedAt: string | null;
|
|
86
|
+
enforcement?: string;
|
|
87
|
+
warning?: string;
|
|
88
|
+
}
|
|
89
|
+
export interface ExaminerRuleCodeRow {
|
|
90
|
+
activationId: string;
|
|
91
|
+
action: string;
|
|
92
|
+
mode?: string;
|
|
93
|
+
status: string;
|
|
94
|
+
enforcement?: string;
|
|
95
|
+
targetRef: string;
|
|
96
|
+
warning?: string;
|
|
97
|
+
}
|
|
98
|
+
export interface ExaminerActivationProjection {
|
|
99
|
+
status: 'ok' | 'degraded';
|
|
100
|
+
reason?: string;
|
|
101
|
+
nextAction?: string;
|
|
102
|
+
prompt: ExaminerPromptActivationRow[];
|
|
103
|
+
ruleCode: ExaminerRuleCodeRow[];
|
|
104
|
+
promptInjectionRoute?: PromptInjectionRouteSummaryWire;
|
|
105
|
+
}
|
|
106
|
+
export interface ExaminerInjectionProjection {
|
|
107
|
+
status: 'available' | 'degraded';
|
|
108
|
+
/** Present when no status could be computed at all (fail-loud, rc-9). */
|
|
109
|
+
globalReason?: string;
|
|
110
|
+
statuses: {
|
|
111
|
+
activationId: string;
|
|
112
|
+
status: string;
|
|
113
|
+
inCurrentWindow?: boolean;
|
|
114
|
+
costChars?: number;
|
|
115
|
+
budget?: number;
|
|
116
|
+
route?: string;
|
|
117
|
+
reason?: string;
|
|
118
|
+
}[];
|
|
119
|
+
budgetProvenance?: {
|
|
120
|
+
capacitySource?: string;
|
|
121
|
+
capacityStatus?: string;
|
|
122
|
+
contextWindowTokens?: number;
|
|
123
|
+
policyVersion?: string;
|
|
124
|
+
fallbackReason?: string;
|
|
125
|
+
};
|
|
126
|
+
warnings: string[];
|
|
127
|
+
}
|
|
128
|
+
export interface ExaminerPrincipleEvidence {
|
|
129
|
+
asOf: string;
|
|
130
|
+
principleId: string;
|
|
131
|
+
principle: {
|
|
132
|
+
id: string;
|
|
133
|
+
text: string;
|
|
134
|
+
status: string;
|
|
135
|
+
ruleCount: number;
|
|
136
|
+
derivedFromPainIds: string[];
|
|
137
|
+
evaluability: string;
|
|
138
|
+
painPreventedCount: number;
|
|
139
|
+
adherenceRate: number;
|
|
140
|
+
valueScore: number;
|
|
141
|
+
runtimeDeactivated?: boolean;
|
|
142
|
+
/** Asset status ≠ runtime activation — pinned for the UI (SPEC §4.1). */
|
|
143
|
+
ledgerStatusNote: string;
|
|
144
|
+
};
|
|
145
|
+
activation: ExaminerActivationProjection;
|
|
146
|
+
injection: ExaminerInjectionProjection;
|
|
147
|
+
receipts: PrincipleReceipts;
|
|
148
|
+
evidenceAudit: ExaminerEvidenceAuditProjection;
|
|
149
|
+
}
|
|
150
|
+
export declare class ExaminerEvidenceModel {
|
|
151
|
+
private readonly workspaceDir;
|
|
152
|
+
constructor(workspaceDir: string);
|
|
153
|
+
/**
|
|
154
|
+
* One principle's full production-evidence aggregate. The caller has
|
|
155
|
+
* already validated the principle id charset; a principle that is not in
|
|
156
|
+
* the ledger returns null (route answers 404 — never a fabricated entry).
|
|
157
|
+
*/
|
|
158
|
+
getPrincipleEvidence(principleId: string): Promise<ExaminerPrincipleEvidence | null>;
|
|
159
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPEC-PD-EXAMINER-001 v0.10.1 §4/§5 — Examiner production-evidence read model.
|
|
3
|
+
*
|
|
4
|
+
* A pure DERIVED projection: composes the existing console models and the
|
|
5
|
+
* host-runtime production projection into ONE per-principle evidence
|
|
6
|
+
* aggregate for the Examiner Principle Evaluation page. It owns no storage,
|
|
7
|
+
* writes nothing, and creates no second evidence truth (P4) — every fact is
|
|
8
|
+
* read through the authority that already owns it:
|
|
9
|
+
*
|
|
10
|
+
* ledger PrinciplesConsoleModel (principle_training_state.json)
|
|
11
|
+
* activations ActivationsConsoleModel (state.db activations + artifacts)
|
|
12
|
+
* prompt inject. readPromptActivationInjectionStatuses (host-runtime,
|
|
13
|
+
* built on the SAME production projection the runtime uses)
|
|
14
|
+
* receipts ReceiptsConsoleModel (principle_applications + coverage)
|
|
15
|
+
* evidence audit ReceiptsConsoleModel.getEvidenceAudit
|
|
16
|
+
* (SqliteInterventionEvidenceStore.readAuditRelations)
|
|
17
|
+
*
|
|
18
|
+
* Honesty contract (SPEC §4.3/§5.5):
|
|
19
|
+
* - Projection / Submitted / Presence / Context Present / Runtime Loaded
|
|
20
|
+
* stay DISTINCT — nothing is promoted.
|
|
21
|
+
* - `agent_claimed` never becomes `runtime_verified`.
|
|
22
|
+
* - Unavailable evidence is reported as DEGRADED/UNAVAILABLE with reason
|
|
23
|
+
* and nextAction — empty data is never presented as "zero events".
|
|
24
|
+
* - RuleHost evidence is surfaced as its own lane; it is never counted as
|
|
25
|
+
* prompt-consumption evidence.
|
|
26
|
+
*/
|
|
27
|
+
import { readPromptActivationInjectionStatuses } from '@principles/host-runtime';
|
|
28
|
+
import { ReceiptsConsoleModel } from './ReceiptsConsoleModel.js';
|
|
29
|
+
import { PrinciplesConsoleModel, CorePrincipleNotGovernedError } from './PrinciplesConsoleModel.js';
|
|
30
|
+
import { ActivationsConsoleModel } from './ActivationsConsoleModel.js';
|
|
31
|
+
/** Bounded per-kind sample shown on the evaluation page (audit stays paged). */
|
|
32
|
+
const AUDIT_SAMPLE_LIMIT = 5;
|
|
33
|
+
function isRecord(value) {
|
|
34
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
35
|
+
}
|
|
36
|
+
function stringOrUndefined(value) {
|
|
37
|
+
return typeof value === 'string' ? value : undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Bounded episode/effect/outcome sample summary from the payload (untrusted).
|
|
41
|
+
* Shortened by CODE POINTS — a UTF-16 slice can split a surrogate pair and
|
|
42
|
+
* render U+FFFD in the Owner-facing panel (EP-11).
|
|
43
|
+
*/
|
|
44
|
+
function recordSummary(payload) {
|
|
45
|
+
if (!isRecord(payload))
|
|
46
|
+
return '';
|
|
47
|
+
const candidate = payload.observationSummary ?? payload.actionSummary ?? payload.feedbackText ?? payload.resultSummary;
|
|
48
|
+
const text = typeof candidate === 'string' ? candidate : '';
|
|
49
|
+
const codePoints = Array.from(text);
|
|
50
|
+
return codePoints.length > 160 ? `${codePoints.slice(0, 157).join('')}…` : text;
|
|
51
|
+
}
|
|
52
|
+
export class ExaminerEvidenceModel {
|
|
53
|
+
workspaceDir;
|
|
54
|
+
constructor(workspaceDir) {
|
|
55
|
+
this.workspaceDir = workspaceDir;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* One principle's full production-evidence aggregate. The caller has
|
|
59
|
+
* already validated the principle id charset; a principle that is not in
|
|
60
|
+
* the ledger returns null (route answers 404 — never a fabricated entry).
|
|
61
|
+
*/
|
|
62
|
+
async getPrincipleEvidence(principleId) {
|
|
63
|
+
const asOf = new Date().toISOString();
|
|
64
|
+
// 1. Ledger facts (the authoritative principle asset source).
|
|
65
|
+
const principlesModel = new PrinciplesConsoleModel(this.workspaceDir);
|
|
66
|
+
let detailOutput;
|
|
67
|
+
try {
|
|
68
|
+
detailOutput = await principlesModel.getPrincipleDetail(principleId);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
// Core principles are not workspace-governed objects: they have no
|
|
72
|
+
// production-evidence aggregate here, so the route answers the same
|
|
73
|
+
// 404 as any other principle absent from the workspace ledger.
|
|
74
|
+
if (error instanceof CorePrincipleNotGovernedError)
|
|
75
|
+
return null;
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
if (!detailOutput)
|
|
79
|
+
return null;
|
|
80
|
+
const detail = detailOutput.principle;
|
|
81
|
+
// 2. Activation lifecycle rows (prompt + RuleCode lanes) — full reuse of
|
|
82
|
+
// the activations read model, filtered to this principle. A read error
|
|
83
|
+
// degrades the ACTIVATION lane only: the ledger facts below still
|
|
84
|
+
// answer, and the lane carries reason + nextAction (never "zero").
|
|
85
|
+
const activationsModel = new ActivationsConsoleModel(this.workspaceDir);
|
|
86
|
+
let activations;
|
|
87
|
+
try {
|
|
88
|
+
activations = await activationsModel.getActivations();
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
activations = {
|
|
92
|
+
activations: [],
|
|
93
|
+
status: 'degraded',
|
|
94
|
+
reason: `activation read failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
95
|
+
nextAction: 'check workspace .pd/state.db readability and the activation schema',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const rows = activations.activations.filter((row) => row.principleId === principleId);
|
|
99
|
+
const activation = {
|
|
100
|
+
status: activations.status,
|
|
101
|
+
reason: activations.reason,
|
|
102
|
+
nextAction: activations.nextAction,
|
|
103
|
+
promptInjectionRoute: activations.promptInjectionRoute,
|
|
104
|
+
prompt: rows
|
|
105
|
+
.filter((row) => row.channel === 'prompt')
|
|
106
|
+
.map((row) => ({
|
|
107
|
+
activationId: row.activationId,
|
|
108
|
+
action: row.action,
|
|
109
|
+
status: row.status,
|
|
110
|
+
activatedAt: row.activatedAt,
|
|
111
|
+
deactivatedAt: row.deactivatedAt,
|
|
112
|
+
enforcement: row.enforcement,
|
|
113
|
+
warning: row.warning,
|
|
114
|
+
})),
|
|
115
|
+
ruleCode: rows
|
|
116
|
+
.filter((row) => row.channel === 'code_tool_hook')
|
|
117
|
+
.map((row) => ({
|
|
118
|
+
activationId: row.activationId,
|
|
119
|
+
action: row.action,
|
|
120
|
+
mode: row.mode,
|
|
121
|
+
status: row.status,
|
|
122
|
+
enforcement: row.enforcement,
|
|
123
|
+
targetRef: row.targetRef,
|
|
124
|
+
warning: row.warning,
|
|
125
|
+
})),
|
|
126
|
+
};
|
|
127
|
+
// 3. Injection statuses from the SAME production projection the runtime
|
|
128
|
+
// uses (route + budget provenance + per-activation single-item facts).
|
|
129
|
+
const injectionReport = await readPromptActivationInjectionStatuses({ workspaceDir: this.workspaceDir });
|
|
130
|
+
const targetStatuses = injectionReport.statuses.filter((row) => row.principleId === principleId);
|
|
131
|
+
const injection = {
|
|
132
|
+
status: injectionReport.globalReason ? 'degraded' : 'available',
|
|
133
|
+
globalReason: injectionReport.globalReason,
|
|
134
|
+
statuses: targetStatuses.map((row) => ({
|
|
135
|
+
activationId: row.activationId,
|
|
136
|
+
status: row.status,
|
|
137
|
+
inCurrentWindow: row.inCurrentWindow,
|
|
138
|
+
costChars: row.costChars,
|
|
139
|
+
budget: row.budget,
|
|
140
|
+
route: row.route,
|
|
141
|
+
reason: row.reason,
|
|
142
|
+
})),
|
|
143
|
+
budgetProvenance: injectionReport.budgetProvenance
|
|
144
|
+
? {
|
|
145
|
+
capacitySource: injectionReport.budgetProvenance.capacitySource,
|
|
146
|
+
capacityStatus: injectionReport.budgetProvenance.capacityStatus,
|
|
147
|
+
contextWindowTokens: injectionReport.budgetProvenance.contextWindowTokens,
|
|
148
|
+
policyVersion: injectionReport.budgetProvenance.policyVersion,
|
|
149
|
+
fallbackReason: injectionReport.budgetProvenance.fallbackReason,
|
|
150
|
+
}
|
|
151
|
+
: undefined,
|
|
152
|
+
warnings: injectionReport.warnings.slice(0, 20),
|
|
153
|
+
};
|
|
154
|
+
// 4. Receipts + 5. evidence audit through the existing receipts model.
|
|
155
|
+
const receiptsModel = new ReceiptsConsoleModel(this.workspaceDir);
|
|
156
|
+
const receipts = await receiptsModel.getPrincipleReceipts(principleId);
|
|
157
|
+
const audit = await receiptsModel.getEvidenceAudit({ type: 'principle', principleId });
|
|
158
|
+
const deliveryConfirmations = { prepared: 0, submitted: 0, hostAccepted: 0, contextPresent: 0, runtimeLoaded: 0 };
|
|
159
|
+
for (const record of audit.deliveries) {
|
|
160
|
+
const confirmation = stringOrUndefined(isRecord(record.payload) ? record.payload.confirmation : undefined);
|
|
161
|
+
if (confirmation === 'prepared')
|
|
162
|
+
deliveryConfirmations.prepared += 1;
|
|
163
|
+
else if (confirmation === 'submitted')
|
|
164
|
+
deliveryConfirmations.submitted += 1;
|
|
165
|
+
else if (confirmation === 'host_accepted')
|
|
166
|
+
deliveryConfirmations.hostAccepted += 1;
|
|
167
|
+
else if (confirmation === 'context_present')
|
|
168
|
+
deliveryConfirmations.contextPresent += 1;
|
|
169
|
+
else if (confirmation === 'runtime_loaded')
|
|
170
|
+
deliveryConfirmations.runtimeLoaded += 1;
|
|
171
|
+
}
|
|
172
|
+
const applicationProof = { agentClaimed: 0, runtimeVerified: 0 };
|
|
173
|
+
for (const record of audit.applications) {
|
|
174
|
+
const proof = stringOrUndefined(isRecord(record.payload) ? record.payload.proofMethod : undefined);
|
|
175
|
+
if (proof === 'runtime_verified')
|
|
176
|
+
applicationProof.runtimeVerified += 1;
|
|
177
|
+
else if (proof === 'agent_claimed')
|
|
178
|
+
applicationProof.agentClaimed += 1;
|
|
179
|
+
}
|
|
180
|
+
// P1-3: pagination truth rides through verbatim — the counts above are
|
|
181
|
+
// LOADED-window sizes, and hasMore per kind tells the Owner more records
|
|
182
|
+
// exist beyond this read.
|
|
183
|
+
const pages = {
|
|
184
|
+
delivery: { hasMore: audit.pages.delivery.hasMore, nextCursor: audit.pages.delivery.nextCursor },
|
|
185
|
+
application: { hasMore: audit.pages.application.hasMore, nextCursor: audit.pages.application.nextCursor },
|
|
186
|
+
behavior_episode: { hasMore: audit.pages.behavior_episode.hasMore, nextCursor: audit.pages.behavior_episode.nextCursor },
|
|
187
|
+
effect: { hasMore: audit.pages.effect.hasMore, nextCursor: audit.pages.effect.nextCursor },
|
|
188
|
+
outcome: { hasMore: audit.pages.outcome.hasMore, nextCursor: audit.pages.outcome.nextCursor },
|
|
189
|
+
};
|
|
190
|
+
const anyHasMore = Object.values(pages).some((page) => page.hasMore);
|
|
191
|
+
const evidenceAudit = {
|
|
192
|
+
status: audit.status,
|
|
193
|
+
reason: audit.reason,
|
|
194
|
+
nextAction: audit.nextAction,
|
|
195
|
+
counts: {
|
|
196
|
+
deliveries: audit.deliveries.length,
|
|
197
|
+
applications: audit.applications.length,
|
|
198
|
+
episodes: audit.episodes.length,
|
|
199
|
+
effects: audit.effects.length,
|
|
200
|
+
outcomes: audit.outcomes.length,
|
|
201
|
+
},
|
|
202
|
+
pages,
|
|
203
|
+
anyHasMore,
|
|
204
|
+
deliveryConfirmations,
|
|
205
|
+
applicationProof,
|
|
206
|
+
unresolvedReferences: audit.unresolvedReferences.length,
|
|
207
|
+
capabilityDeclarations: audit.capabilityDeclarations.slice(0, 12).map((declaration) => ({
|
|
208
|
+
hostKind: String(declaration.hostKind),
|
|
209
|
+
capability: String(declaration.capability),
|
|
210
|
+
status: String(declaration.status),
|
|
211
|
+
maxConfirmation: stringOrUndefined(declaration.maxConfirmation),
|
|
212
|
+
note: stringOrUndefined(declaration.note),
|
|
213
|
+
})),
|
|
214
|
+
samples: {
|
|
215
|
+
deliveries: audit.deliveries.slice(0, AUDIT_SAMPLE_LIMIT).map((record) => ({
|
|
216
|
+
evidenceId: record.evidenceId,
|
|
217
|
+
recordedAt: record.recordedAt,
|
|
218
|
+
sourceKind: String(record.sourceKind),
|
|
219
|
+
confirmation: stringOrUndefined(isRecord(record.payload) ? record.payload.confirmation : undefined) ?? 'unknown',
|
|
220
|
+
outcome: stringOrUndefined(isRecord(record.payload) ? record.payload.outcome : undefined) ?? 'unknown',
|
|
221
|
+
targetKind: stringOrUndefined(isRecord(record.payload) ? record.payload.targetKind : undefined) ?? 'unknown',
|
|
222
|
+
})),
|
|
223
|
+
applications: audit.applications.slice(0, AUDIT_SAMPLE_LIMIT).map((record) => ({
|
|
224
|
+
evidenceId: record.evidenceId,
|
|
225
|
+
recordedAt: record.recordedAt,
|
|
226
|
+
sourceKind: String(record.sourceKind),
|
|
227
|
+
proofMethod: stringOrUndefined(isRecord(record.payload) ? record.payload.proofMethod : undefined) ?? 'unknown',
|
|
228
|
+
action: stringOrUndefined(isRecord(record.payload) ? record.payload.action : undefined) ?? 'unknown',
|
|
229
|
+
})),
|
|
230
|
+
episodes: audit.episodes.slice(0, AUDIT_SAMPLE_LIMIT).map((record) => ({
|
|
231
|
+
evidenceId: record.evidenceId,
|
|
232
|
+
recordedAt: record.recordedAt,
|
|
233
|
+
sourceKind: String(record.sourceKind),
|
|
234
|
+
summary: recordSummary(record.payload),
|
|
235
|
+
})),
|
|
236
|
+
effects: audit.effects.slice(0, AUDIT_SAMPLE_LIMIT).map((record) => ({
|
|
237
|
+
evidenceId: record.evidenceId,
|
|
238
|
+
recordedAt: record.recordedAt,
|
|
239
|
+
sourceKind: String(record.sourceKind),
|
|
240
|
+
summary: recordSummary(record.payload),
|
|
241
|
+
})),
|
|
242
|
+
outcomes: audit.outcomes.slice(0, AUDIT_SAMPLE_LIMIT).map((record) => ({
|
|
243
|
+
evidenceId: record.evidenceId,
|
|
244
|
+
recordedAt: record.recordedAt,
|
|
245
|
+
sourceKind: String(record.sourceKind),
|
|
246
|
+
summary: recordSummary(record.payload),
|
|
247
|
+
})),
|
|
248
|
+
},
|
|
249
|
+
asOf: audit.asOf,
|
|
250
|
+
coverage: audit.coverage ?? null,
|
|
251
|
+
};
|
|
252
|
+
return {
|
|
253
|
+
asOf,
|
|
254
|
+
principleId,
|
|
255
|
+
principle: {
|
|
256
|
+
id: detail.id,
|
|
257
|
+
text: detail.text,
|
|
258
|
+
status: detail.status,
|
|
259
|
+
ruleCount: detail.ruleCount,
|
|
260
|
+
derivedFromPainIds: detail.derivedFromPainIds,
|
|
261
|
+
evaluability: detail.evaluability,
|
|
262
|
+
painPreventedCount: detail.painPreventedCount,
|
|
263
|
+
adherenceRate: detail.adherenceRate,
|
|
264
|
+
valueScore: detail.valueScore,
|
|
265
|
+
runtimeDeactivated: detail.runtimeDeactivated,
|
|
266
|
+
ledgerStatusNote: 'ledger status is the ASSET lifecycle — it is not runtime activation; the runtime rows below are the activation truth',
|
|
267
|
+
},
|
|
268
|
+
activation,
|
|
269
|
+
injection,
|
|
270
|
+
receipts,
|
|
271
|
+
evidenceAudit,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
}
|
|
@@ -171,7 +171,21 @@ export class ReceiptsConsoleModel {
|
|
|
171
171
|
coverage: unreadCoverage('unavailable', 'ledger_table_missing', 'update_plugin'),
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
|
-
|
|
174
|
+
// An unreadable state.db (permissions, corruption) must degrade, not
|
|
175
|
+
// throw: the documented read-path contract is degraded-over-500, so the
|
|
176
|
+
// evidence route keeps its ledger facts and shows WHY receipts are absent.
|
|
177
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
178
|
+
return {
|
|
179
|
+
status: 'degraded',
|
|
180
|
+
reason: `receipt ledger read failed: ${message}`,
|
|
181
|
+
nextAction: 'Run pd runtime diagnostics to check workspace .pd/state.db readability',
|
|
182
|
+
principleId,
|
|
183
|
+
effectCount: 0,
|
|
184
|
+
presenceCount: 0,
|
|
185
|
+
lastEffectAt: null,
|
|
186
|
+
events: [],
|
|
187
|
+
coverage: unreadCoverage('unavailable', 'receipt_read_failed', 'run_runtime_diagnostics'),
|
|
188
|
+
};
|
|
175
189
|
}
|
|
176
190
|
finally {
|
|
177
191
|
conn.close();
|