create-principles-disciple 1.145.1 → 1.145.4

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.
Files changed (173) hide show
  1. package/_release/product-identity.json +2 -2
  2. package/codex-adapter/dist/worker/workspace-worker.js +2 -1
  3. package/codex-adapter/package.json +1 -1
  4. package/console/dist/server/config/pd-config-store.js +3 -0
  5. package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +4 -4
  6. package/console/dist/ui/i18n/en.json +17 -16
  7. package/console/dist/ui/i18n/zh-CN.json +17 -16
  8. package/console/dist/ui/pages/focus/FocusPage.js +9 -5
  9. package/console/dist/ui/pages/focus/OwnerDecisionCard.js +1 -1
  10. package/console/dist/ui/pages/principles/PrincipleDetailPage.js +10 -4
  11. package/console/dist/ui/pages/principles/PrinciplesPage.js +1 -1
  12. package/console/dist/ui/utils/reading-layout.d.ts +19 -0
  13. package/console/dist/ui/utils/reading-layout.js +46 -0
  14. package/console/dist/web/assets/app.js +30 -28
  15. package/core/dist/runtime-v2/index.d.ts +13 -3
  16. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  17. package/core/dist/runtime-v2/index.js +6 -1
  18. package/core/dist/runtime-v2/index.js.map +1 -1
  19. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts +25 -6
  20. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts.map +1 -1
  21. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js +21 -7
  22. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js.map +1 -1
  23. package/core/dist/runtime-v2/internalization/artificer-run-correlation.d.ts +82 -0
  24. package/core/dist/runtime-v2/internalization/artificer-run-correlation.d.ts.map +1 -0
  25. package/core/dist/runtime-v2/internalization/artificer-run-correlation.js +79 -0
  26. package/core/dist/runtime-v2/internalization/artificer-run-correlation.js.map +1 -0
  27. package/core/dist/runtime-v2/internalization/artificer-runner.d.ts +16 -2
  28. package/core/dist/runtime-v2/internalization/artificer-runner.d.ts.map +1 -1
  29. package/core/dist/runtime-v2/internalization/artificer-runner.js +89 -9
  30. package/core/dist/runtime-v2/internalization/artificer-runner.js.map +1 -1
  31. package/core/dist/runtime-v2/internalization/consumer-evidence-preflight.d.ts +141 -0
  32. package/core/dist/runtime-v2/internalization/consumer-evidence-preflight.d.ts.map +1 -0
  33. package/core/dist/runtime-v2/internalization/consumer-evidence-preflight.js +284 -0
  34. package/core/dist/runtime-v2/internalization/consumer-evidence-preflight.js.map +1 -0
  35. package/core/dist/runtime-v2/internalization/evidence-readiness.d.ts +170 -0
  36. package/core/dist/runtime-v2/internalization/evidence-readiness.d.ts.map +1 -0
  37. package/core/dist/runtime-v2/internalization/evidence-readiness.js +335 -0
  38. package/core/dist/runtime-v2/internalization/evidence-readiness.js.map +1 -0
  39. package/core/dist/runtime-v2/internalization/owner-evidence-recovery.d.ts +190 -0
  40. package/core/dist/runtime-v2/internalization/owner-evidence-recovery.d.ts.map +1 -0
  41. package/core/dist/runtime-v2/internalization/owner-evidence-recovery.js +488 -0
  42. package/core/dist/runtime-v2/internalization/owner-evidence-recovery.js.map +1 -0
  43. package/core/dist/runtime-v2/internalization/owner-retry.d.ts +23 -1
  44. package/core/dist/runtime-v2/internalization/owner-retry.d.ts.map +1 -1
  45. package/core/dist/runtime-v2/internalization/owner-retry.js +19 -3
  46. package/core/dist/runtime-v2/internalization/owner-retry.js.map +1 -1
  47. package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +16 -1
  48. package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
  49. package/core/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
  50. package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts +44 -0
  51. package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
  52. package/core/dist/runtime-v2/internalization/pitask-metadata.js +75 -0
  53. package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
  54. package/core/dist/runtime-v2/recovery-actions-log.d.ts +55 -0
  55. package/core/dist/runtime-v2/recovery-actions-log.d.ts.map +1 -1
  56. package/core/dist/runtime-v2/recovery-actions-log.js +84 -3
  57. package/core/dist/runtime-v2/recovery-actions-log.js.map +1 -1
  58. package/core/dist/runtime-v2/recovery-sweep-service.d.ts +116 -1
  59. package/core/dist/runtime-v2/recovery-sweep-service.d.ts.map +1 -1
  60. package/core/dist/runtime-v2/recovery-sweep-service.js +252 -13
  61. package/core/dist/runtime-v2/recovery-sweep-service.js.map +1 -1
  62. package/core/dist/runtime-v2/runtime-state-handle.d.ts +8 -0
  63. package/core/dist/runtime-v2/runtime-state-handle.d.ts.map +1 -1
  64. package/core/dist/runtime-v2/runtime-state-handle.js +1 -0
  65. package/core/dist/runtime-v2/runtime-state-handle.js.map +1 -1
  66. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +16 -1
  67. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
  68. package/core/dist/runtime-v2/store/runtime-state-manager.js +7 -2
  69. package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
  70. package/core/dist/runtime-v2/store/task/memory-task-store.d.ts +7 -1
  71. package/core/dist/runtime-v2/store/task/memory-task-store.d.ts.map +1 -1
  72. package/core/dist/runtime-v2/store/task/memory-task-store.js +16 -1
  73. package/core/dist/runtime-v2/store/task/memory-task-store.js.map +1 -1
  74. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts +7 -1
  75. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
  76. package/core/dist/runtime-v2/store/task/sqlite-task-store.js +31 -2
  77. package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
  78. package/core/dist/runtime-v2/store/task/task-store.d.ts +19 -5
  79. package/core/dist/runtime-v2/store/task/task-store.d.ts.map +1 -1
  80. package/core/dist/telemetry-event.d.ts +2 -2
  81. package/core/dist/telemetry-event.d.ts.map +1 -1
  82. package/core/dist/telemetry-event.js +13 -0
  83. package/core/dist/telemetry-event.js.map +1 -1
  84. package/core/package.json +5 -1
  85. package/dist/installer.d.ts +1 -1
  86. package/dist/installer.d.ts.map +1 -1
  87. package/dist/installer.js +5 -1
  88. package/dist/installer.js.map +1 -1
  89. package/dist/installers/workbuddy-host-installer.d.ts +16 -3
  90. package/dist/installers/workbuddy-host-installer.d.ts.map +1 -1
  91. package/dist/installers/workbuddy-host-installer.js +62 -11
  92. package/dist/installers/workbuddy-host-installer.js.map +1 -1
  93. package/dist/manifest-host-union.d.ts +23 -0
  94. package/dist/manifest-host-union.d.ts.map +1 -0
  95. package/dist/manifest-host-union.js +22 -0
  96. package/dist/manifest-host-union.js.map +1 -0
  97. package/dist/uninstaller.d.ts +2 -1
  98. package/dist/uninstaller.d.ts.map +1 -1
  99. package/dist/uninstaller.js +7 -1
  100. package/dist/uninstaller.js.map +1 -1
  101. package/host-runtime/dist/behavior-example-pack-assembler.d.ts +41 -0
  102. package/host-runtime/dist/behavior-example-pack-assembler.js +243 -0
  103. package/host-runtime/dist/behavior-history.d.ts +6 -0
  104. package/host-runtime/dist/behavior-history.js +76 -0
  105. package/host-runtime/dist/index.d.ts +3 -0
  106. package/host-runtime/dist/index.js +3 -0
  107. package/host-runtime/dist/internalization-consumer-cycle.d.ts +12 -1
  108. package/host-runtime/dist/internalization-consumer-cycle.js +141 -16
  109. package/host-runtime/dist/workspace-behavior-evidence.d.ts +3 -0
  110. package/host-runtime/dist/workspace-behavior-evidence.js +74 -0
  111. package/host-runtime/dist/workspace-telemetry-emitter.js +13 -0
  112. package/host-runtime/package.json +10 -2
  113. package/install-layout/dist/index.d.ts +40 -1
  114. package/install-layout/dist/index.js +88 -6
  115. package/install-layout/package.json +1 -1
  116. package/package.json +3 -3
  117. package/pd-cli/dist/commands/runtime-internalization-audit-artificer-evidence.d.ts +44 -0
  118. package/pd-cli/dist/commands/runtime-internalization-audit-artificer-evidence.d.ts.map +1 -0
  119. package/pd-cli/dist/commands/runtime-internalization-audit-artificer-evidence.js +138 -0
  120. package/pd-cli/dist/commands/runtime-internalization-audit-artificer-evidence.js.map +1 -0
  121. package/pd-cli/dist/commands/runtime-internalization-evidence.d.ts +71 -0
  122. package/pd-cli/dist/commands/runtime-internalization-evidence.d.ts.map +1 -0
  123. package/pd-cli/dist/commands/runtime-internalization-evidence.js +405 -0
  124. package/pd-cli/dist/commands/runtime-internalization-evidence.js.map +1 -0
  125. package/pd-cli/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
  126. package/pd-cli/dist/commands/runtime-internalization-run-once.js +22 -1
  127. package/pd-cli/dist/commands/runtime-internalization-run-once.js.map +1 -1
  128. package/pd-cli/dist/commands/runtime-internalization-run-rulehost.d.ts.map +1 -1
  129. package/pd-cli/dist/commands/runtime-internalization-run-rulehost.js +45 -4
  130. package/pd-cli/dist/commands/runtime-internalization-run-rulehost.js.map +1 -1
  131. package/pd-cli/dist/commands/runtime-recovery-failed-task.d.ts +22 -0
  132. package/pd-cli/dist/commands/runtime-recovery-failed-task.d.ts.map +1 -0
  133. package/pd-cli/dist/commands/runtime-recovery-failed-task.js +470 -0
  134. package/pd-cli/dist/commands/runtime-recovery-failed-task.js.map +1 -0
  135. package/pd-cli/dist/commands/runtime-recovery-failed-tasks.d.ts +24 -0
  136. package/pd-cli/dist/commands/runtime-recovery-failed-tasks.d.ts.map +1 -1
  137. package/pd-cli/dist/commands/runtime-recovery-failed-tasks.js +4 -1
  138. package/pd-cli/dist/commands/runtime-recovery-failed-tasks.js.map +1 -1
  139. package/pd-cli/dist/index.js +13 -0
  140. package/pd-cli/dist/index.js.map +1 -1
  141. package/pd-cli/dist/services/artificer-evidence-audit.d.ts +156 -0
  142. package/pd-cli/dist/services/artificer-evidence-audit.d.ts.map +1 -0
  143. package/pd-cli/dist/services/artificer-evidence-audit.js +321 -0
  144. package/pd-cli/dist/services/artificer-evidence-audit.js.map +1 -0
  145. package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
  146. package/pd-cli/dist/services/rulehost-pipeline-runner.js +17 -3
  147. package/pd-cli/dist/services/rulehost-pipeline-runner.js.map +1 -1
  148. package/pd-cli/package.json +1 -1
  149. package/plugin/dist/bundle.js +545 -541
  150. package/plugin/dist/core/behavior-example-pack-assembler.d.ts +4 -32
  151. package/plugin/dist/core/rule-context-assembler.d.ts +0 -9
  152. package/plugin/dist/openclaw.plugin.json +1 -1
  153. package/plugin/dist/rulehost-evidence.js +27 -27
  154. package/plugin/openclaw.plugin.json +1 -1
  155. package/plugin/package.json +1 -1
  156. package/release-manager/dist/installer.d.ts +1 -1
  157. package/release-manager/dist/installer.d.ts.map +1 -1
  158. package/release-manager/dist/installer.js +5 -1
  159. package/release-manager/dist/installer.js.map +1 -1
  160. package/release-manager/dist/installers/workbuddy-host-installer.d.ts +16 -3
  161. package/release-manager/dist/installers/workbuddy-host-installer.d.ts.map +1 -1
  162. package/release-manager/dist/installers/workbuddy-host-installer.js +62 -11
  163. package/release-manager/dist/installers/workbuddy-host-installer.js.map +1 -1
  164. package/release-manager/dist/manifest-host-union.d.ts +23 -0
  165. package/release-manager/dist/manifest-host-union.d.ts.map +1 -0
  166. package/release-manager/dist/manifest-host-union.js +22 -0
  167. package/release-manager/dist/manifest-host-union.js.map +1 -0
  168. package/release-manager/dist/uninstaller.d.ts +2 -1
  169. package/release-manager/dist/uninstaller.d.ts.map +1 -1
  170. package/release-manager/dist/uninstaller.js +7 -1
  171. package/release-manager/dist/uninstaller.js.map +1 -1
  172. package/release-manager/package.json +2 -2
  173. package/workbuddy-adapter/package.json +1 -1
