@alan512/experienceengine 0.2.1 → 0.3.1

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 (186) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.mcp.json +5 -6
  3. package/README.md +24 -10
  4. package/README.zh-CN.md +22 -8
  5. package/dist/adapters/codex/action-registry.d.ts +23 -1
  6. package/dist/adapters/codex/action-registry.js +73 -0
  7. package/dist/adapters/codex/action-registry.js.map +1 -1
  8. package/dist/adapters/codex/behavior-loop.d.ts +80 -0
  9. package/dist/adapters/codex/behavior-loop.js +189 -0
  10. package/dist/adapters/codex/behavior-loop.js.map +1 -0
  11. package/dist/adapters/codex/mcp-server.d.ts +27 -72
  12. package/dist/adapters/codex/mcp-server.js +36 -160
  13. package/dist/adapters/codex/mcp-server.js.map +1 -1
  14. package/dist/cli/commands/claude-hook.d.ts +4 -0
  15. package/dist/cli/commands/claude-hook.js +105 -21
  16. package/dist/cli/commands/claude-hook.js.map +1 -1
  17. package/dist/cli/commands/codex-hook.d.ts +22 -0
  18. package/dist/cli/commands/codex-hook.js +298 -0
  19. package/dist/cli/commands/codex-hook.js.map +1 -0
  20. package/dist/cli/commands/config.js +9 -1
  21. package/dist/cli/commands/config.js.map +1 -1
  22. package/dist/cli/commands/doctor.js +63 -1
  23. package/dist/cli/commands/doctor.js.map +1 -1
  24. package/dist/cli/commands/inspect.d.ts +1 -1
  25. package/dist/cli/commands/inspect.js +325 -1
  26. package/dist/cli/commands/inspect.js.map +1 -1
  27. package/dist/cli/commands/install.js +4 -1
  28. package/dist/cli/commands/install.js.map +1 -1
  29. package/dist/cli/commands/maintenance.js +4 -0
  30. package/dist/cli/commands/maintenance.js.map +1 -1
  31. package/dist/cli/commands/repair.js +35 -3
  32. package/dist/cli/commands/repair.js.map +1 -1
  33. package/dist/cli/commands/status.js +27 -0
  34. package/dist/cli/commands/status.js.map +1 -1
  35. package/dist/cli/commands/upgrade.js +2 -0
  36. package/dist/cli/commands/upgrade.js.map +1 -1
  37. package/dist/cli/dispatch.js +8 -3
  38. package/dist/cli/dispatch.js.map +1 -1
  39. package/dist/config/config-schema.d.ts +8 -0
  40. package/dist/config/config-schema.js +6 -0
  41. package/dist/config/config-schema.js.map +1 -1
  42. package/dist/config/default-config.js +1 -0
  43. package/dist/config/default-config.js.map +1 -1
  44. package/dist/config/load-config.js +3 -0
  45. package/dist/config/load-config.js.map +1 -1
  46. package/dist/controller/candidate-retriever.d.ts +4 -1
  47. package/dist/controller/candidate-retriever.js +129 -13
  48. package/dist/controller/candidate-retriever.js.map +1 -1
  49. package/dist/controller/injection-renderer.d.ts +2 -2
  50. package/dist/controller/injection-renderer.js +22 -3
  51. package/dist/controller/injection-renderer.js.map +1 -1
  52. package/dist/controller/injection-scorecard.js +3 -0
  53. package/dist/controller/injection-scorecard.js.map +1 -1
  54. package/dist/controller/intervention-controller.d.ts +2 -2
  55. package/dist/controller/intervention-controller.js +183 -25
  56. package/dist/controller/intervention-controller.js.map +1 -1
  57. package/dist/controller/model-reranker-mode.d.ts +4 -0
  58. package/dist/controller/model-reranker-mode.js +14 -0
  59. package/dist/controller/model-reranker-mode.js.map +1 -0
  60. package/dist/controller/model-reranker.d.ts +0 -1
  61. package/dist/controller/model-reranker.js +1 -13
  62. package/dist/controller/model-reranker.js.map +1 -1
  63. package/dist/controller/policy-enricher.d.ts +2 -1
  64. package/dist/controller/policy-enricher.js +71 -11
  65. package/dist/controller/policy-enricher.js.map +1 -1
  66. package/dist/evaluation/openclaw-scenarios.js +12 -5
  67. package/dist/evaluation/openclaw-scenarios.js.map +1 -1
  68. package/dist/experience-management/repo-policy.d.ts +53 -0
  69. package/dist/experience-management/repo-policy.js +175 -0
  70. package/dist/experience-management/repo-policy.js.map +1 -0
  71. package/dist/hybrid/capsule-builder.js +2 -0
  72. package/dist/hybrid/capsule-builder.js.map +1 -1
  73. package/dist/input/scope-resolver.js +16 -4
  74. package/dist/input/scope-resolver.js.map +1 -1
  75. package/dist/install/claude-cli.js +38 -21
  76. package/dist/install/claude-cli.js.map +1 -1
  77. package/dist/install/claude-code-doctor.js +8 -3
  78. package/dist/install/claude-code-doctor.js.map +1 -1
  79. package/dist/install/claude-code-installer.js +5 -2
  80. package/dist/install/claude-code-installer.js.map +1 -1
  81. package/dist/install/claude-runtime-target.d.ts +5 -0
  82. package/dist/install/claude-runtime-target.js +32 -2
  83. package/dist/install/claude-runtime-target.js.map +1 -1
  84. package/dist/install/codex-cli.d.ts +7 -0
  85. package/dist/install/codex-cli.js +43 -20
  86. package/dist/install/codex-cli.js.map +1 -1
  87. package/dist/install/codex-hooks.d.ts +42 -0
  88. package/dist/install/codex-hooks.js +280 -0
  89. package/dist/install/codex-hooks.js.map +1 -0
  90. package/dist/install/codex-installer.d.ts +21 -3
  91. package/dist/install/codex-installer.js +98 -9
  92. package/dist/install/codex-installer.js.map +1 -1
  93. package/dist/install/codex-runtime-target.d.ts +20 -0
  94. package/dist/install/codex-runtime-target.js +85 -17
  95. package/dist/install/codex-runtime-target.js.map +1 -1
  96. package/dist/install/openclaw-cli.d.ts +1 -0
  97. package/dist/install/openclaw-cli.js +52 -3
  98. package/dist/install/openclaw-cli.js.map +1 -1
  99. package/dist/install/openclaw-installer.d.ts +5 -0
  100. package/dist/install/openclaw-installer.js +24 -5
  101. package/dist/install/openclaw-installer.js.map +1 -1
  102. package/dist/install/public-install.d.ts +1 -0
  103. package/dist/install/public-install.js +5 -0
  104. package/dist/install/public-install.js.map +1 -1
  105. package/dist/interaction/repo-summary.d.ts +17 -0
  106. package/dist/interaction/repo-summary.js +33 -15
  107. package/dist/interaction/repo-summary.js.map +1 -1
  108. package/dist/interaction/retrieval-policy-inspection.d.ts +19 -0
  109. package/dist/interaction/retrieval-policy-inspection.js +33 -0
  110. package/dist/interaction/retrieval-policy-inspection.js.map +1 -0
  111. package/dist/interaction/service.d.ts +24 -1
  112. package/dist/interaction/service.js +171 -12
  113. package/dist/interaction/service.js.map +1 -1
  114. package/dist/maintenance/claude-validate-print.d.ts +8 -1
  115. package/dist/maintenance/claude-validate-print.js +52 -2
  116. package/dist/maintenance/claude-validate-print.js.map +1 -1
  117. package/dist/maintenance/experience-export-drafts.d.ts +56 -0
  118. package/dist/maintenance/experience-export-drafts.js +217 -0
  119. package/dist/maintenance/experience-export-drafts.js.map +1 -0
  120. package/dist/maintenance/experience-hygiene.d.ts +38 -0
  121. package/dist/maintenance/experience-hygiene.js +266 -0
  122. package/dist/maintenance/experience-hygiene.js.map +1 -0
  123. package/dist/maintenance/operator-review-flow.d.ts +81 -0
  124. package/dist/maintenance/operator-review-flow.js +172 -0
  125. package/dist/maintenance/operator-review-flow.js.map +1 -0
  126. package/dist/plugin/openclaw-plugin.d.ts +5 -0
  127. package/dist/plugin/runtime-helpers.js +43 -1
  128. package/dist/plugin/runtime-helpers.js.map +1 -1
  129. package/dist/runtime/prompt-service.d.ts +51 -0
  130. package/dist/runtime/prompt-service.js +209 -0
  131. package/dist/runtime/prompt-service.js.map +1 -0
  132. package/dist/runtime/service.d.ts +7 -2
  133. package/dist/runtime/service.js +176 -25
  134. package/dist/runtime/service.js.map +1 -1
  135. package/dist/store/sqlite/db.js +15 -0
  136. package/dist/store/sqlite/db.js.map +1 -1
  137. package/dist/store/sqlite/repositories/attribution-record-repo.d.ts +15 -0
  138. package/dist/store/sqlite/repositories/attribution-record-repo.js +111 -0
  139. package/dist/store/sqlite/repositories/attribution-record-repo.js.map +1 -0
  140. package/dist/store/sqlite/repositories/episode-repo.d.ts +14 -0
  141. package/dist/store/sqlite/repositories/episode-repo.js +84 -0
  142. package/dist/store/sqlite/repositories/episode-repo.js.map +1 -0
  143. package/dist/store/sqlite/repositories/injection-repo.d.ts +2 -0
  144. package/dist/store/sqlite/repositories/injection-repo.js +32 -5
  145. package/dist/store/sqlite/repositories/injection-repo.js.map +1 -1
  146. package/dist/store/sqlite/repositories/input-record-repo.d.ts +1 -0
  147. package/dist/store/sqlite/repositories/input-record-repo.js +23 -10
  148. package/dist/store/sqlite/repositories/input-record-repo.js.map +1 -1
  149. package/dist/store/sqlite/repositories/node-repo.d.ts +1 -0
  150. package/dist/store/sqlite/repositories/node-repo.js +10 -0
  151. package/dist/store/sqlite/repositories/node-repo.js.map +1 -1
  152. package/dist/store/sqlite/repositories/outcome-record-repo.d.ts +1 -0
  153. package/dist/store/sqlite/repositories/outcome-record-repo.js +11 -2
  154. package/dist/store/sqlite/repositories/outcome-record-repo.js.map +1 -1
  155. package/dist/store/sqlite/repositories/repo-policy-repo.d.ts +11 -0
  156. package/dist/store/sqlite/repositories/repo-policy-repo.js +87 -0
  157. package/dist/store/sqlite/repositories/repo-policy-repo.js.map +1 -0
  158. package/dist/store/sqlite/repositories/review-event-repo.d.ts +1 -0
  159. package/dist/store/sqlite/repositories/review-event-repo.js +11 -2
  160. package/dist/store/sqlite/repositories/review-event-repo.js.map +1 -1
  161. package/dist/store/sqlite/repositories/task-run-repo.d.ts +1 -0
  162. package/dist/store/sqlite/repositories/task-run-repo.js +11 -2
  163. package/dist/store/sqlite/repositories/task-run-repo.js.map +1 -1
  164. package/dist/store/sqlite/schema.sql +43 -0
  165. package/dist/store/vector/api-embedding-provider.js +14 -3
  166. package/dist/store/vector/api-embedding-provider.js.map +1 -1
  167. package/dist/store/vector/embeddings.js +5 -51
  168. package/dist/store/vector/embeddings.js.map +1 -1
  169. package/dist/store/vector/local-provider.js +14 -3
  170. package/dist/store/vector/local-provider.js.map +1 -1
  171. package/dist/types/domain.d.ts +89 -1
  172. package/dist/utils/text.js +5 -1
  173. package/dist/utils/text.js.map +1 -1
  174. package/docs/assets/readme/inspect-last-example.svg +37 -0
  175. package/docs/releases/v0.2.1.md +9 -0
  176. package/docs/releases/v0.3.0.md +20 -0
  177. package/docs/releases/v0.3.1.md +23 -0
  178. package/docs/user-guide.md +92 -7
  179. package/openclaw.plugin.json +1 -1
  180. package/package.json +3 -3
  181. package/plugins/claude-code-experienceengine/.claude-plugin/plugin.json +1 -1
  182. package/plugins/claude-code-experienceengine/.mcp.json +3 -9
  183. package/plugins/claude-code-experienceengine/scripts/install-deps.sh +1 -1
  184. package/plugins/claude-code-experienceengine/scripts/mcp-server.sh +43 -0
  185. package/scripts/claude-plugin/claude-hook.sh +6 -1
  186. package/scripts/claude-plugin/mcp-server.sh +45 -0
