create-principles-disciple 1.146.7 → 1.146.9
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/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/host-runtime/dist/internalization-consumer-cycle.js +13 -1
- package/host-runtime/package.json +1 -1
- package/package.json +3 -2
- package/pd-cli/package.json +1 -1
- package/plugin/dist/bundle.js +282 -282
- package/release-manager/package.json +2 -2
|
@@ -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();
|
|
@@ -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
|
}>;
|