@@ -0,0 +1,41 @@
1
+ import type { BehaviorExamplePack } from '@principles/core/runtime-v2';
2
+ import type { RuleHostEvidenceRow, RuleHostContextRow } from '@principles/core/trajectory-types';
3
+ import type { ToolSemanticRegistry } from '@principles/core/runtime-v2';
4
+ export interface BehaviorExampleDataSource {
5
+ getPainEventByCanonicalId(id: string): {
6
+ id: number;
7
+ sessionId: string;
8
+ } | null;
9
+ getRuleHostEvidenceRow(id: number): RuleHostEvidenceRow | null;
10
+ getRuleHostContextRowsBefore(sessionId: string, beforeId: number, limit: number): {
11
+ rows: readonly RuleHostContextRow[];
12
+ truncated: boolean;
13
+ };
14
+ }
15
+ export interface BehaviorExamplePackAssemblerOptions {
16
+ readonly source: BehaviorExampleDataSource;
17
+ readonly toolSemantics: ToolSemanticRegistry;
18
+ }
19
+ export interface BehaviorExamplePackAssemblerInput {
20
+ /** Canonical pain ID (e.g. pain_<ts>_<hash>) — entry point into pain lineage. */
21
+ readonly sourcePainId: string;
22
+ /** Owner's natural-language desired outcome — must be non-empty. */
23
+ readonly ownerDesiredOutcome: string;
24
+ /** Owner-selected call that demonstrates behaviour to block. */
25
+ readonly sourceNegativeToolCallId: number;
26
+ /** Owner-selected calls that demonstrate behaviour to allow (1..3). */
27
+ readonly positiveToolCallIds: readonly number[];
28
+ /**
29
+ * Project directory, used to detect absolute paths that should be redacted
30
+ * to relative/basename form in the assembled pack.
31
+ */
32
+ readonly projectDir: string;
33
+ }
34
+ export declare class BehaviorExamplePackAssembler {
35
+ private readonly db;
36
+ private readonly toolSemantics;
37
+ constructor(opts: BehaviorExamplePackAssemblerOptions);
38
+ /** Assemble only from explicit Owner labels; execution outcome is not a label. */
39
+ assemble(input: BehaviorExamplePackAssemblerInput): BehaviorExamplePack;
40
+ private buildSelectedCase;
41
+ }
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Shared Owner-labelled BehaviorExamplePack assembly (PRI-484 / PRI-992).
3
+ *
4
+ * Reads pain lineage + trajectory rows from TrajectoryDatabase and assembles
5
+ * a validated BehaviorExamplePack for the Artificer. Fail loud on any gap
6
+ * (missing pain, empty trajectory, invalid pack) — no silent fallback.
7
+ *
8
+ * Err-prevention:
9
+ * - ERR-001: every DB row field validated as `unknown` (no `as` bypass on
10
+ * parsed params_json or row casts).
11
+ * - ERR-069: Artificer shared schema — invalid pack must fail loud.
12
+ * - ERR-026: reuses production TrajectoryDatabase (no hand-written DDL).
13
+ * - rc-2: structural type guards, no `as T` on parsed input.
14
+ * - rc-5: Object.hasOwn over `in`.
15
+ * - rc-9: every failure path throws a structured Error with a clear reason.
16
+ * - PRI-980 review P2-1: trajectory STORE I/O failures throw the typed
17
+ * `evidence_source_error` (readTrajectoryStore) so the readiness contract
18
+ * preserves them as storage faults, not Owner evidence faults (§6.2).
19
+ *
20
+ * Spec: docs/superpowers/specs/2026-06-27-rulecode-context-vision-design.md §7.2
21
+ *
22
+ * Boundary: this module is I/O. Pure logic (type + validator) lives in
23
+ * `@principles/core/runtime-v2` → `behavior-example-pack.ts`.
24
+ */
25
+ import { buildRuleHostAction } from '@principles/core/runtime-v2/internalization/rule-host-input-builder';
26
+ import { computeBehaviorFacts } from '@principles/core/runtime-v2/internalization/rule-context-v2';
27
+ import { validateBehaviorExamplePack } from '@principles/core/runtime-v2/internalization/behavior-example-pack';
28
+ // Factory lives in evidence-readiness.ts (the classification authority); it is
29
+ // consumed through its registered LEAF subpath. A VALUE import of the
30
+ // runtime-v2 barrel on this satellite path re-pulls the whole LLM graph and
31
+ // fails the satellite-purity guard (OPT-002, scripts/build/check-satellite-purity.mjs) —
32
+ // the leaf is on core's exports allowlist for exactly this consumer.
33
+ import { createOwnerEvidenceScopeError } from '@principles/core/runtime-v2/internalization/evidence-readiness';
34
+ import { assembleBehaviorHistory } from './behavior-history.js';
35
+ // ── internal constants ─────────────────────────────────────────────────────
36
+ const MAX_POSITIVES = 3;
37
+ const MAX_EVIDENCE_REFS = 5;
38
+ const HISTORY_LIMIT = 100;
39
+ const PROTO_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
40
+ /**
41
+ * PRI-980 review P2-1 (SPEC-PD-IAER-001 §6.2 case 3): the trajectory STORE
42
+ * throwing is an I/O/storage failure of the evidence source itself — it must
43
+ * surface as the typed `evidence_source_error` kind so the readiness contract
44
+ * preserves it as a storage fault instead of misreporting "Owner labelled bad
45
+ * evidence". Structural, never message sniffing: the store RETURNING no row
46
+ * is a data gap (the typed scope kinds below); the store THROWING is the I/O
47
+ * failure signal. Assembler-owned data-shape validation errors stay plain
48
+ * Errors (→ `invalid`), unchanged.
49
+ */
50
+ function readTrajectoryStore(what, op) {
51
+ try {
52
+ return op();
53
+ }
54
+ catch (error) {
55
+ const message = error instanceof Error ? error.message : String(error);
56
+ throw createOwnerEvidenceScopeError('evidence_source_error', `[BehaviorExamplePackAssembler] trajectory store ${what} failed (evidence source error, SPEC-PD-IAER-001 §6.2): ${message}`);
57
+ }
58
+ }
59
+ // ── type guards (rc-2: no `as` bypass) ─────────────────────────────────────
60
+ function isPlainObject(value) {
61
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
62
+ }
63
+ function hasNoProtoKeys(value) {
64
+ for (const key of Object.keys(value)) {
65
+ if (PROTO_KEYS.has(key))
66
+ return false;
67
+ }
68
+ return true;
69
+ }
70
+ // ── BehaviorExamplePackAssembler ───────────────────────────────────────────
71
+ /**
72
+ * Host-neutral assembler: pain lineage + trajectory → validated BehaviorExamplePack.
73
+ *
74
+ * The caller owns source lifecycle and supplies the real host vocabulary.
75
+ */
76
+ function validateOwnerSelection(input) {
77
+ if (!Number.isSafeInteger(input.sourceNegativeToolCallId) || input.sourceNegativeToolCallId <= 0) {
78
+ throw new Error('[BehaviorExamplePackAssembler] sourceNegativeToolCallId must be a positive integer');
79
+ }
80
+ if (!Array.isArray(input.positiveToolCallIds) || input.positiveToolCallIds.length === 0 || input.positiveToolCallIds.length > MAX_POSITIVES) {
81
+ throw new Error('[BehaviorExamplePackAssembler] positiveToolCallIds must contain 1 to at most 3 IDs');
82
+ }
83
+ const allIds = [input.sourceNegativeToolCallId, ...input.positiveToolCallIds];
84
+ if (allIds.some((id) => !Number.isSafeInteger(id) || id <= 0) || new Set(allIds).size !== allIds.length) {
85
+ throw new Error('[BehaviorExamplePackAssembler] selected tool call IDs must be unique positive integers');
86
+ }
87
+ }
88
+ function requireSelectedRow(row, id) {
89
+ if (!row) {
90
+ // PRI-983: typed scope classification → stable `owner_behavior_examples_stale`
91
+ // at the readiness contract (the referenced trajectory row does not exist).
92
+ throw createOwnerEvidenceScopeError('tool_call_not_found', `[BehaviorExamplePackAssembler] selected tool call id=${id} not found`);
93
+ }
94
+ return row;
95
+ }
96
+ // ── internal: case builder ─────────────────────────────────────────────────
97
+ /**
98
+ * Build a GoldenTraceCaseInput from a trajectory tool_call row.
99
+ *
100
+ * ERR-001: parses params_json as unknown and structurally validates (must be
101
+ * a plain object without proto-pollution keys). Throws on malformed input
102
+ * (rc-9: no silent fallback).
103
+ */
104
+ function buildCaseFromRow(row, kind, expectedDecision) {
105
+ if (typeof row.toolName !== 'string' || row.toolName.length === 0) {
106
+ throw new Error(`[BehaviorExamplePackAssembler] row id=${row.id}: toolName must be a non-empty string (rc-9 fail loud)`);
107
+ }
108
+ if (typeof row.paramsJson !== 'string') {
109
+ throw new Error(`[BehaviorExamplePackAssembler] row id=${row.id}: paramsJson must be a string (rc-9 fail loud)`);
110
+ }
111
+ let parsedParams;
112
+ try {
113
+ parsedParams = JSON.parse(row.paramsJson);
114
+ }
115
+ catch {
116
+ throw new Error(`[BehaviorExamplePackAssembler] row id=${row.id}: paramsJson is not valid JSON (rc-9 fail loud)`);
117
+ }
118
+ if (!isPlainObject(parsedParams)) {
119
+ throw new Error(`[BehaviorExamplePackAssembler] row id=${row.id}: paramsJson must parse to a non-array object (rc-9 fail loud)`);
120
+ }
121
+ if (!hasNoProtoKeys(parsedParams)) {
122
+ throw new Error(`[BehaviorExamplePackAssembler] row id=${row.id}: paramsJson must not carry prototype-pollution keys (ERR-076, rc-9 fail loud)`);
123
+ }
124
+ return {
125
+ caseId: `case-${kind}-${row.id}`,
126
+ kind,
127
+ toolName: row.toolName,
128
+ params: parsedParams,
129
+ expectedDecision,
130
+ };
131
+ }
132
+ /**
133
+ * Redact sensitive fields from tool call params before they enter the pack.
134
+ *
135
+ * First-version policy:
136
+ * - Absolute paths outside the project tree → replaced with `<redacted:absolute-path>`
137
+ * - String values that look like secrets (long base64 / AKIA-style keys / etc.)
138
+ * → replaced with `<redacted:secret>`
139
+ *
140
+ * Records each redaction in `notes` so the Artificer and the owner can see
141
+ * what was stripped (spec §7.2: redactionNotes is part of the pack contract).
142
+ */
143
+ function looksLikeAbsolutePath(value) {
144
+ // POSIX absolute path or Windows drive path
145
+ return value.startsWith('/') || /^[A-Za-z]:[\\/]/.test(value);
146
+ }
147
+ const SECRET_PATTERNS = [
148
+ /AKIA[0-9A-Z]{16}/, // AWS access key ID
149
+ /[A-Za-z0-9+/]{40,}/, // long base64-ish token (≥40 chars, no spaces)
150
+ ];
151
+ function looksLikeSecret(value) {
152
+ if (value.length < 20)
153
+ return false; // too short to be a real secret
154
+ for (const pattern of SECRET_PATTERNS) {
155
+ if (pattern.test(value))
156
+ return true;
157
+ }
158
+ return false;
159
+ }
160
+ function redactParams(params, projectDir) {
161
+ const notes = [];
162
+ const redacted = {};
163
+ for (const key of Object.keys(params)) {
164
+ if (PROTO_KEYS.has(key))
165
+ continue; // defense in depth (ERR-076)
166
+ const value = params[key];
167
+ if (typeof value !== 'string') {
168
+ redacted[key] = value;
169
+ continue;
170
+ }
171
+ // Path-like values
172
+ if (looksLikeAbsolutePath(value) && !value.startsWith(projectDir)) {
173
+ redacted[key] = '<redacted:absolute-path>';
174
+ notes.push(`redacted absolute path in field "${key}" (outside project tree)`);
175
+ continue;
176
+ }
177
+ // Secret-like values (AWS keys, long base64 tokens, etc.)
178
+ if (looksLikeSecret(value)) {
179
+ redacted[key] = '<redacted:secret>';
180
+ notes.push(`redacted possible secret in field "${key}"`);
181
+ continue;
182
+ }
183
+ redacted[key] = value;
184
+ }
185
+ return { redacted, notes };
186
+ }
187
+ export class BehaviorExamplePackAssembler {
188
+ db;
189
+ toolSemantics;
190
+ constructor(opts) {
191
+ this.db = opts.source;
192
+ this.toolSemantics = opts.toolSemantics;
193
+ }
194
+ /** Assemble only from explicit Owner labels; execution outcome is not a label. */
195
+ assemble(input) {
196
+ const pain = readTrajectoryStore(`pain lineage read for canonical_pain_id="${input.sourcePainId}"`, () => this.db.getPainEventByCanonicalId(input.sourcePainId));
197
+ if (!pain) {
198
+ // PRI-983: typed scope classification (structural marker — the readiness
199
+ // contract maps this to the stable `owner_behavior_examples_stale`
200
+ // code; FR-03: an unresolvable pain reference must never be silently
201
+ // reused). Message text unchanged (historical audit signature).
202
+ throw createOwnerEvidenceScopeError('pain_not_found', `[BehaviorExamplePackAssembler] pain event not found for canonical_pain_id="${input.sourcePainId}" (ERR-069 fail loud)`);
203
+ }
204
+ validateOwnerSelection(input);
205
+ const negativeRow = requireSelectedRow(readTrajectoryStore(`evidence row read for id=${input.sourceNegativeToolCallId}`, () => this.db.getRuleHostEvidenceRow(input.sourceNegativeToolCallId)), input.sourceNegativeToolCallId);
206
+ const positiveRows = input.positiveToolCallIds.map((id) => requireSelectedRow(readTrajectoryStore(`evidence row read for id=${id}`, () => this.db.getRuleHostEvidenceRow(id)), id));
207
+ for (const row of [negativeRow, ...positiveRows]) {
208
+ if (row.sessionId !== pain.sessionId) {
209
+ // PRI-983: cross pain/session selection → typed scope error → stable
210
+ // `owner_behavior_examples_scope_mismatch` at the readiness contract.
211
+ throw createOwnerEvidenceScopeError('session_mismatch', `[BehaviorExamplePackAssembler] selected tool call ${row.id} session mismatch; all examples must belong to pain session ${pain.sessionId}`);
212
+ }
213
+ }
214
+ const redactionNotes = [];
215
+ const buildCtx = { projectDir: input.projectDir, redactionNotes };
216
+ const sourceNegativeCase = this.buildSelectedCase(negativeRow, 'negative', buildCtx);
217
+ const positiveCounterexamples = positiveRows.map((row) => this.buildSelectedCase(row, 'positive', buildCtx));
218
+ const pack = {
219
+ sourceNegativeCase,
220
+ ownerDesiredOutcome: input.ownerDesiredOutcome,
221
+ positiveCounterexamples,
222
+ evidenceRefs: [`pain:${pain.id}`, `tool_call:${negativeRow.id}`, ...positiveRows.map((row) => `tool_call:${row.id}`)].slice(0, MAX_EVIDENCE_REFS),
223
+ redactionNotes,
224
+ };
225
+ // ── 9. Validate — fail loud (ERR-069) ──
226
+ const validation = validateBehaviorExamplePack(pack);
227
+ if (!validation.valid) {
228
+ throw new Error(`[BehaviorExamplePackAssembler] assembled pack failed validateBehaviorExamplePack: ${validation.errors.join('; ')} (ERR-069 fail loud)`);
229
+ }
230
+ return pack;
231
+ }
232
+ buildSelectedCase(row, kind, ctx) {
233
+ const { projectDir, redactionNotes } = ctx;
234
+ const base = buildCaseFromRow(row, kind, kind === 'negative' ? 'block' : 'allow');
235
+ const redaction = redactParams(base.params, projectDir);
236
+ redactionNotes.push(...redaction.notes);
237
+ const historyResult = readTrajectoryStore(`context history read for session=${row.sessionId} before id=${row.id}`, () => this.db.getRuleHostContextRowsBefore(row.sessionId, row.id, HISTORY_LIMIT));
238
+ const history = assembleBehaviorHistory(historyResult.rows, historyResult.truncated, { projectDir, toolSemantics: this.toolSemantics });
239
+ const action = buildRuleHostAction(row.toolName, redaction.redacted, projectDir, { canonicalKind: this.toolSemantics.resolve(row.toolName) });
240
+ const ruleContext = { version: 2, history, facts: computeBehaviorFacts(history, action.normalizedPath || null, null) };
241
+ return { ...base, params: redaction.redacted, ruleContext };
242
+ }
243
+ }
@@ -0,0 +1,6 @@
1
+ import type { RuleHistoryWindow, ToolSemanticRegistry } from '@principles/core/runtime-v2';
2
+ import type { RuleHostContextRow } from '@principles/core/trajectory-types';
3
+ export declare function assembleBehaviorHistory(rows: readonly RuleHostContextRow[], truncated: boolean, options: {
4
+ projectDir: string;
5
+ toolSemantics: ToolSemanticRegistry;
6
+ }): RuleHistoryWindow;
@@ -0,0 +1,76 @@
1
+ /** Shared validated history assembly; host vocabulary is supplied by its owner. */
2
+ import { extractFilePathFromParams, normalizePathPure } from '@principles/core/runtime-v2/internalization/rule-host-input-builder';
3
+ const VALID_OUTCOMES = new Set(['success', 'failure', 'blocked']);
4
+ function isPlainObject(value) {
5
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ }
7
+ function isRuleToolOutcome(value) {
8
+ return typeof value === 'string' && VALID_OUTCOMES.has(value);
9
+ }
10
+ function unavailable(reason) {
11
+ return {
12
+ status: 'unavailable',
13
+ unavailableReason: reason,
14
+ truncated: false,
15
+ calls: [],
16
+ };
17
+ }
18
+ export function assembleBehaviorHistory(rows, truncated, options) {
19
+ const { projectDir, toolSemantics } = options;
20
+ const records = [];
21
+ for (const row of rows) {
22
+ // ── validate tool_name (ERR-001: unknown) ──
23
+ const { toolName } = row;
24
+ if (typeof toolName !== 'string' || toolName.length === 0) {
25
+ return unavailable(`row id=${row.id}: tool_name must be a non-empty string`);
26
+ }
27
+ // ── validate outcome (ERR-001: unknown, enum check) ──
28
+ const { outcome } = row;
29
+ if (!isRuleToolOutcome(outcome)) {
30
+ return unavailable(`row id=${row.id}: outcome "${row.outcome}" is not a valid RuleToolOutcome`);
31
+ }
32
+ // ── validate & parse params_json (ERR-001: unknown) ──
33
+ const paramsJsonStr = row.paramsJson;
34
+ if (typeof paramsJsonStr !== 'string') {
35
+ return unavailable(`row id=${row.id}: params_json must be a string`);
36
+ }
37
+ let parsedParams;
38
+ try {
39
+ parsedParams = JSON.parse(paramsJsonStr);
40
+ }
41
+ catch {
42
+ return unavailable(`row id=${row.id}: params_json is not valid JSON`);
43
+ }
44
+ // Must be a non-array object (spec §5.2)
45
+ if (!isPlainObject(parsedParams)) {
46
+ return unavailable(`row id=${row.id}: params_json must parse to a non-array object`);
47
+ }
48
+ // ── build RuleToolCallRecord ──
49
+ // PRI-634-F: resolve through the supplied registry (baseline + host
50
+ // layer) instead of baseline-only canonicalizeToolKind — production tools
51
+ // like 'shell'/'cmd'/'delete_file' previously degraded to 'other' here
52
+ // while the gate classified them as bash/write (vocabulary drift).
53
+ const canonicalKind = toolSemantics.resolve(toolName);
54
+ // Extract normalized path using the same pure logic as buildRuleHostAction
55
+ // (avoids production/replay drift, spec §4.4)
56
+ const rawPath = extractFilePathFromParams(parsedParams, {
57
+ isBashTool: canonicalKind === 'execute',
58
+ isWriteTool: canonicalKind === 'write',
59
+ toolName,
60
+ });
61
+ const normalizedPath = normalizePathPure(rawPath, projectDir);
62
+ records.push({
63
+ sequenceId: row.id,
64
+ toolName,
65
+ canonicalKind,
66
+ normalizedPath: normalizedPath === '' ? null : normalizedPath,
67
+ paramsSummary: parsedParams,
68
+ outcome,
69
+ });
70
+ }
71
+ return {
72
+ status: 'available',
73
+ truncated,
74
+ calls: records,
75
+ };
76
+ }
@@ -85,3 +85,6 @@ export declare function createProductionHostRuntime(options?: Partial<Pick<HostR
85
85
  */
86
86
  events?: HostEventEmitter;
87
87
  }): HostRuntime;