@@ -10,19 +10,37 @@ const summarizeRecommendation = (benchmark) => {
10
10
  return "Review the current repo summary and recent interventions before deciding whether to stay live or move back to shadow.";
11
11
  }
12
12
  };
13
- export const buildRepoSummary = (input) => ({
14
- scope: input.scope,
15
- recent: {
16
- latestSessionId: input.latest?.sessionId,
17
- latestTaskSummary: input.latest?.summary,
18
- latestActivityAt: input.latest?.createdAt,
19
- latestIntervention: input.latest?.intervention,
20
- latestAutoFeedback: input.latest?.autoFeedback,
21
- latestAutoFeedbackReason: input.latest?.autoFeedbackReason,
22
- latestDecisionExplanation: input.latest?.decisionExplanation,
23
- latestTrustSummary: input.latest?.trustSummary
24
- },
25
- benchmark: input.learning.benchmark,
26
- recommendedNextAction: summarizeRecommendation(input.learning.benchmark)
27
- });
13
+ export const buildRepoSummary = (input) => {
14
+ const policy = input.policyInspection?.policy ?? input.policy;
15
+ return {
16
+ scope: input.scope,
17
+ recent: {
18
+ latestSessionId: input.latest?.sessionId,
19
+ latestTaskSummary: input.latest?.summary,
20
+ latestActivityAt: input.latest?.createdAt,
21
+ latestIntervention: input.latest?.intervention,
22
+ latestAutoFeedback: input.latest?.autoFeedback,
23
+ latestAutoFeedbackReason: input.latest?.autoFeedbackReason,
24
+ latestDecisionExplanation: input.latest?.decisionExplanation,
25
+ latestTrustSummary: input.latest?.trustSummary
26
+ },
27
+ benchmark: input.learning.benchmark,
28
+ policy: policy
29
+ ? {
30
+ configuredMode: policy.configured_mode,
31
+ effectiveMode: policy.effective_mode,
32
+ circuitState: policy.circuit_state,
33
+ circuitReason: policy.circuit_reason,
34
+ liveDiagnosticsDisabled: policy.live_diagnostics_disabled,
35
+ updatedAt: policy.updated_at,
36
+ lastTrippedAt: policy.last_tripped_at,
37
+ restoredAt: policy.restored_at,
38
+ evidenceSummary: input.policyInspection?.evidenceSummary,
39
+ evidence: input.policyInspection?.evidence,
40
+ restoreGuidance: input.policyInspection?.restoreGuidance
41
+ }
42
+ : undefined,
43
+ recommendedNextAction: summarizeRecommendation(input.learning.benchmark)
44
+ };
45
+ };
28
46
  //# sourceMappingURL=repo-summary.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"repo-summary.js","sourceRoot":"","sources":["../../src/interaction/repo-summary.ts"],"names":[],"mappings":"AAuBA,MAAM,uBAAuB,GAAG,CAAC,SAA2B,EAAU,EAAE;IACtE,QAAQ,SAAS,CAAC,OAAO,EAAE,CAAC;QAC1B,KAAK,SAAS;YACZ,OAAO,gFAAgF,CAAC;QAC1F,KAAK,YAAY;YACf,OAAO,yGAAyG,CAAC;QACnH,KAAK,SAAS;YACZ,OAAO,sGAAsG,CAAC;QAChH;YACE,OAAO,uHAAuH,CAAC;IACnI,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAQhC,EAAyB,EAAE,CAAC,CAAC;IAC5B,KAAK,EAAE,KAAK,CAAC,KAAK;IAClB,MAAM,EAAE;QACN,eAAe,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS;QACxC,iBAAiB,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO;QACxC,gBAAgB,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS;QACzC,kBAAkB,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY;QAC9C,kBAAkB,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY;QAC9C,wBAAwB,EAAE,KAAK,CAAC,MAAM,EAAE,kBAAkB;QAC1D,yBAAyB,EAAE,KAAK,CAAC,MAAM,EAAE,mBAAmB;QAC5D,kBAAkB,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY;KAC/C;IACD,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,SAAS;IACnC,qBAAqB,EAAE,uBAAuB,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;CACzE,CAAC,CAAC"}
