@principles/pd-cli 1.152.7 → 1.152.8
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/dist/commands/__tests__/runtime-probe-config.test.js +2 -1
- package/dist/commands/__tests__/runtime-probe-config.test.js.map +1 -1
- package/dist/commands/__tests__/version.test.js +104 -0
- package/dist/commands/__tests__/version.test.js.map +1 -1
- package/dist/commands/diagnose.d.ts.map +1 -1
- package/dist/commands/diagnose.js +3 -6
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/pain-retry.d.ts.map +1 -1
- package/dist/commands/pain-retry.js +3 -6
- package/dist/commands/pain-retry.js.map +1 -1
- package/dist/commands/runtime-activation.d.ts.map +1 -1
- package/dist/commands/runtime-activation.js +9 -3
- package/dist/commands/runtime-activation.js.map +1 -1
- package/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-once.js +5 -11
- package/dist/commands/runtime-internalization-run-once.js.map +1 -1
- package/dist/commands/runtime-internalization-run-rulehost.d.ts +14 -0
- package/dist/commands/runtime-internalization-run-rulehost.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-rulehost.js +67 -15
- package/dist/commands/runtime-internalization-run-rulehost.js.map +1 -1
- package/dist/index.js +0 -12
- package/dist/index.js.map +1 -1
- package/dist/services/__tests__/rulehost-readiness.test.js +0 -3
- package/dist/services/__tests__/rulehost-readiness.test.js.map +1 -1
- package/dist/services/pain-flood-simulation-runner.d.ts.map +1 -1
- package/dist/services/pain-flood-simulation-runner.js +0 -4
- package/dist/services/pain-flood-simulation-runner.js.map +1 -1
- package/dist/services/rulehost-pipeline-runner.d.ts +90 -21
- package/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
- package/dist/services/rulehost-pipeline-runner.js +328 -24
- package/dist/services/rulehost-pipeline-runner.js.map +1 -1
- package/dist/services/synthetic-baseline-runner.d.ts.map +1 -1
- package/dist/services/synthetic-baseline-runner.js +0 -4
- package/dist/services/synthetic-baseline-runner.js.map +1 -1
- package/dist/services/version-report.d.ts +16 -0
- package/dist/services/version-report.d.ts.map +1 -1
- package/dist/services/version-report.js +37 -2
- package/dist/services/version-report.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/__tests__/runtime-probe-config.test.ts +2 -1
- package/src/commands/__tests__/version.test.ts +110 -0
- package/src/commands/diagnose.ts +3 -6
- package/src/commands/pain-retry.ts +3 -6
- package/src/commands/runtime-activation.ts +9 -3
- package/src/commands/runtime-internalization-run-once.ts +5 -11
- package/src/commands/runtime-internalization-run-rulehost.ts +69 -15
- package/src/index.ts +0 -13
- package/src/services/__tests__/rulehost-readiness.test.ts +0 -3
- package/src/services/pain-flood-simulation-runner.ts +0 -4
- package/src/services/rulehost-pipeline-runner.ts +368 -35
- package/src/services/synthetic-baseline-runner.ts +0 -4
- package/src/services/version-report.ts +52 -2
- package/tests/commands/config-doctor.test.ts +0 -2
- package/tests/commands/pain-evidence.test.ts +1 -1
- package/tests/commands/run-rulehost-handler.test.ts +43 -0
- package/tests/commands/runtime-features.test.ts +0 -1
- package/tests/commands/runtime-internalization-run-once-language.test.ts +0 -1
- package/tests/e2e/cross-package-acceptance.test.ts +13 -3
- package/tests/services/pd-config-loader.test.ts +0 -1
- package/tests/services/rulehost-pipeline-runner.test.ts +378 -12
- package/dist/commands/runtime-internalization-context-trace.d.ts +0 -25
- package/dist/commands/runtime-internalization-context-trace.d.ts.map +0 -1
- package/dist/commands/runtime-internalization-context-trace.js +0 -374
- package/dist/commands/runtime-internalization-context-trace.js.map +0 -1
- package/src/commands/runtime-internalization-context-trace.ts +0 -445
- package/tests/bdd/context-trace.steps.ts +0 -229
- package/tests/commands/runtime-internalization-context-trace.test.ts +0 -110
|
@@ -26,16 +26,6 @@
|
|
|
26
26
|
*/
|
|
27
27
|
import { RuntimeStateManager, StoreEventEmitter } from '@principles/core/runtime-v2';
|
|
28
28
|
import type { AdversarialLoopResult, PDRuntimeAdapter, RefinerRuleHostGateDeps, PIArtifactStore, BehaviorExamplePack, EvaluatorRunnerDeps, EvaluatorValidator } from '@principles/core/runtime-v2';
|
|
29
|
-
/**
|
|
30
|
-
* Layer 0 content-hash function for the internalization progressive
|
|
31
|
-
* disclosure (design §6.1). Core never imports `node:crypto`, so the plugin/CLI
|
|
32
|
-
* layer injects the algorithm. Consumed only when the
|
|
33
|
-
* `artifact_summary_redundancy` flag is on; harmless otherwise.
|
|
34
|
-
*
|
|
35
|
-
* Exported so other CLI command modules (e.g. `runtime-internalization-run-once`)
|
|
36
|
-
* can reuse the same algorithm without each re-importing `node:crypto`.
|
|
37
|
-
*/
|
|
38
|
-
export declare const contentHashFn: (input: string) => string;
|
|
39
29
|
/**
|
|
40
30
|
* Code-rule capability (atomic: ArtificerL2 + Evaluator).
|
|
41
31
|
*
|
|
@@ -133,8 +123,8 @@ export interface RuleHostPipelineStage {
|
|
|
133
123
|
* rule artifact exists and is WAITING for owner review. This is NOT owner
|
|
134
124
|
* approval — it means the candidate is ready for the owner to review.
|
|
135
125
|
* - `text_principle_only`: code-rule capability OFF (artificer or evaluator
|
|
136
|
-
* disabled). No rule artifact; a text principle artifact is produced for
|
|
137
|
-
* prompt
|
|
126
|
+
* disabled). No rule artifact; a text principle artifact is produced for the
|
|
127
|
+
* prompt channel and enqueued into the existing approval queue (PRI-804).
|
|
138
128
|
* - `generation_rejected`: pipeline failed (no dreamer task, stage failure, or
|
|
139
129
|
* evaluator rejected the candidate). No rule artifact.
|
|
140
130
|
*/
|
|
@@ -152,10 +142,10 @@ export interface RuleHostPipelineResult {
|
|
|
152
142
|
readonly principleArtifactId: string | null;
|
|
153
143
|
/**
|
|
154
144
|
* Approval ID when the candidate was auto-enqueued into the ApprovalQueue.
|
|
155
|
-
* Present
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
145
|
+
* Present for decision='candidate_ready_for_owner_review' (rule artifact,
|
|
146
|
+
* code_tool_hook channel) AND for decision='text_principle_only' (principle
|
|
147
|
+
* artifact, prompt channel — PRI-804 wiring). Null when the enqueue itself
|
|
148
|
+
* failed — check degradationReason for the structured reason.
|
|
159
149
|
*/
|
|
160
150
|
readonly approvalId: string | null;
|
|
161
151
|
/** Structured reason when decision is not candidate_ready_for_owner_review. */
|
|
@@ -183,11 +173,6 @@ export interface CreateEvaluatorRunnerDepsInputs {
|
|
|
183
173
|
readonly artifactStore: PIArtifactStore;
|
|
184
174
|
/** Workspace directory containing `.pd/config.yaml` (flag source). */
|
|
185
175
|
readonly workspaceDir: string;
|
|
186
|
-
/**
|
|
187
|
-
* Layer 0 content-hash function (design §6.1). Optional — defaults to the
|
|
188
|
-
* module-level sha256-hex `contentHashFn`. Allow override for tests.
|
|
189
|
-
*/
|
|
190
|
-
readonly contentHashFn?: (input: string) => string;
|
|
191
176
|
}
|
|
192
177
|
/**
|
|
193
178
|
* Build the full `EvaluatorRunnerDeps` for CLI production paths, including the
|
|
@@ -204,4 +189,88 @@ export interface CreateEvaluatorRunnerDepsInputs {
|
|
|
204
189
|
* Returns the newly created repair task's ID (UUIDv4, rc-7: fresh per call).
|
|
205
190
|
*/
|
|
206
191
|
export declare function createEvaluatorRunnerDeps(inputs: CreateEvaluatorRunnerDepsInputs): EvaluatorRunnerDeps;
|
|
192
|
+
/**
|
|
193
|
+
* Owner Decision Experience v1 Phase A (SPEC §9.1) — structured ledger
|
|
194
|
+
* identity binding outcome for the internalization chain.
|
|
195
|
+
*
|
|
196
|
+
* `bound`/`already_bound` mean the scribe principle artifact carries a
|
|
197
|
+
* source_principle_id that was READ BACK from the PI store after the write
|
|
198
|
+
* and, when the chain candidate resolved, that it equals the unique existing
|
|
199
|
+
* ledger principle. Every other status is a publication blocker: the reason
|
|
200
|
+
* codes follow the SPEC's suggested vocabulary
|
|
201
|
+
* (candidate_unresolved / ledger_missing / ledger_ambiguous /
|
|
202
|
+
* identity_conflict / binding_write_failed).
|
|
203
|
+
*/
|
|
204
|
+
export type ScribeIdentityBinding = {
|
|
205
|
+
status: 'bound';
|
|
206
|
+
principleId: string;
|
|
207
|
+
} | {
|
|
208
|
+
status: 'already_bound';
|
|
209
|
+
principleId: string;
|
|
210
|
+
} | {
|
|
211
|
+
status: 'no_principle_artifact';
|
|
212
|
+
} | {
|
|
213
|
+
status: 'candidate_unresolved';
|
|
214
|
+
} | {
|
|
215
|
+
status: 'ledger_missing';
|
|
216
|
+
candidateId: string;
|
|
217
|
+
} | {
|
|
218
|
+
status: 'ledger_ambiguous';
|
|
219
|
+
candidateId: string;
|
|
220
|
+
principleIds: string[];
|
|
221
|
+
} | {
|
|
222
|
+
status: 'identity_conflict';
|
|
223
|
+
existingPrincipleId: string;
|
|
224
|
+
resolvedPrincipleId: string;
|
|
225
|
+
} | {
|
|
226
|
+
status: 'bound_target_missing';
|
|
227
|
+
principleId: string;
|
|
228
|
+
} | {
|
|
229
|
+
status: 'binding_write_failed';
|
|
230
|
+
errorMessage: string;
|
|
231
|
+
} | {
|
|
232
|
+
status: 'binding_readback_failed';
|
|
233
|
+
expectedPrincipleId: string;
|
|
234
|
+
errorMessage: string;
|
|
235
|
+
};
|
|
236
|
+
/** Only these two statuses authorize governance publication (approval enqueue). */
|
|
237
|
+
export declare function isIdentityBindingVerified(binding: ScribeIdentityBinding): binding is Extract<ScribeIdentityBinding, {
|
|
238
|
+
status: 'bound' | 'already_bound';
|
|
239
|
+
}>;
|
|
240
|
+
/** Stable machine reason code for observability/logs (SPEC §9.1 vocabulary). */
|
|
241
|
+
export declare function scribeIdentityReasonCode(binding: ScribeIdentityBinding): string;
|
|
242
|
+
/** Human/telemetry note for the scribe stage; '' on a verified binding. */
|
|
243
|
+
export declare function scribeIdentityNote(binding: ScribeIdentityBinding): string;
|
|
244
|
+
interface BackfillIdentityParams {
|
|
245
|
+
readonly stateManager: RuntimeStateManager;
|
|
246
|
+
readonly artifactStore: PIArtifactStore;
|
|
247
|
+
readonly scribeTaskId: string;
|
|
248
|
+
readonly dreamerTaskId: string;
|
|
249
|
+
readonly workspaceDir: string;
|
|
250
|
+
readonly now: string;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* EP002-R4 follow-up #2 + Phase A: backfill the scribe principle artifact's
|
|
254
|
+
* source_principle_id with the LEDGER principle UUID (shared by the rule and
|
|
255
|
+
* text paths — runs right after the scribe stage succeeds). Every downstream
|
|
256
|
+
* identity resolution reads this column FIRST (extractPrincipleId /
|
|
257
|
+
* EvaluatorRunner.extractPrincipleIdFromArtifact), so this single write is
|
|
258
|
+
* what binds approvals, Console grouping, ledger upgrades, and rule
|
|
259
|
+
* artifacts to the ledger UUID instead of the title namespace.
|
|
260
|
+
*
|
|
261
|
+
* Phase A contract changes vs the note-only version:
|
|
262
|
+
* - the outcome is structured (ScribeIdentityBinding), not a free string;
|
|
263
|
+
* - an existing binding that DISAGREES with the resolved ledger target is an
|
|
264
|
+
* identity_conflict — never overwritten;
|
|
265
|
+
* - a successful write is verified by reading the artifact back from the
|
|
266
|
+
* PI store (log success alone is not binding evidence — SPEC §9.1);
|
|
267
|
+
* - a pre-existing binding is only trusted when its target exists in the
|
|
268
|
+
* ledger read (listForCandidate is not applicable — we check presence by
|
|
269
|
+
* resolving through the same ledger load).
|
|
270
|
+
*
|
|
271
|
+
* Still never throws — identity enrichment must not break the generation
|
|
272
|
+
* chain; it only gates the later governance publication.
|
|
273
|
+
*/
|
|
274
|
+
export declare function backfillScribeIdentity(params: BackfillIdentityParams): Promise<ScribeIdentityBinding>;
|
|
275
|
+
export {};
|
|
207
276
|
//# sourceMappingURL=rulehost-pipeline-runner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rulehost-pipeline-runner.d.ts","sourceRoot":"","sources":["../../src/services/rulehost-pipeline-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EACL,mBAAmB,EACnB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"rulehost-pipeline-runner.d.ts","sourceRoot":"","sources":["../../src/services/rulehost-pipeline-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,22 +31,14 @@ import { RuntimeStateManager, StoreEventEmitter, DreamerRunner, DefaultDreamerVa
|
|
|
31
31
|
computeFeatureFlagsFromConfig, isFeatureEnabled,
|
|
32
32
|
// PRI-714: resolve outputLanguage from the same effective config the
|
|
33
33
|
// runners already receive (EP-07: canonical resolved value, not raw input).
|
|
34
|
-
resolveOutputLanguage, buildArtificerHostSemanticContext,
|
|
35
|
-
|
|
34
|
+
resolveOutputLanguage, buildArtificerHostSemanticContext,
|
|
35
|
+
// PRI-804(a): ledger identity lookup for the source_principle_id backfill.
|
|
36
|
+
PrincipleTreeLedgerAdapter, } from '@principles/core/runtime-v2';
|
|
37
|
+
import * as path from 'node:path';
|
|
36
38
|
import { loadPdConfig } from './pd-config-loader.js';
|
|
37
39
|
import { createEvaluatorRuntimeContext } from '@principles/host-runtime';
|
|
38
40
|
/* eslint-disable @typescript-eslint/no-use-before-define -- helpers declared after main, matching codebase convention */
|
|
39
41
|
import { compileDemoRule } from './demo-rule-compiler.js';
|
|
40
|
-
/**
|
|
41
|
-
* Layer 0 content-hash function for the internalization progressive
|
|
42
|
-
* disclosure (design §6.1). Core never imports `node:crypto`, so the plugin/CLI
|
|
43
|
-
* layer injects the algorithm. Consumed only when the
|
|
44
|
-
* `artifact_summary_redundancy` flag is on; harmless otherwise.
|
|
45
|
-
*
|
|
46
|
-
* Exported so other CLI command modules (e.g. `runtime-internalization-run-once`)
|
|
47
|
-
* can reuse the same algorithm without each re-importing `node:crypto`.
|
|
48
|
-
*/
|
|
49
|
-
export const contentHashFn = (input) => createHash('sha256').update(input).digest('hex');
|
|
50
42
|
// ── gateDeps builder ─────────────────────────────────────────────────────────
|
|
51
43
|
/**
|
|
52
44
|
* Build the sandbox gate deps for adversarial replay.
|
|
@@ -149,7 +141,7 @@ export async function runRuleHostPipeline(opts) {
|
|
|
149
141
|
stages.push({ name: 'dreamer', taskId: dreamerSeedTaskId, status: 'succeeded' });
|
|
150
142
|
}
|
|
151
143
|
else {
|
|
152
|
-
const dreamerRunner = new DreamerRunner({ stateManager, runtimeAdapter: agentAdapters.dreamer, eventEmitter, validator: new DefaultDreamerValidator(), artifactStore
|
|
144
|
+
const dreamerRunner = new DreamerRunner({ stateManager, runtimeAdapter: agentAdapters.dreamer, eventEmitter, validator: new DefaultDreamerValidator(), artifactStore }, runnerOptsFor(agentAdapters.dreamer));
|
|
153
145
|
const dreamerResult = await runStage(dreamerRunner, dreamerSeedTaskId, { maxStageRetries, pollIntervalMs });
|
|
154
146
|
stages.push(stageFromResult('dreamer', dreamerSeedTaskId, dreamerResult));
|
|
155
147
|
if (dreamerResult.status !== 'succeeded') {
|
|
@@ -162,7 +154,7 @@ export async function runRuleHostPipeline(opts) {
|
|
|
162
154
|
onProgress('philosopher', 'start');
|
|
163
155
|
const philosopherTaskId = `${correlation}-philosopher-${Date.now().toString(36)}`;
|
|
164
156
|
await createInternalizationTask(stateManager, philosopherTaskId, 'philosopher', [dreamerSeedTaskId], channel, timeoutMs);
|
|
165
|
-
const philosopherRunner = new PhilosopherRunner({ stateManager, runtimeAdapter: agentAdapters.philosopher, eventEmitter, validator: new DefaultPhilosopherValidator(), artifactStore
|
|
157
|
+
const philosopherRunner = new PhilosopherRunner({ stateManager, runtimeAdapter: agentAdapters.philosopher, eventEmitter, validator: new DefaultPhilosopherValidator(), artifactStore }, runnerOptsFor(agentAdapters.philosopher));
|
|
166
158
|
const philosopherResult = await runStage(philosopherRunner, philosopherTaskId, { maxStageRetries, pollIntervalMs });
|
|
167
159
|
stages.push(stageFromResult('philosopher', philosopherTaskId, philosopherResult));
|
|
168
160
|
if (philosopherResult.status !== 'succeeded') {
|
|
@@ -174,7 +166,7 @@ export async function runRuleHostPipeline(opts) {
|
|
|
174
166
|
onProgress('scribe', 'start');
|
|
175
167
|
const scribeTaskId = `${correlation}-scribe-${Date.now().toString(36)}`;
|
|
176
168
|
await createInternalizationTask(stateManager, scribeTaskId, 'scribe', [philosopherTaskId], channel, timeoutMs);
|
|
177
|
-
const scribeRunner = new ScribeRunner({ stateManager, runtimeAdapter: agentAdapters.scribe, eventEmitter, validator: new DefaultScribeValidator(), artifactStore
|
|
169
|
+
const scribeRunner = new ScribeRunner({ stateManager, runtimeAdapter: agentAdapters.scribe, eventEmitter, validator: new DefaultScribeValidator(), artifactStore }, runnerOptsFor(agentAdapters.scribe));
|
|
178
170
|
const scribeResult = await runStage(scribeRunner, scribeTaskId, { maxStageRetries, pollIntervalMs });
|
|
179
171
|
stages.push(stageFromResult('scribe', scribeTaskId, scribeResult));
|
|
180
172
|
if (scribeResult.status !== 'succeeded') {
|
|
@@ -182,6 +174,29 @@ export async function runRuleHostPipeline(opts) {
|
|
|
182
174
|
return rejectedResult(opts.painId, stages, `scribe_failed: ${scribeResult.failureReason ?? scribeResult.status}`);
|
|
183
175
|
}
|
|
184
176
|
onProgress('scribe', 'succeeded');
|
|
177
|
+
// EP002-R4 follow-up #2 + Owner Decision Experience v1 Phase A: backfill
|
|
178
|
+
// the LEDGER principle UUID on the scribe artifact for BOTH paths (rule +
|
|
179
|
+
// text). Downstream identity resolution (extractPrincipleId's column-first
|
|
180
|
+
// order) then binds the rule artifact, the approval grouping, and the
|
|
181
|
+
// post-approve ledger upgrade to the ledger UUID instead of falling back
|
|
182
|
+
// to principleDraft.title — the title namespace is what left rule-channel
|
|
183
|
+
// approvals stranded as title-keyed groups the Console detail page cannot
|
|
184
|
+
// reach.
|
|
185
|
+
//
|
|
186
|
+
// Phase A (SPEC §9.1): the binding outcome is STRUCTURED. An unverified
|
|
187
|
+
// binding no longer silently continues into Owner approval publication —
|
|
188
|
+
// the enqueue sites below refuse to create an approval subject for an
|
|
189
|
+
// artifact whose stable ledger identity is not established, while the
|
|
190
|
+
// generation chain and its artifacts are preserved for later replay.
|
|
191
|
+
const identityBinding = await backfillScribeIdentity({
|
|
192
|
+
stateManager, artifactStore, scribeTaskId, dreamerTaskId: dreamerSeedTaskId,
|
|
193
|
+
workspaceDir: opts.workspaceDir, now: new Date().toISOString(),
|
|
194
|
+
});
|
|
195
|
+
if (scribeIdentityNote(identityBinding) !== '') {
|
|
196
|
+
const scribeStage = stages.find((s) => s.name === 'scribe');
|
|
197
|
+
if (scribeStage)
|
|
198
|
+
scribeStage.reason = scribeIdentityNote(identityBinding).replace(/^; /, '');
|
|
199
|
+
}
|
|
185
200
|
// ── Atomic capability branching ──
|
|
186
201
|
// Per user correction (2026-06-18): ArtificerL2 + Evaluator are atomic.
|
|
187
202
|
// When OFF (or not provided), skip the adversarial loop entirely and
|
|
@@ -190,7 +205,14 @@ export async function runRuleHostPipeline(opts) {
|
|
|
190
205
|
if (!capabilityEnabled) {
|
|
191
206
|
stages.push({ name: 'adversarial_loop', status: 'skipped', reason: capabilityDisabledReason });
|
|
192
207
|
onProgress('adversarial_loop', 'skipped', capabilityDisabledReason);
|
|
193
|
-
|
|
208
|
+
// PRI-804: text principles enter the existing prompt-channel approval
|
|
209
|
+
// queue (Owner governance, same gate as RuleCode) instead of dead-ending.
|
|
210
|
+
const approvalStore = new SqliteApprovalQueueStore(stateManager.connection);
|
|
211
|
+
return await textPrincipleOnlyResult({
|
|
212
|
+
painId: opts.painId, stages, scribeTaskId, disabledReason: capabilityDisabledReason,
|
|
213
|
+
artifactStore, approvalStore, now: new Date().toISOString(),
|
|
214
|
+
identityBinding,
|
|
215
|
+
});
|
|
194
216
|
}
|
|
195
217
|
// ── Stage: adversarial loop (artificer↔evaluator) ──
|
|
196
218
|
// Capability ON: use the artificerAdapter (L2 write-test-fix) for the
|
|
@@ -234,7 +256,7 @@ export async function runRuleHostPipeline(opts) {
|
|
|
234
256
|
const hostSemanticContext = buildArtificerHostSemanticContext(evaluatorContext.registry, evaluatorContext.hostKinds ?? []);
|
|
235
257
|
const artificerRunner = new ArtificerRunner({
|
|
236
258
|
stateManager, runtimeAdapter: capability.artificerAdapter, eventEmitter, validator: new DefaultArtificerValidator(), artifactStore,
|
|
237
|
-
behaviorExamplePack: opts.behaviorExamplePack,
|
|
259
|
+
behaviorExamplePack: opts.behaviorExamplePack,
|
|
238
260
|
}, {
|
|
239
261
|
...runnerOptsFor(capability.artificerAdapter),
|
|
240
262
|
hostSemanticContext,
|
|
@@ -285,8 +307,32 @@ export async function runRuleHostPipeline(opts) {
|
|
|
285
307
|
// owner can review it. Without this, the pipeline produces a candidate
|
|
286
308
|
// artifact but it never enters the approval queue — the production chain
|
|
287
309
|
// is broken at step 2→3. Tests manually called enqueue(); production did not.
|
|
310
|
+
//
|
|
311
|
+
// Owner Decision Experience v1 Phase A (SPEC §9.1.4/§9.1.6): creating an
|
|
312
|
+
// Owner approval subject is a governance publication — it requires a
|
|
313
|
+
// verified stable ledger identity. When the chain's candidate could not be
|
|
314
|
+
// bound to exactly one existing ledger principle, the rule artifact must
|
|
315
|
+
// NOT enter the approvable set, even though the artifact itself (and every
|
|
316
|
+
// intermediate artifact) is preserved. Recovery is replay: after the
|
|
317
|
+
// ledger/identity repair, re-running the pipeline re-attempts the binding
|
|
318
|
+
// idempotently and then publishes.
|
|
288
319
|
let approvalId = null;
|
|
289
320
|
if (pipelineDecision === 'candidate_ready_for_owner_review' && loopResult.ruleArtifactId) {
|
|
321
|
+
const ruleIdentity = await resolveRuleArtifactIdentity(artifactStore, loopResult.ruleArtifactId, identityBinding);
|
|
322
|
+
if (ruleIdentity === null) {
|
|
323
|
+
const reasonCode = scribeIdentityReasonCode(identityBinding);
|
|
324
|
+
return {
|
|
325
|
+
decision: pipelineDecision,
|
|
326
|
+
painId: opts.painId,
|
|
327
|
+
stages,
|
|
328
|
+
scribeTaskId,
|
|
329
|
+
adversarialLoop: loopResult,
|
|
330
|
+
ruleArtifactId: loopResult.ruleArtifactId,
|
|
331
|
+
principleArtifactId: loopResult.principleArtifactId,
|
|
332
|
+
approvalId: null,
|
|
333
|
+
degradationReason: `identity_binding_unverified: ${reasonCode}. Rule artifact NOT enqueued for Owner approval (governance publication boundary). Artifacts preserved; repair the ledger identity and re-run this pipeline to publish.`,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
290
336
|
try {
|
|
291
337
|
const approvalStore = new SqliteApprovalQueueStore(stateManager.connection);
|
|
292
338
|
const riskLevel = getChannelRiskLevel(channel);
|
|
@@ -418,10 +464,6 @@ export function createEvaluatorRunnerDeps(inputs) {
|
|
|
418
464
|
eventEmitter,
|
|
419
465
|
validator,
|
|
420
466
|
artifactStore,
|
|
421
|
-
// Layer 0 (design §6.1): inject the content-hash function so the evaluator
|
|
422
|
-
// writer can attach a `predecessorSummary.contentHash` for staleness
|
|
423
|
-
// detection. Defaults to the module-level sha256-hex constant.
|
|
424
|
-
contentHashFn: inputs.contentHashFn ?? contentHashFn,
|
|
425
467
|
isRepairLoopEnabled: () => {
|
|
426
468
|
// rc-9: never throw on malformed config — fail safe to false so the
|
|
427
469
|
// legacy (non-repair) path runs. The malformed config is already
|
|
@@ -530,12 +572,274 @@ function rejectedResult(painId, stages, degradationReason) {
|
|
|
530
572
|
degradationReason,
|
|
531
573
|
};
|
|
532
574
|
}
|
|
575
|
+
/** Only these two statuses authorize governance publication (approval enqueue). */
|
|
576
|
+
export function isIdentityBindingVerified(binding) {
|
|
577
|
+
return binding.status === 'bound' || binding.status === 'already_bound';
|
|
578
|
+
}
|
|
579
|
+
/** Stable machine reason code for observability/logs (SPEC §9.1 vocabulary). */
|
|
580
|
+
export function scribeIdentityReasonCode(binding) {
|
|
581
|
+
switch (binding.status) {
|
|
582
|
+
case 'bound':
|
|
583
|
+
case 'already_bound':
|
|
584
|
+
return 'bound';
|
|
585
|
+
case 'no_principle_artifact':
|
|
586
|
+
return 'no_principle_artifact';
|
|
587
|
+
case 'candidate_unresolved':
|
|
588
|
+
return 'candidate_unresolved';
|
|
589
|
+
case 'ledger_missing':
|
|
590
|
+
return 'ledger_missing';
|
|
591
|
+
case 'ledger_ambiguous':
|
|
592
|
+
return 'ledger_ambiguous';
|
|
593
|
+
case 'identity_conflict':
|
|
594
|
+
return 'identity_conflict';
|
|
595
|
+
case 'bound_target_missing':
|
|
596
|
+
return 'identity_conflict';
|
|
597
|
+
case 'binding_write_failed':
|
|
598
|
+
return 'binding_write_failed';
|
|
599
|
+
case 'binding_readback_failed':
|
|
600
|
+
return 'binding_write_failed';
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/** Human/telemetry note for the scribe stage; '' on a verified binding. */
|
|
604
|
+
export function scribeIdentityNote(binding) {
|
|
605
|
+
switch (binding.status) {
|
|
606
|
+
case 'bound':
|
|
607
|
+
case 'already_bound':
|
|
608
|
+
return '';
|
|
609
|
+
case 'no_principle_artifact':
|
|
610
|
+
return '; source_principle_id_backfill_skipped: no principle artifact from scribe';
|
|
611
|
+
case 'candidate_unresolved':
|
|
612
|
+
return '; source_principle_id_backfill_skipped: no candidateId on the dreamer seed (candidate_unresolved)';
|
|
613
|
+
case 'ledger_missing':
|
|
614
|
+
return `; source_principle_id_backfill_skipped: candidate ${binding.candidateId} matches no ledger principle (ledger_missing)`;
|
|
615
|
+
case 'ledger_ambiguous':
|
|
616
|
+
return `; source_principle_id_backfill_skipped: candidate ${binding.candidateId} matches ${binding.principleIds.length} ledger principles (ledger_ambiguous)`;
|
|
617
|
+
case 'identity_conflict':
|
|
618
|
+
return `; source_principle_id_backfill_refused: artifact already bound to ${binding.existingPrincipleId}, chain resolves to ${binding.resolvedPrincipleId} (identity_conflict)`;
|
|
619
|
+
case 'bound_target_missing':
|
|
620
|
+
return `; source_principle_id_backfill_refused: artifact bound to ${binding.principleId} which is absent from the ledger (identity_conflict)`;
|
|
621
|
+
case 'binding_write_failed':
|
|
622
|
+
return `; source_principle_id_backfill_failed: ${binding.errorMessage}`;
|
|
623
|
+
case 'binding_readback_failed':
|
|
624
|
+
return `; source_principle_id_backfill_unverified: read-back mismatch for ${binding.expectedPrincipleId}: ${binding.errorMessage}`;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* PRI-804(a) + Phase A: resolve the UNIQUE ledger principle for the chain.
|
|
629
|
+
*
|
|
630
|
+
* The ledger principle is the identity the Console groups approvals by and the
|
|
631
|
+
* one `upgradeLedgerPrinciple` activates after approval. The only durable link
|
|
632
|
+
* from the internalization chain to the ledger is the candidateId carried in
|
|
633
|
+
* the dreamer task's diagnosticJson (set by the intake bridge) — the ledger
|
|
634
|
+
* entry stores it in derivedFromPainIds. Phase A hardens the lookup:
|
|
635
|
+
* `listForCandidate` (plural, same adapter the intake bridge uses — one
|
|
636
|
+
* authority, no second truth) rejects zero and multi matches instead of
|
|
637
|
+
* silently taking the first hit.
|
|
638
|
+
*/
|
|
639
|
+
async function resolveLedgerIdentity(stateManager, dreamerTaskId, workspaceDir) {
|
|
640
|
+
let candidateId;
|
|
641
|
+
try {
|
|
642
|
+
const dreamerTask = await stateManager.getTask(dreamerTaskId);
|
|
643
|
+
if (typeof dreamerTask?.diagnosticJson === 'string') {
|
|
644
|
+
const parsed = JSON.parse(dreamerTask.diagnosticJson);
|
|
645
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
646
|
+
&& Object.hasOwn(parsed, 'candidateId')) {
|
|
647
|
+
const stored = Reflect.get(parsed, 'candidateId');
|
|
648
|
+
if (typeof stored === 'string' && stored.length > 0)
|
|
649
|
+
candidateId = stored;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
catch {
|
|
654
|
+
return { status: 'candidate_unresolved' }; // malformed dreamer diagnosticJson
|
|
655
|
+
}
|
|
656
|
+
if (!candidateId)
|
|
657
|
+
return { status: 'candidate_unresolved' };
|
|
658
|
+
let matches;
|
|
659
|
+
try {
|
|
660
|
+
const ledger = new PrincipleTreeLedgerAdapter({ stateDir: path.join(workspaceDir, '.state') });
|
|
661
|
+
matches = ledger.listForCandidate(candidateId);
|
|
662
|
+
}
|
|
663
|
+
catch {
|
|
664
|
+
// Ledger unreadable cannot be distinguished from "no ledger" here — the
|
|
665
|
+
// write attempt below will surface the concrete error; treat as missing
|
|
666
|
+
// with the skip note (same observable class as before Phase A).
|
|
667
|
+
return { status: 'ledger_missing', candidateId };
|
|
668
|
+
}
|
|
669
|
+
if (matches.length === 0)
|
|
670
|
+
return { status: 'ledger_missing', candidateId };
|
|
671
|
+
if (matches.length > 1) {
|
|
672
|
+
return { status: 'ledger_ambiguous', candidateId, principleIds: matches.map((m) => m.id) };
|
|
673
|
+
}
|
|
674
|
+
const [unique] = matches;
|
|
675
|
+
if (unique === undefined)
|
|
676
|
+
return { status: 'ledger_missing', candidateId };
|
|
677
|
+
return { status: 'resolved', principleId: unique.id };
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* EP002-R4 follow-up #2 + Phase A: backfill the scribe principle artifact's
|
|
681
|
+
* source_principle_id with the LEDGER principle UUID (shared by the rule and
|
|
682
|
+
* text paths — runs right after the scribe stage succeeds). Every downstream
|
|
683
|
+
* identity resolution reads this column FIRST (extractPrincipleId /
|
|
684
|
+
* EvaluatorRunner.extractPrincipleIdFromArtifact), so this single write is
|
|
685
|
+
* what binds approvals, Console grouping, ledger upgrades, and rule
|
|
686
|
+
* artifacts to the ledger UUID instead of the title namespace.
|
|
687
|
+
*
|
|
688
|
+
* Phase A contract changes vs the note-only version:
|
|
689
|
+
* - the outcome is structured (ScribeIdentityBinding), not a free string;
|
|
690
|
+
* - an existing binding that DISAGREES with the resolved ledger target is an
|
|
691
|
+
* identity_conflict — never overwritten;
|
|
692
|
+
* - a successful write is verified by reading the artifact back from the
|
|
693
|
+
* PI store (log success alone is not binding evidence — SPEC §9.1);
|
|
694
|
+
* - a pre-existing binding is only trusted when its target exists in the
|
|
695
|
+
* ledger read (listForCandidate is not applicable — we check presence by
|
|
696
|
+
* resolving through the same ledger load).
|
|
697
|
+
*
|
|
698
|
+
* Still never throws — identity enrichment must not break the generation
|
|
699
|
+
* chain; it only gates the later governance publication.
|
|
700
|
+
*/
|
|
701
|
+
export async function backfillScribeIdentity(params) {
|
|
702
|
+
const { stateManager, artifactStore, scribeTaskId, dreamerTaskId, workspaceDir, now } = params;
|
|
703
|
+
try {
|
|
704
|
+
const arts = await artifactStore.listBySourceTaskId(scribeTaskId);
|
|
705
|
+
const principleArt = arts.find((a) => a.artifactKind === 'principle');
|
|
706
|
+
if (!principleArt)
|
|
707
|
+
return { status: 'no_principle_artifact' };
|
|
708
|
+
const resolution = await resolveLedgerIdentity(stateManager, dreamerTaskId, workspaceDir);
|
|
709
|
+
if (principleArt.sourcePrincipleId) {
|
|
710
|
+
// Already bound (rerun). Idempotent success only when the binding is
|
|
711
|
+
// consistent with what the chain resolves to now.
|
|
712
|
+
if (resolution.status === 'resolved') {
|
|
713
|
+
if (resolution.principleId === principleArt.sourcePrincipleId) {
|
|
714
|
+
return { status: 'already_bound', principleId: principleArt.sourcePrincipleId };
|
|
715
|
+
}
|
|
716
|
+
return {
|
|
717
|
+
status: 'identity_conflict',
|
|
718
|
+
existingPrincipleId: principleArt.sourcePrincipleId,
|
|
719
|
+
resolvedPrincipleId: resolution.principleId,
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
if (resolution.status === 'ledger_ambiguous')
|
|
723
|
+
return resolution;
|
|
724
|
+
// Chain candidate unresolved/missing on this replay — the durable
|
|
725
|
+
// binding still stands only if its target is a real ledger principle.
|
|
726
|
+
const ledger = new PrincipleTreeLedgerAdapter({ stateDir: path.join(workspaceDir, '.state') });
|
|
727
|
+
if (ledger.hasPrinciple(principleArt.sourcePrincipleId)) {
|
|
728
|
+
return { status: 'already_bound', principleId: principleArt.sourcePrincipleId };
|
|
729
|
+
}
|
|
730
|
+
return { status: 'bound_target_missing', principleId: principleArt.sourcePrincipleId };
|
|
731
|
+
}
|
|
732
|
+
if (resolution.status !== 'resolved')
|
|
733
|
+
return resolution;
|
|
734
|
+
const ledgerPrincipleId = resolution.principleId;
|
|
735
|
+
await artifactStore.upsertArtifact({ ...principleArt, sourcePrincipleId: ledgerPrincipleId, updatedAt: now });
|
|
736
|
+
// Read-back verification: the PI store must return the bound ID.
|
|
737
|
+
try {
|
|
738
|
+
const readBack = await artifactStore.getArtifactById(principleArt.artifactId);
|
|
739
|
+
if (readBack?.sourcePrincipleId !== ledgerPrincipleId) {
|
|
740
|
+
return {
|
|
741
|
+
status: 'binding_readback_failed',
|
|
742
|
+
expectedPrincipleId: ledgerPrincipleId,
|
|
743
|
+
errorMessage: `read-back sourcePrincipleId=${readBack?.sourcePrincipleId ?? 'null'}`,
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
catch (readErr) {
|
|
748
|
+
const msg = readErr instanceof Error ? readErr.message : String(readErr);
|
|
749
|
+
return { status: 'binding_readback_failed', expectedPrincipleId: ledgerPrincipleId, errorMessage: msg };
|
|
750
|
+
}
|
|
751
|
+
return { status: 'bound', principleId: ledgerPrincipleId };
|
|
752
|
+
}
|
|
753
|
+
catch (err) {
|
|
754
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
755
|
+
return { status: 'binding_write_failed', errorMessage: msg };
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Phase A (SPEC §9.1.6): before the rule artifact enters the approvable set,
|
|
760
|
+
* verify the chain's ledger identity is VERIFIED and the rule artifact carries
|
|
761
|
+
* exactly that binding. A chain without a verified unique ledger target must
|
|
762
|
+
* not publish, even if the artifact itself carries some id (e.g. a legacy
|
|
763
|
+
* title-namespace value the evaluator would have echoed). Returns the
|
|
764
|
+
* verified principle id, or null when publication must be refused.
|
|
765
|
+
*/
|
|
766
|
+
async function resolveRuleArtifactIdentity(artifactStore, ruleArtifactId, identityBinding) {
|
|
767
|
+
if (!isIdentityBindingVerified(identityBinding))
|
|
768
|
+
return null;
|
|
769
|
+
let artifactSourcePrincipleId;
|
|
770
|
+
try {
|
|
771
|
+
const ruleArt = await artifactStore.getArtifactById(ruleArtifactId);
|
|
772
|
+
artifactSourcePrincipleId = ruleArt?.sourcePrincipleId;
|
|
773
|
+
}
|
|
774
|
+
catch {
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
if (artifactSourcePrincipleId === undefined || artifactSourcePrincipleId !== identityBinding.principleId)
|
|
778
|
+
return null;
|
|
779
|
+
return artifactSourcePrincipleId;
|
|
780
|
+
}
|
|
533
781
|
async function textPrincipleOnlyResult(params) {
|
|
534
|
-
const { painId, stages, scribeTaskId, disabledReason, artifactStore } = params;
|
|
782
|
+
const { painId, stages, scribeTaskId, disabledReason, artifactStore, approvalStore, now, identityBinding } = params;
|
|
535
783
|
// Look up the principle artifact produced by the scribe stage.
|
|
536
784
|
try {
|
|
537
785
|
const arts = await artifactStore.listBySourceTaskId(scribeTaskId);
|
|
538
786
|
const principleArt = arts.find((a) => a.artifactKind === 'principle');
|
|
787
|
+
// Identity backfill already ran on the shared post-scribe step
|
|
788
|
+
// (backfillScribeIdentity) for BOTH paths; nothing to do here.
|
|
789
|
+
// On the text path the evaluator never runs, so nothing else flips the
|
|
790
|
+
// validation status — mark it validated here (same store API the
|
|
791
|
+
// evaluator uses; scribe task success already implies its validator
|
|
792
|
+
// passed — peer-runner contract: output_invalid tasks never succeed).
|
|
793
|
+
let identityNote = '';
|
|
794
|
+
if (principleArt && principleArt.validationStatus !== 'validated') {
|
|
795
|
+
try {
|
|
796
|
+
await artifactStore.updateValidationStatus(principleArt.artifactId, 'validated');
|
|
797
|
+
}
|
|
798
|
+
catch (err) {
|
|
799
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
800
|
+
identityNote = `; validation_status_mark_failed: ${msg}`;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
// PRI-804: a text principle is a prompt-channel intervention and follows the
|
|
804
|
+
// same Owner governance as RuleCode (Owner decision 2026-09-15): enqueue it
|
|
805
|
+
// into the EXISTING approval queue so the Owner can approve it in Console.
|
|
806
|
+
// Deterministic approval id (apr_prompt_<artifactId>) + INSERT OR IGNORE make
|
|
807
|
+
// replays idempotent. Enqueue failure degrades observably (rc-9), never
|
|
808
|
+
// throwing away the artifact.
|
|
809
|
+
//
|
|
810
|
+
// Phase A governance publication boundary: an enqueue is only allowed when
|
|
811
|
+
// the artifact's ledger identity binding is VERIFIED. Unverified bindings
|
|
812
|
+
// keep the artifact (and its validation status) but must not create an
|
|
813
|
+
// Owner approval subject; recovery is a replay after identity repair.
|
|
814
|
+
let approvalId = null;
|
|
815
|
+
let enqueueNote = '';
|
|
816
|
+
if (!principleArt) {
|
|
817
|
+
enqueueNote = '; approval_enqueue_skipped: no principle artifact from scribe';
|
|
818
|
+
}
|
|
819
|
+
else if (!isIdentityBindingVerified(identityBinding)) {
|
|
820
|
+
const reasonCode = scribeIdentityReasonCode(identityBinding);
|
|
821
|
+
enqueueNote = `; approval_enqueue_skipped: identity_binding_unverified (${reasonCode}). Principle artifact preserved; repair the ledger identity and re-run this pipeline to publish.`;
|
|
822
|
+
}
|
|
823
|
+
else if (identityBinding.principleId !== principleArt.sourcePrincipleId) {
|
|
824
|
+
enqueueNote = `; approval_enqueue_skipped: identity_binding_unverified (identity_conflict: chain resolves to ${identityBinding.principleId}, artifact carries ${principleArt.sourcePrincipleId ?? 'no binding'}). Principle artifact preserved; repair the ledger identity and re-run this pipeline to publish.`;
|
|
825
|
+
}
|
|
826
|
+
else {
|
|
827
|
+
try {
|
|
828
|
+
const record = await approvalStore.enqueue({
|
|
829
|
+
artifactId: principleArt.artifactId,
|
|
830
|
+
channel: 'prompt',
|
|
831
|
+
riskLevel: getChannelRiskLevel('prompt'),
|
|
832
|
+
summary: `Text principle candidate for pain ${painId}`,
|
|
833
|
+
triggerReason: `text_principle_only: pain=${painId}, principle=${principleArt.artifactId}; code-rule capability off (${disabledReason})`,
|
|
834
|
+
}, now);
|
|
835
|
+
const { approvalId: enqueuedApprovalId } = record;
|
|
836
|
+
approvalId = enqueuedApprovalId;
|
|
837
|
+
}
|
|
838
|
+
catch (err) {
|
|
839
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
840
|
+
enqueueNote = `; approval_enqueue_failed: ${msg}. Manual enqueue required: pd activation dispatch --artifact-id ${principleArt.artifactId} --channel prompt`;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
539
843
|
return {
|
|
540
844
|
decision: 'text_principle_only',
|
|
541
845
|
painId,
|
|
@@ -543,8 +847,8 @@ async function textPrincipleOnlyResult(params) {
|
|
|
543
847
|
scribeTaskId,
|
|
544
848
|
ruleArtifactId: null,
|
|
545
849
|
principleArtifactId: principleArt?.artifactId ?? null,
|
|
546
|
-
approvalId
|
|
547
|
-
degradationReason: `code_rule_capability_off: ${disabledReason}`,
|
|
850
|
+
approvalId,
|
|
851
|
+
degradationReason: `code_rule_capability_off: ${disabledReason}${identityNote}${enqueueNote}`,
|
|
548
852
|
};
|
|
549
853
|
}
|
|
550
854
|
catch (error) {
|