88
+ export * from './behavior-example-pack-assembler.js';
89
+ export * from './behavior-history.js';
90
+ export * from './workspace-behavior-evidence.js';
@@ -265,3 +265,6 @@ export function createProductionHostRuntime(options = {}) {
265
265
  },
266
266
  });
267
267
  }
268
+ export * from './behavior-example-pack-assembler.js';
269
+ export * from './behavior-history.js';
270
+ export * from './workspace-behavior-evidence.js';
@@ -20,7 +20,7 @@
20
20
  * Hosts inject: a logger, a structured-event sink, their tool catalog, and an
21
21
  * env getter. Everything else is the existing production path.
22
22
  */
23
- import { type ToolSemanticRegistry } from '@principles/core/runtime-v2';
23
+ import { type ToolSemanticRegistry, type BehaviorEvidencePort } from '@principles/core/runtime-v2';
24
24
  import { type ConsumerGovernanceLogger } from './internalization-consumer-governance.js';
25
25
  export declare const INTERNALIZATION_AUTO_CONSUMER_FLAG_ID = "internalization_auto_consumer";
26
26
  /** Structural logger port — OpenClaw PluginLogger and worker loggers satisfy this. */
@@ -60,6 +60,17 @@ export interface InternalizationConsumerCyclePorts {
60
60
  * omitted → the artificer prompt is built without host context.
61
61
  */
62
62
  readonly hostKinds?: readonly string[];
63
+ /**
64
+ * PRI-982 (SPEC-PD-IAER-001 FR-02/FR-03): host-injected Owner evidence
65
+ * assembly for code_tool_hook artificer tasks. The production
66
+ * implementation (openclaw-plugin) wraps `BehaviorExamplePackAssembler`;
67
+ * hosts that cannot assemble evidence (Codex worker as of PRI-982) omit it.
68
+ * Absent port + a code-channel task degrades OBSERVABLY
69
+ * (evidence_source_unavailable) and the task STAYS PENDING — a host
70
+ * assembly gap must never read as "Owner has not labeled" (SPEC §6.2),
71
+ * and the runner is never constructed evidence-less.
72
+ */
73
+ readonly behaviorEvidence?: BehaviorEvidencePort;
63
74
  /** Log label used in human log prefixes ('AutoConsumer' | 'CodexWorker' | …). */
64
75
  readonly logLabel: string;
65
76
  readonly envGetter?: (name: string) => string | undefined;
@@ -23,7 +23,7 @@
23
23
  import { createRuntimeStateHandle, InternalizationOrchestrator, DreamerRunner, PhilosopherRunner, ScribeRunner, ArtificerRunner, buildArtificerHostSemanticContext, EvaluatorRunner, RolloutReviewerRunner, DefaultDreamerValidator, DefaultPhilosopherValidator, DefaultScribeValidator, DefaultArtificerValidator, DefaultEvaluatorValidator, DefaultRolloutReviewerValidator, PiAiRuntimeAdapter, L2AgentLoopAdapter, ArtificerL2Adapter, buildL2PrincipleReaderFromLedger, OpenClawCliRuntimeAdapter, storeEmitter, createProductionGateDeps, resolveRuntimeConfigFromPdConfig, resolveRuntimeConfigForAgent, AGENT_NAME_FOR_TASK_KIND, isRuntimeConfigError, computeConsumerDecision, FULL_CHAIN_CONSUMER_RUNNER_KINDS, DEFAULT_CONSUMER_RUNNER_KINDS, InternalizationQueueReadModel, MVP_CORE_TASK_KINDS, SqliteConnection, SqliteReconciliationCursorStore, SUCCEEDED_TRANSITIONS_SCOPE, PrincipleTreeLedgerAdapter,
24
24
  // PRI-714: resolve outputLanguage from the same effective config the
25
25
  // runners already receive (EP-07: canonical resolved value, not raw input).
26
- resolveOutputLanguage, } from '@principles/core/runtime-v2';
26
+ resolveOutputLanguage, hydratePITaskRecord, preflightArtificerOwnerEvidence, } from '@principles/core/runtime-v2';
27
27
  import { loadLedger } from '@principles/core/principle-tree-ledger';