1
+ {"version":3,"file":"repo-summary.js","sourceRoot":"","sources":["../../src/interaction/repo-summary.ts"],"names":[],"mappings":"AAsCA,MAAM,uBAAuB,GAAG,CAAC,SAA2B,EAAU,EAAE;IACtE,QAAQ,SAAS,CAAC,OAAO,EAAE,CAAC;QAC1B,KAAK,SAAS;YACZ,OAAO,gFAAgF,CAAC;QAC1F,KAAK,YAAY;YACf,OAAO,yGAAyG,CAAC;QACnH,KAAK,SAAS;YACZ,OAAO,sGAAsG,CAAC;QAChH;YACE,OAAO,uHAAuH,CAAC;IACnI,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAUhC,EAAyB,EAAE;IAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,gBAAgB,EAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC;IAE9D,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,MAAM,EAAE;YACN,eAAe,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS;YACxC,iBAAiB,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO;YACxC,gBAAgB,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS;YACzC,kBAAkB,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY;YAC9C,kBAAkB,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY;YAC9C,wBAAwB,EAAE,KAAK,CAAC,MAAM,EAAE,kBAAkB;YAC1D,yBAAyB,EAAE,KAAK,CAAC,MAAM,EAAE,mBAAmB;YAC5D,kBAAkB,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY;SAC/C;QACD,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,SAAS;QACnC,MAAM,EAAE,MAAM;YACZ,CAAC,CAAC;gBACE,cAAc,EAAE,MAAM,CAAC,eAAe;gBACtC,aAAa,EAAE,MAAM,CAAC,cAAc;gBACpC,YAAY,EAAE,MAAM,CAAC,aAAa;gBAClC,aAAa,EAAE,MAAM,CAAC,cAAc;gBACpC,uBAAuB,EAAE,MAAM,CAAC,yBAAyB;gBACzD,SAAS,EAAE,MAAM,CAAC,UAAU;gBAC5B,aAAa,EAAE,MAAM,CAAC,eAAe;gBACrC,UAAU,EAAE,MAAM,CAAC,WAAW;gBAC9B,eAAe,EAAE,KAAK,CAAC,gBAAgB,EAAE,eAAe;gBACxD,QAAQ,EAAE,KAAK,CAAC,gBAAgB,EAAE,QAAQ;gBAC1C,eAAe,EAAE,KAAK,CAAC,gBAAgB,EAAE,eAAe;aACzD;YACH,CAAC,CAAC,SAAS;QACb,qBAAqB,EAAE,uBAAuB,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;KACzE,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { InjectionScorecard, PolicyEnrichmentComponent, RetrievalPolicyStageDiagnostic } from "../types/domain.js";
2
+ export type RetrievalPolicyStageInspection = {
3
+ stage: RetrievalPolicyStageDiagnostic["stage"];
4
+ acceptedCount?: number;
5
+ rejectedCount?: number;
6
+ passedCount?: number;
7
+ reasonCodes: string[];
8
+ };
9
+ export type RetrievalPolicyComponentInspection = PolicyEnrichmentComponent;
10
+ export type RetrievalPolicyInspectionSummary = {
11
+ stages: RetrievalPolicyStageInspection[];
12
+ semanticMode?: "skipped" | "rerank" | "backfill";
13
+ topPolicyComponents: RetrievalPolicyComponentInspection[];
14
+ rejectedCandidates: Array<{
15
+ id: string;
16
+ reasonCodes: string[];
17
+ }>;
18
+ };
19
+ export declare const buildRetrievalPolicyInspectionSummary: (scorecard?: InjectionScorecard) => RetrievalPolicyInspectionSummary | undefined;
@@ -0,0 +1,33 @@
1
+ const SEMANTIC_STAGE = "semantic_rerank_backfill";
2
+ const inferSemanticMode = (stage) => {
3
+ const modeReason = stage?.reasonCodes.find((reason) => reason.startsWith("semantic_mode:"));
4
+ const mode = modeReason?.slice("semantic_mode:".length);
5
+ return mode === "skipped" || mode === "rerank" || mode === "backfill" ? mode : undefined;
6
+ };
7
+ export const buildRetrievalPolicyInspectionSummary = (scorecard) => {
8
+ const stages = scorecard?.retrievalPolicyDiagnostics?.stages ?? [];
9
+ const topCandidate = scorecard?.topCandidates?.[0];
10
+ const topPolicyComponents = [...(topCandidate?.policyComponents ?? [])]
11
+ .sort((left, right) => Math.abs(right.value) - Math.abs(left.value))
12
+ .slice(0, 5);
13
+ const rejectedCandidates = scorecard?.rejectedCandidates?.slice(0, 5).map((candidate) => ({
14
+ id: candidate.id,
15
+ reasonCodes: candidate.reasonCodes
16
+ })) ?? [];
17
+ if (!stages.length && !topPolicyComponents.length && !rejectedCandidates.length) {
18
+ return undefined;
19
+ }
20
+ return {
21
+ stages: stages.map((stage) => ({
22
+ stage: stage.stage,
23
+ acceptedCount: stage.acceptedCount,
24
+ rejectedCount: stage.rejectedCount,
25
+ passedCount: stage.passedCount,
26
+ reasonCodes: stage.reasonCodes
27
+ })),
28
+ semanticMode: inferSemanticMode(stages.find((stage) => stage.stage === SEMANTIC_STAGE)),
29
+ topPolicyComponents,
30
+ rejectedCandidates
31
+ };
32
+ };
33
+ //# sourceMappingURL=retrieval-policy-inspection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retrieval-policy-inspection.js","sourceRoot":"","sources":["../../src/interaction/retrieval-policy-inspection.ts"],"names":[],"mappings":"AA0BA,MAAM,cAAc,GAAG,0BAA0B,CAAC;AAElD,MAAM,iBAAiB,GAAG,CACxB,KAAiD,EACC,EAAE;IACpD,MAAM,UAAU,GAAG,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC5F,MAAM,IAAI,GAAG,UAAU,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3F,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qCAAqC,GAAG,CACnD,SAA8B,EACgB,EAAE;IAChD,MAAM,MAAM,GAAG,SAAS,EAAE,0BAA0B,EAAE,MAAM,IAAI,EAAE,CAAC;IACnE,MAAM,YAAY,GAAG,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,mBAAmB,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,gBAAgB,IAAI,EAAE,CAAC,CAAC;SACpE,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;SACnE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACf,MAAM,kBAAkB,GACtB,SAAS,EAAE,kBAAkB,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC7D,EAAE,EAAE,SAAS,CAAC,EAAE;QAChB,WAAW,EAAE,SAAS,CAAC,WAAW;KACnC,CAAC,CAAC,IAAI,EAAE,CAAC;IAEZ,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,mBAAmB,CAAC,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC;QAChF,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC7B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;SAC/B,CAAC,CAAC;QACH,YAAY,EAAE,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,cAAc,CAAC,CAAC;QACvF,mBAAmB;QACnB,kBAAkB;KACnB,CAAC;AACJ,CAAC,CAAC"}
@@ -1,8 +1,12 @@
1
1
  import type { ExperienceEngineConfig } from "../config/config-schema.js";
2
2
  import { type BenchmarkSummary } from "../evaluation/benchmark-summary.js";
3
+ import { type HygieneReviewFilters, type HygieneReviewReport } from "../maintenance/experience-hygiene.js";
4
+ import { type ExperienceExportDraftFilters, type ExperienceExportDraftReport } from "../maintenance/experience-export-drafts.js";
5
+ import { type OperatorReviewReport } from "../maintenance/operator-review-flow.js";
3
6
  import { type HybridRouteDecision } from "../hybrid/router.js";
4
- import type { CandidateLifecycleState, DistillationSource, DistillationJobState, EvaluationMode, ExperienceInputRecord, FeedbackAttributionReason, InjectionEvent, InjectionScorecard, ExperienceNode, ExperienceNodeType, ExperienceState, TaskRun } from "../types/domain.js";
7
+ import type { CandidateLifecycleState, DistillationSource, DistillationJobState, EvaluationMode, ExperienceInputRecord, FeedbackAttributionReason, AttributionRecord, EpisodeProjection, InjectionEvent, InjectionScorecard, ExperienceNode, ExperienceNodeType, ExperienceState, TaskRun } from "../types/domain.js";
5
8
  import { type ExperienceRepoSummary } from "./repo-summary.js";
9
+ import { type RetrievalPolicyInspectionSummary } from "./retrieval-policy-inspection.js";
6
10
  export type ExperienceNodeSummary = {
7
11
  id: string;
8
12
  type: ExperienceNode["node_type"];
@@ -53,6 +57,7 @@ export type ExperienceTimelineEntry = {
53
57
  };
54
58
  export type ExperienceLastInspection = {
55
59
  sessionId?: string;
60
+ episodeId?: string;
56
61
  scopeId: string;
57
62
  taskType: ExperienceInputRecord["task_type"];
58
63
  intervention: "inject" | "skip" | "shadow" | "holdout";
@@ -60,6 +65,8 @@ export type ExperienceLastInspection = {
60
65
  delivered?: boolean;
61
66
  autoFeedback: "helped" | "harmed" | "none";
62
67
  autoFeedbackReason?: InjectionEvent["attribution_reason"];
68
+ attributionRecords: AttributionRecord[];
69
+ episodeProjection?: EpisodeProjection;
63
70
  outcome: ExperienceInputRecord["outcome_signal"];
64
71
  injectedNodes: ExperienceNodeSummary[];
65
72
  hints: string[];
@@ -68,6 +75,7 @@ export type ExperienceLastInspection = {
68
75
  decisionExplanation?: string;
69
76
  trustSummary?: string;
70
77
  retrievalNotes: string[];
78
+ retrievalPolicySummary?: RetrievalPolicyInspectionSummary;
71
79
  timeline: ExperienceTimelineEntry[];
72
80
  learningStatus?: TaskRun["learning_status"];
73
81
  learningReason?: string;
@@ -171,6 +179,9 @@ export declare class ExperienceInteractionService {
171
179
  private readonly hybridWorkerClient;
172
180
  private readonly inputRepo;
173
181
  private readonly injectionRepo;
182
+ private readonly attributionRecordRepo;
183
+ private readonly repoPolicyRepo;
184
+ private readonly episodeRepo;
174
185
  private readonly nodeRepo;
175
186
  private readonly candidateRepo;
176
187
  private readonly jobRepo;
@@ -197,8 +208,20 @@ export declare class ExperienceInteractionService {
197
208
  listNodesByState(state: ExperienceState): ExperienceNodeSummary[];
198
209
  listNodesByType(nodeType: ExperienceNodeType): ExperienceNodeSummary[];
199
210
  inspectLearningSummary(): ExperienceLearningSummary;
211
+ inspectHygiene(cwd?: string, filters?: Omit<HygieneReviewFilters, "scopeId"> & {
212
+ scopeId?: string;
213
+ }): HygieneReviewReport;
214
+ inspectExportDrafts(cwd?: string, filters?: Omit<ExperienceExportDraftFilters, "scopeId"> & {
215
+ scopeId?: string;
216
+ }): ExperienceExportDraftReport;
217
+ inspectReview(cwd?: string, filters?: {
218
+ scopeId?: string;
219
+ limit?: number;
220
+ }): OperatorReviewReport;
200
221
  private buildLearningSummary;
201
222
  inspectRepoSummary(cwd?: string): ExperienceRepoSummary;
223
+ inspectRepoPolicy(cwd?: string): import("../experience-management/repo-policy.js").RepoPolicyInspection;
224
+ restoreRepoPolicy(cwd?: string): import("../types/domain.js").RepoPolicy;
202
225
  inspectFirstValueReadiness(cwd?: string): ExperienceFirstValueReadiness;
203
226
  inspectDecisionHealth(cwd?: string, limit?: number): ExperienceDecisionHealth;
204
227
  feedbackLast(feedback: FeedbackValue, cwd?: string): FeedbackResult;
@@ -3,6 +3,10 @@ import { buildBenchmarkSummary } from "../evaluation/benchmark-summary.js";
3
3
  import { buildExplainDecisionCapsule } from "../hybrid/capsule-builder.js";
4
4
  import { resolveHybridExplainProviderEndpoint } from "../hybrid/explain-provider-client.js";
5
5
  import { resolveHybridRolloutState } from "../hybrid/rollout.js";
6
+ import { buildDefaultRepoPolicy, inspectRepoPolicyEvidence } from "../experience-management/repo-policy.js";
7
+ import { buildHygieneReviewReport } from "../maintenance/experience-hygiene.js";
8
+ import { buildExperienceExportDraftReport } from "../maintenance/experience-export-drafts.js";
9
+ import { buildOperatorReviewFlow } from "../maintenance/operator-review-flow.js";
6
10
  import { selectHybridRoute } from "../hybrid/router.js";
7
11
  import { HybridWorkerClient } from "../hybrid/worker-client.js";
8
12
  import { resolveScope } from "../input/scope-resolver.js";
@@ -12,6 +16,9 @@ import { DistillationJobRepository } from "../store/sqlite/repositories/distilla
12
16
  import { HybridInvocationTraceRepository } from "../store/sqlite/repositories/hybrid-invocation-trace-repo.js";
13
17
  import { InputRecordRepository } from "../store/sqlite/repositories/input-record-repo.js";
14
18
  import { InjectionRepository } from "../store/sqlite/repositories/injection-repo.js";
19
+ import { AttributionRecordRepository } from "../store/sqlite/repositories/attribution-record-repo.js";
20
+ import { RepoPolicyRepository } from "../store/sqlite/repositories/repo-policy-repo.js";
21
+ import { EpisodeRepository } from "../store/sqlite/repositories/episode-repo.js";
15
22
  import { NodeRepository } from "../store/sqlite/repositories/node-repo.js";
16
23
  import { OutcomeRecordRepository } from "../store/sqlite/repositories/outcome-record-repo.js";
17
24
  import { ReviewEventRepository } from "../store/sqlite/repositories/review-event-repo.js";
@@ -20,8 +27,9 @@ import { TaskRunRepository } from "../store/sqlite/repositories/task-run-repo.js
20
27
  import { applyGovernedNodeFeedback, deriveNodeOriginProfileForNode } from "../experience-management/node-lifecycle-governance.js";
21
28
  import { deriveGovernanceSignals, isPotentialMisfire } from "../experience-management/governance-observability.js";
22
29
  import { nowIso } from "../utils/clock.js";
23
- import { createId } from "../utils/ids.js";
30
+ import { createId, stableId } from "../utils/ids.js";
24
31
  import { buildRepoSummary } from "./repo-summary.js";
32
+ import { buildRetrievalPolicyInspectionSummary } from "./retrieval-policy-inspection.js";
25
33
  const normalizeHybridExplainPrompt = (value) => value
26
34
  .toLowerCase()
27
35
  .replace(/\s+/g, " ")
@@ -89,14 +97,41 @@ export const deriveStructuredSilenceReason = (input) => {
89
97
  }
90
98
  return "unknown";
91
99
  };
92
- const toReviewEvent = (nodeId, eventType, source, taskRunId) => ({
100
+ const toReviewEvent = (nodeId, eventType, source, taskRunId, episodeId) => ({
93
101
  id: createId("review"),
102
+ episode_id: episodeId,
94
103
  node_id: nodeId,
95
104
  task_run_id: taskRunId,
96
105
  event_type: eventType,
97
106
  source,
98
107
  created_at: nowIso()
99
108
  });
109
+ const toManualOverrideAttributionRecord = (input) => {
110
+ const timestamp = nowIso();
111
+ return {
112
+ id: stableId("attr", `${input.injectionEvent?.injection_id ?? "manual"}:${input.nodeId}:manual_override:${input.feedback}:${timestamp}`),
113
+ injection_id: input.injectionEvent?.injection_id,
114
+ node_id: input.nodeId,
115
+ episode_id: input.episodeId ?? input.injectionEvent?.episode_id,
116
+ intervention_strength: input.injectionEvent?.scorecard?.interventionStrength,
117
+ injection_mode: input.injectionEvent?.mode,
118
+ delivery_mode: input.injectionEvent?.delivery_mode,
119
+ delivered: Boolean(input.injectionEvent?.delivered),
120
+ outcome: input.injectionEvent?.was_successful === true
121
+ ? "success"
122
+ : input.injectionEvent?.was_successful === false
123
+ ? "failure"
124
+ : "unknown",
125
+ attribution_verdict: input.feedback === "helped" ? "strong_helped" : "strong_harmed",
126
+ confidence: "high",
127
+ evidence_refs: input.evidenceRefs,
128
+ user_override: input.feedback,
129
+ source: "manual_override",
130
+ attribution_reason: "manual_override",
131
+ created_at: timestamp,
132
+ resolved_at: timestamp
133
+ };
134
+ };
100
135
  const deriveNodeRisk = (node) => {
101
136
  if (node.state === "candidate") {
102
137
  return "high";
@@ -353,6 +388,9 @@ export class ExperienceInteractionService {
353
388
  hybridWorkerClient;
354
389
  inputRepo;
355
390
  injectionRepo;
391
+ attributionRecordRepo;
392
+ repoPolicyRepo;
393
+ episodeRepo;
356
394
  nodeRepo;
357
395
  candidateRepo;
358
396
  jobRepo;
@@ -367,6 +405,9 @@ export class ExperienceInteractionService {
367
405
  bootstrapDatabase(db);
368
406
  this.inputRepo = new InputRecordRepository(db);
369
407
  this.injectionRepo = new InjectionRepository(db);
408
+ this.attributionRecordRepo = new AttributionRecordRepository(db);
409
+ this.repoPolicyRepo = new RepoPolicyRepository(db);
410
+ this.episodeRepo = new EpisodeRepository(db);
370
411
  this.nodeRepo = new NodeRepository(db);
371
412
  this.candidateRepo = new CandidateRepository(db);
372
413
  this.jobRepo = new DistillationJobRepository(db);
@@ -458,15 +499,19 @@ export class ExperienceInteractionService {
458
499
  if (!record) {
459
500
  return undefined;
460
501
  }
461
- const injectionEvent = record.session_id
502
+ const episodeProjection = record.episode_id ? this.episodeRepo.getByEpisodeId(record.episode_id) : undefined;
503
+ const injectionEvent = episodeProjection?.injection_events[0] ?? (record.session_id
462
504
  ? this.injectionRepo.getLatestBySessionId(record.session_id)
463
505
  : record.injected_node_ids.length
464
506
  ? this.injectionRepo.getLatest()
465
- : undefined;
507
+ : undefined);
466
508
  const selectedNodeIds = injectionEvent?.injected_node_ids?.length
467
509
  ? injectionEvent.injected_node_ids
468
510
  : record.injected_node_ids;
469
511
  const injectedNodes = this.nodeRepo.listByIds(selectedNodeIds);
512
+ const attributionRecords = episodeProjection?.attribution_records ?? (injectionEvent
513
+ ? this.attributionRecordRepo.listByInjectionId(injectionEvent.injection_id)
514
+ : selectedNodeIds.flatMap((nodeId) => this.attributionRecordRepo.listByNodeId(nodeId)));
470
515
  const scorecard = injectionEvent?.scorecard ??
471
516
  (selectedNodeIds.length
472
517
  ? buildInjectionScorecard({
@@ -479,8 +524,8 @@ export class ExperienceInteractionService {
479
524
  injected_node_ids: selectedNodeIds
480
525
  }, "inject", injectedNodes, record.session_id)
481
526
  : undefined);
482
- const taskRun = record.session_id ? this.taskRunRepo.getLatestBySessionId(record.session_id) : undefined;
483
- const reviewEvents = taskRun?.id ? this.reviewEventRepo.listByTaskRunId(taskRun.id) : [];
527
+ const taskRun = episodeProjection?.task_run ?? (record.session_id ? this.taskRunRepo.getLatestBySessionId(record.session_id) : undefined);
528
+ const reviewEvents = episodeProjection?.review_events ?? (taskRun?.id ? this.reviewEventRepo.listByTaskRunId(taskRun.id) : []);
484
529
  const autoFeedback = summarizeAutomaticFeedback(reviewEvents);
485
530
  const intervention = injectionEvent?.mode === "skip"
486
531
  ? "skip"
@@ -491,7 +536,7 @@ export class ExperienceInteractionService {
491
536
  ? "holdout"
492
537
  : "shadow"
493
538
  : "inject";
494
- const outcomeRecord = taskRun?.id ? this.outcomeRepo.listByTaskRunId(taskRun.id)[0] : undefined;
539
+ const outcomeRecord = episodeProjection?.outcome_records[0] ?? (taskRun?.id ? this.outcomeRepo.listByTaskRunId(taskRun.id)[0] : undefined);
495
540
  const latestAutomaticFeedback = reviewEvents.find((event) => event.source === "automatic");
496
541
  const autoFeedbackReason = inferAutoFeedbackReason({
497
542
  explicitReason: injectionEvent?.attribution_reason,
@@ -502,6 +547,7 @@ export class ExperienceInteractionService {
502
547
  const decisionExplanation = buildDecisionExplanation({ intervention, scorecard });
503
548
  return {
504
549
  sessionId: record.session_id,
550
+ episodeId: record.episode_id,
505
551
  scopeId: record.scope_id,
506
552
  taskType: record.task_type,
507
553
  intervention,
@@ -509,6 +555,8 @@ export class ExperienceInteractionService {
509
555
  delivered: injectionEvent?.delivered,
510
556
  autoFeedback,
511
557
  autoFeedbackReason,
558
+ attributionRecords,
559
+ episodeProjection,
512
560
  outcome: record.outcome_signal,
513
561
  injectedNodes: injectedNodes.map(toNodeSummary),
514
562
  hints: injectedNodes.map((node) => node.compact_hint),
@@ -517,6 +565,7 @@ export class ExperienceInteractionService {
517
565
  decisionExplanation,
518
566
  trustSummary: buildTrustSummary({ scorecard, injectedNodes: injectedNodes.map(toNodeSummary) }),
519
567
  retrievalNotes: buildRetrievalNotes(scorecard),
568
+ retrievalPolicySummary: buildRetrievalPolicyInspectionSummary(scorecard),
520
569
  timeline: buildLatestTimeline({
521
570
  record,
522
571
  taskRunCreatedAt: taskRun?.created_at,
@@ -539,13 +588,15 @@ export class ExperienceInteractionService {
539
588
  if (!event) {
540
589
  return undefined;
541
590
  }
542
- const taskRun = event.session_id ? this.taskRunRepo.getLatestBySessionId(event.session_id) : undefined;
591
+ const episodeProjection = event.episode_id ? this.episodeRepo.getByEpisodeId(event.episode_id) : undefined;
592
+ const taskRun = episodeProjection?.task_run ?? (event.session_id ? this.taskRunRepo.getLatestBySessionId(event.session_id) : undefined);
543
593
  const latestRecord = event.session_id ? this.inputRepo.getLatestBySessionId(event.session_id) : undefined;
544
594
  if (latestRecord) {
545
595
  return this.inspectRecord(latestRecord);
546
596
  }
547
597
  const injectedNodes = this.nodeRepo.listByIds(event.injected_node_ids);
548
- const reviewEvents = taskRun?.id ? this.reviewEventRepo.listByTaskRunId(taskRun.id) : [];
598
+ const attributionRecords = episodeProjection?.attribution_records ?? this.attributionRecordRepo.listByInjectionId(event.injection_id);
599
+ const reviewEvents = episodeProjection?.review_events ?? (taskRun?.id ? this.reviewEventRepo.listByTaskRunId(taskRun.id) : []);
549
600
  const autoFeedback = summarizeAutomaticFeedback(reviewEvents);
550
601
  const latestAutomaticFeedback = reviewEvents.find((reviewEvent) => reviewEvent.source === "automatic");
551
602
  const intervention = event.mode === "skip"
@@ -555,13 +606,14 @@ export class ExperienceInteractionService {
555
606
  ? "holdout"
556
607
  : "shadow"
557
608
  : "inject";
558
- const outcomeRecord = taskRun?.id ? this.outcomeRepo.listByTaskRunId(taskRun.id)[0] : undefined;
609
+ const outcomeRecord = episodeProjection?.outcome_records[0] ?? (taskRun?.id ? this.outcomeRepo.listByTaskRunId(taskRun.id)[0] : undefined);
559
610
  const outcome = outcomeRecord?.outcome_signal ??
560
611
  (taskRun?.final_status === "success" ? "success" : taskRun?.final_status === "failure" ? "failure" : "unknown");
561
612
  const summary = event.task_summary ?? taskRun?.task_summary ?? "Latest injection event";
562
613
  const decisionExplanation = buildDecisionExplanation({ intervention, scorecard: event.scorecard });
563
614
  return {
564
615
  sessionId: event.session_id,
616
+ episodeId: event.episode_id,
565
617
  scopeId: event.scope_id,
566
618
  taskType: event.task_type,
567
619
  intervention,
@@ -574,6 +626,8 @@ export class ExperienceInteractionService {
574
626
  intervention,
575
627
  outcome
576
628
  }),
629
+ attributionRecords,
630
+ episodeProjection,
577
631
  outcome,
578
632
  injectedNodes: injectedNodes.map(toNodeSummary),
579
633
  hints: injectedNodes.map((node) => node.compact_hint),
@@ -582,6 +636,7 @@ export class ExperienceInteractionService {
582
636
  decisionExplanation,
583
637
  trustSummary: buildTrustSummary({ scorecard: event.scorecard, injectedNodes: injectedNodes.map(toNodeSummary) }),
584
638
  retrievalNotes: buildRetrievalNotes(event.scorecard),
639
+ retrievalPolicySummary: buildRetrievalPolicyInspectionSummary(event.scorecard),
585
640
  timeline: buildLatestTimeline({
586
641
  record: {
587
642
  record_id: `injection:${event.injection_id}`,
@@ -665,6 +720,77 @@ export class ExperienceInteractionService {
665
720
  inspectLearningSummary() {
666
721
  return this.buildLearningSummary();
667
722
  }
723
+ inspectHygiene(cwd = process.cwd(), filters = {}) {
724
+ const scopeId = filters.scopeId ?? resolveScope(cwd).scope_id;
725
+ const candidateStates = ["pending", "distilled", "failed", "discarded"];
726
+ return buildHygieneReviewReport({
727
+ nodes: this.nodeRepo.listByScope(scopeId),
728
+ candidates: candidateStates.flatMap((state) => this.candidateRepo.listByLifecycleState(state)).filter((candidate) => candidate.scope_id === scopeId),
729
+ attributionRecords: this.attributionRecordRepo.listRecentByScope(scopeId, Math.max(50, filters.limit ?? 20)),
730
+ filters: {
731
+ ...filters,
732
+ scopeId
733
+ }
734
+ });
735
+ }
736
+ inspectExportDrafts(cwd = process.cwd(), filters = {}) {
737
+ const scopeId = filters.scopeId ?? resolveScope(cwd).scope_id;
738
+ const candidateStates = ["pending", "distilled", "failed", "discarded"];
739
+ const nodes = this.nodeRepo.listByScope(scopeId);
740
+ const explicitLowReadiness = Boolean(filters.nodeId || filters.risk || filters.state || filters.deliveryState);
741
+ const candidateNodes = nodes
742
+ .filter((node) => !filters.nodeId || node.id === filters.nodeId)
743
+ .filter((node) => !filters.nodeType || node.node_type === filters.nodeType)
744
+ .filter((node) => !filters.taskFamily || node.task_type === filters.taskFamily)
745
+ .filter((node) => !filters.state || node.state === filters.state)
746
+ .filter((node) => !filters.deliveryState || node.delivery_state === filters.deliveryState)
747
+ .filter((node) => explicitLowReadiness || (node.state === "active" && (!node.delivery_state || node.delivery_state === "eligible") && node.harmed_count <= node.helped_count));
748
+ const candidates = candidateStates
749
+ .flatMap((state) => this.candidateRepo.listByLifecycleState(state))
750
+ .filter((candidate) => candidate.scope_id === scopeId);
751
+ const attributionRecordsById = new Map();
752
+ for (const record of this.attributionRecordRepo.listRecentByScope(scopeId, Math.max(50, filters.limit ?? 20))) {
753
+ attributionRecordsById.set(record.id, record);
754
+ }
755
+ for (const node of candidateNodes) {
756
+ for (const record of this.attributionRecordRepo.listByNodeId(node.id)) {
757
+ attributionRecordsById.set(record.id, record);
758
+ }
759
+ }
760
+ const attributionRecords = [...attributionRecordsById.values()];
761
+ const hygiene = buildHygieneReviewReport({
762
+ nodes,
763
+ candidates,
764
+ attributionRecords,
765
+ filters: {
766
+ scopeId,
767
+ limit: Math.max(50, filters.limit ?? 20)
768
+ }
769
+ });
770
+ return buildExperienceExportDraftReport({
771
+ nodes,
772
+ candidates,
773
+ attributionRecords,
774
+ hygieneFindings: hygiene.findings,
775
+ filters: {
776
+ ...filters,
777
+ scopeId
778
+ }
779
+ });
780
+ }
781
+ inspectReview(cwd = process.cwd(), filters = {}) {
782
+ const scopeId = filters.scopeId ?? resolveScope(cwd).scope_id;
783
+ const limit = filters.limit ?? 5;
784
+ const repo = this.inspectRepoSummary(cwd);
785
+ const hygiene = this.inspectHygiene(cwd, { scopeId, limit });
786
+ const exportDrafts = this.inspectExportDrafts(cwd, { scopeId, limit });
787
+ return buildOperatorReviewFlow({
788
+ repo,
789
+ hygiene,
790
+ exportDrafts,
791
+ limit
792
+ });
793
+ }
668
794
  buildLearningSummary(scopeId) {
669
795
  const candidateStates = ["pending", "distilled", "failed", "discarded"];
670
796
  const jobStates = ["pending", "processing", "succeeded", "failed", "discarded"];
@@ -728,6 +854,7 @@ export class ExperienceInteractionService {
728
854
  const latestRecord = this.inputRepo.getLatestByScope(scope.scope_id);
729
855
  const latest = latestRecord ? this.inspectRecord(latestRecord) : undefined;
730
856
  const learning = this.buildLearningSummary(scope.scope_id);
857
+ const policyInspection = this.inspectRepoPolicy(cwd);
731
858
  return buildRepoSummary({
732
859
  scope: {
733
860
  scopeId: scope.scope_id,
@@ -735,9 +862,20 @@ export class ExperienceInteractionService {
735
862
  rootPath: scope.root_path
736
863
  },
737
864
  latest: latest && latest.scopeId === scope.scope_id ? latest : undefined,
738
- learning
865
+ learning,
866
+ policyInspection
739
867
  });
740
868
  }
869
+ inspectRepoPolicy(cwd = process.cwd()) {
870
+ const scope = resolveScope(cwd);
871
+ const policy = this.repoPolicyRepo.get(scope.scope_id) ??
872
+ buildDefaultRepoPolicy(scope.scope_id, this.config.repoExperienceMode);
873
+ return inspectRepoPolicyEvidence(policy, this.attributionRecordRepo.listRecentEligibleByScope(scope.scope_id), this.injectionRepo.listRecentResolvedByScope(scope.scope_id));
874
+ }
875
+ restoreRepoPolicy(cwd = process.cwd()) {
876
+ const scope = resolveScope(cwd);
877
+ return this.repoPolicyRepo.restore(scope.scope_id, this.config.repoExperienceMode);
878
+ }
741
879
  inspectFirstValueReadiness(cwd = process.cwd()) {
742
880
  const scope = resolveScope(cwd);
743
881
  const summary = this.buildLearningSummary(scope.scope_id);
@@ -873,9 +1011,25 @@ export class ExperienceInteractionService {
873
1011
  const taskRunId = record.session_id
874
1012
  ? this.taskRunRepo.getLatestBySessionId(record.session_id)?.id
875
1013
  : undefined;
1014
+ const episodeId = record.episode_id;
1015
+ const injectionEvent = record.session_id
1016
+ ? this.injectionRepo.getLatestBySessionId(record.session_id)
1017
+ : undefined;
1018
+ const evidenceRefs = [
1019
+ record.record_id,
1020
+ taskRunId,
1021
+ injectionEvent?.injection_id
1022
+ ].filter((value) => Boolean(value));
876
1023
  for (const node of nodes) {
877
1024
  this.nodeRepo.upsert(applyGovernedNodeFeedback(node, feedback, this.deriveOriginProfile(node)));
878
- this.reviewEventRepo.upsert(toReviewEvent(node.id, feedback === "helped" ? "mark_helped" : "mark_harmed", "user", taskRunId));
1025
+ this.reviewEventRepo.upsert(toReviewEvent(node.id, feedback === "helped" ? "mark_helped" : "mark_harmed", "user", taskRunId, episodeId));
1026
+ this.attributionRecordRepo.insert(toManualOverrideAttributionRecord({
1027
+ nodeId: node.id,
1028
+ feedback,
1029
+ injectionEvent,
1030
+ episodeId,
1031
+ evidenceRefs
1032
+ }));
879
1033
  }
880
1034
  return {
881
1035
  status: "updated",
@@ -894,6 +1048,11 @@ export class ExperienceInteractionService {
894
1048
  }
895
1049
  this.nodeRepo.upsert(applyGovernedNodeFeedback(node, feedback, this.deriveOriginProfile(node)));
896
1050
  this.reviewEventRepo.upsert(toReviewEvent(nodeId, feedback === "helped" ? "mark_helped" : "mark_harmed", "user"));
1051
+ this.attributionRecordRepo.insert(toManualOverrideAttributionRecord({
1052
+ nodeId,
1053
+ feedback,
1054
+ evidenceRefs: [`manual:${nodeId}`]
1055
+ }));
897
1056
  return {
898
1057
  status: "updated",
899
1058
  feedback,