28
28
  import { loadPdConfigForPlugin, loadFeatureFlagFromConfig } from './pd-config.js';
29
29
  import { createEvaluatorRepairDeps, createRolloutGovernanceDeps } from './internalization-consumer-governance.js';
@@ -296,6 +296,22 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
296
296
  // without touching leases, idempotency, dependency order, or the
297
297
  // single-task-per-cycle semantics. Ties keep the original priority order.
298
298
  const orderedRunnerKinds = orderKindsByOldestReadyStage(decision.runnerKinds, snapshot.readyTasks);
299
+ // PRI-634 A3: workspace-scoped telemetry sink. Constructed here (per-wake,
300
+ // workspaceDir in scope) so events from THIS workspace's runners are
301
+ // attributable to THIS workspace — a global subscriber on the storeEmitter
302
+ // singleton cannot (multi-workspace isolation, see
303
+ // workspace-telemetry-emitter.ts). Persist failures degrade through the
304
+ // host's structured event port, never break the runner.
305
+ // PRI-795 review P1: shared by the EvaluatorRunner AND the ArtificerL2
306
+ // adapter so `artificer_l2_complete` evidence (abortOwner/budget/elapsed/
307
+ // stopReason/tokenUsage) persists durably instead of vanishing with the
308
+ // process (the EP002-R3 "nothing recoverable after a failure" class).
309
+ const workspaceEmitter = new WorkspaceTelemetryEmitter(storeEmitter, workspaceDir, (detail) => {
310
+ emitEvent('WORKSPACE_TELEMETRY_PERSIST_FAILED', detail);
311
+ });
312
+ let artificerEvidence = null;
313
+ let authoritativeChannel = null;
314
+ let preflightSkipReason;
299
315
  for (const kind of orderedRunnerKinds) {
300
316
  const agentName = AGENT_NAME_FOR_TASK_KIND[kind];
301
317
  if (agentName === undefined) {
@@ -327,6 +343,109 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
327
343
  }
328
344
  const candidate = await orchestrator.wakeOnce(kind);
329
345
  if (candidate.decision === 'would_lease') {
346
+ // PRI-982 (SPEC-PD-IAER-001 §4.2 / FR-02): authoritative channel routing
347
+ // for artificer tasks. The channel is written deterministically by the
348
+ // intake bridge at seed time (pi_metadata.channel — the SINGLE authority,
349
+ // inherited along the chain) and is read from the leased task's hydrated
350
+ // metadata here; the cycle NEVER guesses it from task names or model text.
351
+ // - code_tool_hook → RuleCode sub-chain: evidence preflight FIRST, then
352
+ // L2 (flag permitting) with the assembled pack injected;
353
+ // - prompt → the legal non-L2 pipeline (PiAi), evidence-exempt
354
+ // (production: 40 successes on that pipeline);
355
+ // - unresolvable → refuse to guess: observable skip, no run (T08).
356
+ const leasedTaskForChannel = await stateManager.getTask(candidate.taskId);
357
+ const leasedPiTask = leasedTaskForChannel ? hydratePITaskRecord(leasedTaskForChannel) : null;
358
+ authoritativeChannel = leasedPiTask?.channel ?? null;
359
+ if (candidate.taskKind === 'artificer') {
360
+ if (authoritativeChannel === null || leasedPiTask === null) {
361
+ // Defensive: wakeOnce never leases non-hydratable tasks, so reaching
362
+ // here means the metadata corrupted between wake and read. Fail closed
363
+ // — never route an artificer task without an authoritative channel.
364
+ const channelInfo = JSON.stringify({
365
+ reason: 'artificer_channel_unresolvable',
366
+ taskId: candidate.taskId,
367
+ nextAction: 'Inspect and repair the task metadata (pi_metadata.channel); the consumer refuses to route an artificer task without an authoritative channel (SPEC-PD-IAER-001 §4.2, T08).',
368
+ });
369
+ emitEvent('INTERNALIZATION_CONSUMER_SKIP', channelInfo);
370
+ logger.warn(`[PD:${logLabel}] Artificer task ${candidate.taskId} skipped: authoritative channel unresolvable.`);
371
+ preflightSkipReason = 'artificer_channel_unresolvable';
372
+ continue;
373
+ }
374
+ if (authoritativeChannel === 'code_tool_hook') {
375
+ // Capability OFF must block generation, not merely select L1.
376
+ if (!loadFeatureFlagFromConfig(workspaceDir, 'code_rule_capability').enabled) {
377
+ emitEvent('INTERNALIZATION_CONSUMER_SKIP', JSON.stringify({
378
+ reason: 'code_rule_capability_disabled',
379
+ taskId: candidate.taskId,
380
+ taskKind: candidate.taskKind,
381
+ nextAction: 'Owner must enable code_rule_capability before Code Rule generation.',
382
+ }));
383
+ logger.warn('[PD:' + logLabel + '] Code Rule task ' + candidate.taskId + ' paused: capability disabled.');
384
+ preflightSkipReason = 'code_rule_capability_disabled';
385
+ continue;
386
+ }
387
+ artificerEvidence = await preflightArtificerOwnerEvidence({
388
+ taskId: candidate.taskId,
389
+ piTask: leasedPiTask,
390
+ owner,
391
+ workspaceDir, // FR-08 run-correlation telemetry (PRI-984)
392
+ stateManager,
393
+ eventEmitter: workspaceEmitter,
394
+ behaviorEvidence: ports.behaviorEvidence,
395
+ });
396
+ if (artificerEvidence.decision === 'evidence_source_unavailable') {
397
+ // SPEC §6.2: a host assembly gap is NOT "Owner has not labeled".
398
+ // The task stays pending (re-observable next cycle, zero model
399
+ // cost); the runner is never constructed evidence-less.
400
+ const gapInfo = JSON.stringify({
401
+ reason: 'evidence_source_unavailable',
402
+ taskId: candidate.taskId,
403
+ taskKind: candidate.taskKind,
404
+ nextAction: 'Host must wire the behaviorEvidence port (BehaviorExamplePackAssembler) into the consumer cycle ports; the task stays pending until then.',
405
+ });
406
+ emitEvent('INTERNALIZATION_CONSUMER_SKIP', gapInfo);
407
+ logger.warn(`[PD:${logLabel}] Artificer task ${candidate.taskId} skipped: evidence source unavailable (host must wire the behaviorEvidence port).`);
408
+ preflightSkipReason = 'evidence_source_unavailable';
409
+ continue;
410
+ }
411
+ if (artificerEvidence.decision === 'evidence_source_error') {
412
+ // PRI-980 review P2-1 — SPEC §6.2 case 3: a trajectory-store I/O
413
+ // fault is preserved AS a storage fault. Same family as the absent
414
+ // port: the task stays wake-eligible (the next cycle retries the
415
+ // assembly at zero model cost), the runner is never constructed,
416
+ // and the preflight already emitted the structured degradation
417
+ // telemetry (reason=evidence_source_error, reviewPersisted=false).
418
+ const storageInfo = JSON.stringify({
419
+ reason: 'evidence_source_error',
420
+ taskId: candidate.taskId,
421
+ taskKind: candidate.taskKind,
422
+ nextAction: artificerEvidence.nextAction,
423
+ ...(artificerEvidence.detail !== undefined ? { detail: artificerEvidence.detail } : {}),
424
+ });
425
+ emitEvent('INTERNALIZATION_CONSUMER_SKIP', storageInfo);
426
+ logger.warn(`[PD:${logLabel}] Artificer task ${candidate.taskId} skipped: evidence source error (storage fault preserved; task stays wake-eligible).`);
427
+ preflightSkipReason = 'evidence_source_error';
428
+ continue;
429
+ }
430
+ if (artificerEvidence.decision === 'blocked') {
431
+ // FR-04: the preflight already moved the task atomically into
432
+ // needs_human_review — outside the wake scan (pending/retry_wait
433
+ // only), so it is never auto-rewoken (FR-05/T02). reviewPersisted
434
+ // = false means a concurrent writer won the CAS; still no run.
435
+ emitEvent('INTERNALIZATION_CONSUMER_EVIDENCE_BLOCKED', JSON.stringify({
436
+ taskId: candidate.taskId,
437
+ reasonCode: artificerEvidence.reasonCode,
438
+ nextAction: artificerEvidence.nextAction,
439
+ ...(artificerEvidence.missingFields !== undefined ? { missingFields: [...artificerEvidence.missingFields] } : {}),
440
+ ...(artificerEvidence.detail !== undefined ? { detail: artificerEvidence.detail } : {}),
441
+ reviewPersisted: artificerEvidence.reviewPersisted,
442
+ }));
443
+ logger.warn(`[PD:${logLabel}] Artificer task ${candidate.taskId} blocked on Owner evidence (${artificerEvidence.reasonCode}, reviewPersisted=${artificerEvidence.reviewPersisted}).`);
444
+ preflightSkipReason = 'artificer_blocked_on_owner_evidence';
445
+ continue;
446
+ }
447
+ }
448
+ }
330
449
  wakeResult = candidate;
331
450
  taskRuntimeConfig = kindRuntime;
332
451
  break;
@@ -337,6 +456,10 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
337
456
  lastSkipReason = candidate.decision === 'no_ready_tasks' ? candidate.reason : undefined;
338
457
  }
339
458
  if (!wakeResult || taskRuntimeConfig === null) {
459
+ if (preflightSkipReason !== undefined) {
460
+ lastSkipDecision = preflightSkipReason;
461
+ lastSkipReason = undefined;
462
+ }
340
463
  // A: 预算由周期 finally 统一执行 (每周期恰一次,含 backlog 场景)
341
464
  const skipPayload = { decision: lastSkipDecision };
342
465
  if (lastSkipReason) {
@@ -349,19 +472,6 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
349
472
  // PRI-719: everything downstream reads the LEASED TASK's resolved config —
350
473
  // the kind-specific profile, not the shared gate resolution.
351
474
  const taskRuntimeKind = taskRuntimeConfig.runtimeKind;
352
- // PRI-634 A3: workspace-scoped telemetry sink. Constructed here (per-wake,
353
- // workspaceDir in scope) so events from THIS workspace's runners are
354
- // attributable to THIS workspace — a global subscriber on the storeEmitter
355
- // singleton cannot (multi-workspace isolation, see
356
- // workspace-telemetry-emitter.ts). Persist failures degrade through the
357
- // host's structured event port, never break the runner.
358
- // PRI-795 review P1: shared by the EvaluatorRunner AND the ArtificerL2
359
- // adapter so `artificer_l2_complete` evidence (abortOwner/budget/elapsed/
360
- // stopReason/tokenUsage) persists durably instead of vanishing with the
361
- // process (the EP002-R3 "nothing recoverable after a failure" class).
362
- const workspaceEmitter = new WorkspaceTelemetryEmitter(storeEmitter, workspaceDir, (detail) => {
363
- emitEvent('WORKSPACE_TELEMETRY_PERSIST_FAILED', detail);
364
- });
365
475
  let adapter;
366
476
  if (taskRuntimeKind === 'pi-ai') {
367
477
  // PRI-419: when l2_dreamer flag is on AND this is a dreamer task, route
@@ -372,9 +482,15 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
372
482
  // so the model can self-iterate before submitting. Honors the
373
483
  // code_rule_capability emergency-disable flag (PR #1661 CodeRabbit P1).
374
484
  const codeRuleFlag = loadFeatureFlagFromConfig(workspaceDir, 'code_rule_capability');
485
+ // PRI-982 (SPEC-PD-IAER-001 §4.2): the authoritative TARGET CHANNEL is
486
+ // part of the L2 selection — `pi_metadata.channel === 'code_tool_hook'`
487
+ // (the seed-time bridge authority). prompt-channel artificer tasks are
488
+ // misroutes on L2 (production: 54 failures) and keep the legal non-L2
489
+ // PiAi pipeline below; unresolvable channels never enter L2.
375
490
  if (wakeResult.taskKind === 'artificer'
376
491
  && taskRuntimeKind === 'pi-ai'
377
- && codeRuleFlag.enabled) {
492
+ && codeRuleFlag.enabled
493
+ && authoritativeChannel === 'code_tool_hook') {
378
494
  adapter = new ArtificerL2Adapter({
379
495
  // Profile readiness check guarantees these are non-undefined for
380
496
  // pi-ai runtime kind (validatePdLocalProfile enforces provider/model/apiKeyEnv).
@@ -518,7 +634,16 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
518
634
  }, runnerOptions);
519
635
  break;
520
636
  case 'artificer':
521
- runner = new ArtificerRunner({ stateManager, runtimeAdapter: adapter, eventEmitter: storeEmitter, artifactStore: stateManager.piArtifactStore, validator: new DefaultArtificerValidator() },
637
+ runner = new ArtificerRunner({
638
+ stateManager, runtimeAdapter: adapter, eventEmitter: storeEmitter, artifactStore: stateManager.piArtifactStore, validator: new DefaultArtificerValidator(),
639
+ // PRI-982 (SPEC-PD-IAER-001 FR-02): the preflight-assembled Owner
640
+ // evidence pack — present only when the authoritative channel is
641
+ // code_tool_hook AND the readiness gate returned ready (same
642
+ // ArtificerRunnerDeps shape as the run-rulehost CLI pipeline).
643
+ ...(artificerEvidence !== null && artificerEvidence.decision === 'ready'
644
+ ? { behaviorExamplePack: artificerEvidence.pack }
645
+ : {}),
646
+ },
522
647
  // PRI-741: thread the host semantic projection into generation.
523
648
  { ...runnerOptions, ...(artificerHostSemanticContext !== undefined ? { hostSemanticContext: artificerHostSemanticContext } : {}) });
524
649
  break;
@@ -0,0 +1,3 @@
1
+ import type { BehaviorEvidencePort, ToolSemanticRegistry } from '@principles/core/runtime-v2';
2
+ /** Lazy storage access: missing labels need no DB, and a failed open remains observable. */
3
+ export declare function createWorkspaceBehaviorEvidencePort(workspaceDir: string, toolSemantics?: ToolSemanticRegistry): BehaviorEvidencePort;