@alan512/experienceengine 0.3.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +7 -2
  2. package/README.zh-CN.md +7 -2
  3. package/dist/cli/commands/doctor.js +56 -0
  4. package/dist/cli/commands/doctor.js.map +1 -1
  5. package/dist/cli/commands/inspect.js +78 -1
  6. package/dist/cli/commands/inspect.js.map +1 -1
  7. package/dist/cli/commands/maintenance.js +80 -2
  8. package/dist/cli/commands/maintenance.js.map +1 -1
  9. package/dist/compiler/command-normalizer.d.ts +27 -0
  10. package/dist/compiler/command-normalizer.js +263 -0
  11. package/dist/compiler/command-normalizer.js.map +1 -0
  12. package/dist/compiler/trajectory-compiler.d.ts +12 -0
  13. package/dist/compiler/trajectory-compiler.js +179 -0
  14. package/dist/compiler/trajectory-compiler.js.map +1 -0
  15. package/dist/compiler/trajectory-matcher.d.ts +18 -0
  16. package/dist/compiler/trajectory-matcher.js +419 -0
  17. package/dist/compiler/trajectory-matcher.js.map +1 -0
  18. package/dist/config/config-schema.d.ts +11 -0
  19. package/dist/config/config-schema.js +9 -0
  20. package/dist/config/config-schema.js.map +1 -1
  21. package/dist/config/default-config.js +1 -0
  22. package/dist/config/default-config.js.map +1 -1
  23. package/dist/controller/candidate-retriever.d.ts +3 -1
  24. package/dist/controller/candidate-retriever.js +247 -7
  25. package/dist/controller/candidate-retriever.js.map +1 -1
  26. package/dist/controller/intervention-controller.js +48 -21
  27. package/dist/controller/intervention-controller.js.map +1 -1
  28. package/dist/controller/trigger-evaluator.d.ts +2 -1
  29. package/dist/controller/trigger-evaluator.js +10 -4
  30. package/dist/controller/trigger-evaluator.js.map +1 -1
  31. package/dist/experience-management/node-lifecycle-governance.js +25 -6
  32. package/dist/experience-management/node-lifecycle-governance.js.map +1 -1
  33. package/dist/input/fingerprint-extractor.d.ts +53 -0
  34. package/dist/input/fingerprint-extractor.js +620 -0
  35. package/dist/input/fingerprint-extractor.js.map +1 -0
  36. package/dist/interaction/service.d.ts +11 -1
  37. package/dist/interaction/service.js +11 -1
  38. package/dist/interaction/service.js.map +1 -1
  39. package/dist/maintenance/vector-migrator.d.ts +37 -0
  40. package/dist/maintenance/vector-migrator.js +170 -0
  41. package/dist/maintenance/vector-migrator.js.map +1 -0
  42. package/dist/plugin/openclaw-plugin.d.ts +8 -0
  43. package/dist/runtime/prompt-service.js +10 -0
  44. package/dist/runtime/prompt-service.js.map +1 -1
  45. package/dist/runtime/service.d.ts +2 -0
  46. package/dist/runtime/service.js +127 -2
  47. package/dist/runtime/service.js.map +1 -1
  48. package/dist/store/sqlite/db.js +18 -1
  49. package/dist/store/sqlite/db.js.map +1 -1
  50. package/dist/store/sqlite/repositories/attribution-record-repo.js +12 -0
  51. package/dist/store/sqlite/repositories/attribution-record-repo.js.map +1 -1
  52. package/dist/store/sqlite/repositories/node-repo.d.ts +1 -0
  53. package/dist/store/sqlite/repositories/node-repo.js +47 -2
  54. package/dist/store/sqlite/repositories/node-repo.js.map +1 -1
  55. package/dist/store/sqlite/repositories/scope-fingerprint-repo.d.ts +8 -0
  56. package/dist/store/sqlite/repositories/scope-fingerprint-repo.js +23 -0
  57. package/dist/store/sqlite/repositories/scope-fingerprint-repo.js.map +1 -0
  58. package/dist/store/sqlite/schema.sql +26 -0
  59. package/dist/store/vector/embeddings.d.ts +4 -2
  60. package/dist/store/vector/embeddings.js +19 -6
  61. package/dist/store/vector/embeddings.js.map +1 -1
  62. package/dist/store/vector/local-provider.d.ts +1 -1
  63. package/dist/store/vector/local-provider.js +40 -8
  64. package/dist/store/vector/local-provider.js.map +1 -1
  65. package/dist/store/vector/offline-manifest.d.ts +7 -0
  66. package/dist/store/vector/offline-manifest.js +162 -0
  67. package/dist/store/vector/offline-manifest.js.map +1 -0
  68. package/dist/store/vector/provider-types.d.ts +1 -0
  69. package/dist/types/domain.d.ts +111 -1
  70. package/dist/types/plugin.d.ts +1 -0
  71. package/docs/releases/v0.4.0.md +39 -0
  72. package/docs/user-guide.md +41 -0
  73. package/openclaw.plugin.json +1 -1
  74. package/package.json +1 -1
@@ -0,0 +1,162 @@
1
+ import { readFileSync, existsSync, mkdirSync, copyFileSync } from "node:fs";
2
+ import { join, dirname, isAbsolute } from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ export const isSafeRelativePath = (p) => {
5
+ if (typeof p !== "string" || !p)
6
+ return false;
7
+ if (isAbsolute(p))
8
+ return false;
9
+ if (/^[a-zA-Z]:/.test(p))
10
+ return false;
11
+ const parts = p.split(/[/\\]/);
12
+ for (const part of parts) {
13
+ if (part === ".." || part === ".") {
14
+ return false;
15
+ }
16
+ }
17
+ return true;
18
+ };
19
+ export const calculateFileSha256 = (filePath) => {
20
+ const content = readFileSync(filePath);
21
+ return createHash("sha256").update(content).digest("hex");
22
+ };
23
+ export const validateOfflineManifest = (manifest, baseDir) => {
24
+ if (!manifest || typeof manifest !== "object") {
25
+ throw new Error("Invalid manifest format: not an object.");
26
+ }
27
+ const m = manifest;
28
+ if (typeof m.manifestVersion !== "string" || !m.manifestVersion) {
29
+ throw new Error("Missing or invalid manifestVersion.");
30
+ }
31
+ if (m.providerId !== "local") {
32
+ throw new Error(`Unsupported providerId: ${m.providerId}. Only 'local' is supported.`);
33
+ }
34
+ if (typeof m.modelId !== "string" || !m.modelId) {
35
+ throw new Error("Missing or invalid modelId.");
36
+ }
37
+ if (typeof m.dimensions !== "number" || m.dimensions <= 0) {
38
+ throw new Error("Missing or invalid dimensions.");
39
+ }
40
+ if (typeof m.preprocessingVersion !== "string" || !m.preprocessingVersion) {
41
+ throw new Error("Missing or invalid preprocessingVersion.");
42
+ }
43
+ if (!m.assets || typeof m.assets !== "object") {
44
+ throw new Error("Missing or invalid assets object.");
45
+ }
46
+ const validatedAssets = {};
47
+ const assetsObj = m.assets;
48
+ for (const [key, value] of Object.entries(assetsObj)) {
49
+ if (!value || typeof value !== "object") {
50
+ throw new Error(`Invalid asset entry for key: ${key}.`);
51
+ }
52
+ const val = value;
53
+ if (typeof val.path !== "string" || !val.path || !isSafeRelativePath(val.path)) {
54
+ throw new Error(`Missing, invalid, or unsafe path for asset: ${key}. Path traversal detected or suspected.`);
55
+ }
56
+ if (typeof val.sha256 !== "string" || !val.sha256) {
57
+ throw new Error(`Missing or invalid sha256 checksum for asset: ${key}.`);
58
+ }
59
+ // Validate file presence and sha256
60
+ const absolutePath = join(baseDir, val.path);
61
+ if (!existsSync(absolutePath)) {
62
+ throw new Error(`Asset file missing: ${absolutePath}`);
63
+ }
64
+ const actualSha256 = calculateFileSha256(absolutePath);
65
+ if (actualSha256 !== val.sha256) {
66
+ throw new Error(`Asset checksum mismatch for file ${val.path}. Expected: ${val.sha256}, Actual: ${actualSha256}`);
67
+ }
68
+ validatedAssets[key] = {
69
+ path: val.path,
70
+ sha256: val.sha256
71
+ };
72
+ }
73
+ let manifestId = typeof m.id === "string" ? m.id : undefined;
74
+ if (!manifestId) {
75
+ const assetKeysSorted = Object.keys(validatedAssets).sort();
76
+ const assetsString = assetKeysSorted
77
+ .map((key) => `${key}:${validatedAssets[key].sha256}`)
78
+ .join(";");
79
+ const idSource = `${m.modelId}|${m.dimensions}|${m.preprocessingVersion}|${assetsString}`;
80
+ manifestId = `derived-${createHash("sha256").update(idSource).digest("hex").slice(0, 16)}`;
81
+ }
82
+ return {
83
+ id: manifestId,
84
+ manifestVersion: m.manifestVersion,
85
+ providerId: m.providerId,
86
+ modelId: m.modelId,
87
+ dimensions: m.dimensions,
88
+ preprocessingVersion: m.preprocessingVersion,
89
+ assets: validatedAssets,
90
+ license: typeof m.license === "string" ? m.license : undefined,
91
+ sourceMetadata: typeof m.sourceMetadata === "object" && m.sourceMetadata !== null ? m.sourceMetadata : undefined
92
+ };
93
+ };
94
+ export const loadOfflineManifestForModel = (cacheDir, model) => {
95
+ const modelCacheDir = join(cacheDir, ...model.split("/"));
96
+ const manifestPath = join(modelCacheDir, "manifest.json");
97
+ if (!existsSync(manifestPath)) {
98
+ throw new Error(`Manifest file not found: ${manifestPath}`);
99
+ }
100
+ try {
101
+ const rawContent = readFileSync(manifestPath, "utf8");
102
+ const manifestJson = JSON.parse(rawContent);
103
+ return validateOfflineManifest(manifestJson, modelCacheDir);
104
+ }
105
+ catch (error) {
106
+ const msg = error instanceof Error ? error.message : String(error);
107
+ throw new Error(`Failed to load or validate manifest at ${manifestPath}: ${msg}`);
108
+ }
109
+ };
110
+ export const importOfflineAssetPack = async (packDir, cacheDir) => {
111
+ const manifestPath = join(packDir, "manifest.json");
112
+ if (!existsSync(manifestPath)) {
113
+ throw new Error(`Asset pack missing manifest.json at ${packDir}`);
114
+ }
115
+ // Read and validate the manifest in the staging/pack directory
116
+ let manifest;
117
+ try {
118
+ const rawContent = readFileSync(manifestPath, "utf8");
119
+ const manifestJson = JSON.parse(rawContent);
120
+ manifest = validateOfflineManifest(manifestJson, packDir);
121
+ }
122
+ catch (error) {
123
+ const msg = error instanceof Error ? error.message : String(error);
124
+ throw new Error(`Import failed during manifest validation: ${msg}`);
125
+ }
126
+ const targetModelDir = join(cacheDir, ...manifest.modelId.split("/"));
127
+ mkdirSync(targetModelDir, { recursive: true });
128
+ // Copy manifest.json
129
+ copyFileSync(manifestPath, join(targetModelDir, "manifest.json"));
130
+ // Copy all assets
131
+ for (const asset of Object.values(manifest.assets)) {
132
+ const sourcePath = join(packDir, asset.path);
133
+ const targetPath = join(targetModelDir, asset.path);
134
+ mkdirSync(dirname(targetPath), { recursive: true });
135
+ copyFileSync(sourcePath, targetPath);
136
+ }
137
+ try {
138
+ const { clearLocalEmbeddingProviderCache } = await import("./local-provider.js");
139
+ const { clearEmbeddingRuntimeCaches } = await import("./embeddings.js");
140
+ clearLocalEmbeddingProviderCache();
141
+ clearEmbeddingRuntimeCaches();
142
+ }
143
+ catch {
144
+ // Gracefully handle dynamic import edge cases
145
+ }
146
+ };
147
+ export const exportOfflineAssetPack = async (cacheDir, model, targetPackDir) => {
148
+ // Load and validate from cache
149
+ const manifest = loadOfflineManifestForModel(cacheDir, model);
150
+ const modelCacheDir = join(cacheDir, ...model.split("/"));
151
+ mkdirSync(targetPackDir, { recursive: true });
152
+ // Copy manifest.json
153
+ copyFileSync(join(modelCacheDir, "manifest.json"), join(targetPackDir, "manifest.json"));
154
+ // Copy all assets
155
+ for (const asset of Object.values(manifest.assets)) {
156
+ const sourcePath = join(modelCacheDir, asset.path);
157
+ const targetPath = join(targetPackDir, asset.path);
158
+ mkdirSync(dirname(targetPath), { recursive: true });
159
+ copyFileSync(sourcePath, targetPath);
160
+ }
161
+ };
162
+ //# sourceMappingURL=offline-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline-manifest.js","sourceRoot":"","sources":["../../../src/store/vector/offline-manifest.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ export type SemanticEmbeddingProvider = {
3
3
  model: string;
4
4
  version: string;
5
5
  dimensions: number;
6
+ manifestId?: string;
6
7
  embedQuery(text: string): Promise<number[]>;
7
8
  embedPassage(text: string): Promise<number[]>;
8
9
  };
@@ -1,7 +1,7 @@
1
1
  export type TaskType = "bug_fix" | "build_debug" | "config_debug" | "test_debug" | "integration_fix" | "feature_add" | "refactor" | "performance" | "general";
2
2
  export type ResolvedTaskType = TaskType | "unknown";
3
3
  export type ExperienceState = "candidate" | "priority_candidate" | "active" | "cooling" | "retired";
4
- export type DeliveryState = "shadow_only" | "conservative_only" | "eligible" | "quarantined";
4
+ export type DeliveryState = "shadow_only" | "conservative_only" | "eligible" | "quarantined" | "shadow_probe" | "retired";
5
5
  export type ExperienceNodeType = "strategy" | "warning";
6
6
  export type PromotionSignal = "normal" | "high_value";
7
7
  export type MergeAction = "ADD" | "UPDATE" | "NONE";
@@ -88,6 +88,7 @@ export type RetrievalContext = {
88
88
  isReadOnly?: boolean;
89
89
  modulePaths?: string[];
90
90
  expectationCorrectionIntent?: boolean;
91
+ db?: any;
91
92
  };
92
93
  export type RetrievalPolicyStageName = "retrieval_context" | "hard_filter" | "shortlist" | "semantic_rerank_backfill" | "policy_enrichment" | "decision_assembly";
93
94
  export type RetrievalPolicyStageDiagnostic = {
@@ -245,6 +246,24 @@ export type ExperienceNode = {
245
246
  last_harmed_at?: string;
246
247
  quarantined_at?: string;
247
248
  quarantine_reason?: string;
249
+ embedding_manifest_id?: string;
250
+ migration_status?: 'current' | 'pending' | 'migrating' | 'failed';
251
+ migration_last_error?: string;
252
+ migration_updated_at?: string;
253
+ source_fingerprint_hash?: string;
254
+ portable_validation_evidence?: {
255
+ compatibilityClasses: Record<string, {
256
+ successReuseCount: number;
257
+ harmCount: number;
258
+ lastUsedAt: number;
259
+ }>;
260
+ };
261
+ quarantine_lease_expires_at?: string;
262
+ quarantine_original_delivery_state?: DeliveryState;
263
+ quarantine_release_attempt_count?: number;
264
+ quarantine_last_release_attempt_at?: string;
265
+ quarantine_release_reason?: string;
266
+ quarantine_no_harm_pass_count?: number;
248
267
  created_at: string;
249
268
  updated_at: string;
250
269
  };
@@ -283,6 +302,11 @@ export type AttributionRecord = {
283
302
  user_override?: "helped" | "harmed" | "neutral";
284
303
  source: AttributionSource;
285
304
  attribution_reason?: FeedbackAttributionReason | "manual_override" | "diagnostic_record";
305
+ trajectory_verdict?: string;
306
+ trajectory_confidence?: string;
307
+ trajectory_matched_expectations?: string[];
308
+ trajectory_violated_expectations?: string[];
309
+ trajectory_evidence_refs?: string[];
286
310
  created_at: string;
287
311
  resolved_at?: string;
288
312
  };
@@ -336,6 +360,7 @@ export type InjectionScorecardNode = {
336
360
  export type InjectionScorecardCandidate = {
337
361
  id: string;
338
362
  matchScorecard?: MatchScorecard;
363
+ portabilityScorecard?: PortabilityScorecard;
339
364
  semanticScore?: number;
340
365
  lexicalScore?: number;
341
366
  fusedScore?: number;
@@ -514,3 +539,88 @@ export type DistillationJob = {
514
539
  finished_at?: string;
515
540
  discarded_at?: string;
516
541
  };
542
+ export type ProjectFingerprint = {
543
+ schemaVersion: string;
544
+ fingerprintHash: string;
545
+ timestamp: number;
546
+ primaryLanguage: string;
547
+ packageManager: string;
548
+ lockfileFamily: string;
549
+ frameworks: Record<string, number>;
550
+ databaseOrORM: Record<string, number>;
551
+ testBuildTools: Record<string, number>;
552
+ hostRuntimeAdapters: Record<string, number>;
553
+ configMarkers: string[];
554
+ workspaceRootPath?: string;
555
+ projectRootScopeId?: string;
556
+ };
557
+ export type ScopeFingerprint = {
558
+ scope_id: string;
559
+ schema_version: string;
560
+ fingerprint_hash: string;
561
+ fingerprint_json: string;
562
+ created_at: string;
563
+ updated_at: string;
564
+ };
565
+ export type OfflineAssetManifest = {
566
+ id?: string;
567
+ manifestVersion: string;
568
+ providerId: string;
569
+ modelId: string;
570
+ dimensions: number;
571
+ preprocessingVersion: string;
572
+ assets: Record<string, {
573
+ path: string;
574
+ sha256: string;
575
+ }>;
576
+ license?: string;
577
+ sourceMetadata?: Record<string, unknown>;
578
+ };
579
+ export type PortabilityBand = "incompatible" | "weakly_related" | "same_family" | "validated_portable";
580
+ export type PortabilityScorecard = {
581
+ portabilityBand: PortabilityBand;
582
+ score: number;
583
+ matchedLanguage: boolean;
584
+ sharedDependencies: string[];
585
+ penalties: Array<{
586
+ dependency: string;
587
+ category: string;
588
+ penalty: number;
589
+ reason: string;
590
+ }>;
591
+ negativeEvidence: string[];
592
+ whyScore: string;
593
+ successReuseCount?: number;
594
+ harmCount?: number;
595
+ };
596
+ export type TrajectoryExpectationType = "recommend" | "avoid";
597
+ export type ExpectationActionType = "command" | "artifact" | "generic";
598
+ export type TrajectoryExpectation = {
599
+ id: string;
600
+ type: TrajectoryExpectationType;
601
+ actionType: ExpectationActionType;
602
+ toolNamePattern?: string;
603
+ commandPattern?: string;
604
+ artifactPattern?: string;
605
+ artifactAction?: "read" | "write" | "any";
606
+ originalStep: string;
607
+ ordered: boolean;
608
+ sourceField?: "recommended_steps" | "avoid_steps" | "success_signal" | "stop_condition" | "escalation_condition";
609
+ requiredForAdoption?: boolean;
610
+ };
611
+ export type CompiledTrajectoryExpectations = {
612
+ orderedExpectations: TrajectoryExpectation[];
613
+ unorderedExpectations: TrajectoryExpectation[];
614
+ };
615
+ export type NormalizedToolEvent = {
616
+ toolName: string;
617
+ commandFamily?: string;
618
+ subcommand?: string;
619
+ normalizedInput?: string;
620
+ normalizedOutput?: string;
621
+ artifactExtension?: string;
622
+ artifactName?: string;
623
+ artifactPath?: string;
624
+ artifactPaths?: string[];
625
+ status: "success" | "failure" | "unknown";
626
+ };
@@ -8,6 +8,7 @@ export type HostPromptContext = {
8
8
  taskSummary?: string;
9
9
  contextSummary?: string;
10
10
  injectedNodeIds?: string[];
11
+ outcomeSignal?: string;
11
12
  };
12
13
  export type HostToolResult = {
13
14
  sessionId?: string;
@@ -0,0 +1,39 @@
1
+ # ExperienceEngine v0.4.0
2
+
3
+ `v0.4.0` introduces the **Governed Portable Experience** layer, enabling safe cross-repository experience sharing, stack-aware compatibility fingerprinting, causal trajectory-based attribution, and a lease-based quarantine and shadow-probe lifecycle.
4
+
5
+ ## What Changed
6
+
7
+ - **Offline Embedding Profiles**: Added support for `standard`, `local-download`, and `strict-offline` embedding profile configurations, backed by a strict offline asset manifest registry that validates checksums and licenses.
8
+ - **Compatibility Fingerprints**: Implemented deterministic fingerprint extraction for repository scopes, capturing primary languages, frameworks, ORMs, and lockfile-resolved (npm, pnpm, yarn) dependency major versions, with monorepo workspace package support.
9
+ - **Portability Scoring and Bands**: Created a scorecard combining fingerprint, path, and task family similarity with prior reuse evidence. Classifies cross-repo candidates into portability bands (`validated_portable`, `same_family`, `weakly_related`, `incompatible`) with strict framework SemVer major-version penalties.
10
+ - **Cross-Repository Intervention Governance**: Enabled progressive delivery where `same_family` candidates are limited to conservative prompt delivery, `weakly_related` remain record-only, and `incompatible` are skipped.
11
+ - **Causal Trajectory Attribution**: Built a compiler that normalizes tool names/arguments and redacts volatile tokens (temp paths, UUIDs, branch names) to check timelines against compiled expectations (`recommended_steps` and `avoid_steps`). Yields detailed verdicts (`adoption_detected`, `non_adoption_detected`, `contra_adoption_detected`, etc.).
12
+ - **Quarantine Shadow-Probe Lifecycle**: Expired quarantine leases now transition candidates to a `shadow_probe` state. These are withheld from live prompt injection but evaluated as diagnostic candidates, restoring to `conservative` only after accumulating bounded no-harm passes in matching tasks, or retiring/re-quarantining on repeated harm.
13
+ - **Enhanced Diagnostics and Operator Surfaces**:
14
+ - Extended `ee doctor` to report embedding profile type, manifest checksum health, and offline asset readiness.
15
+ - Extended `ee inspect` with verbose output showing compatibility fingerprint summaries, SemVer penalties, causal trajectory verdicts with matched/violated expectations, and quarantine lease/shadow-probe histories.
16
+ - Updated README, README.zh-CN, and `docs/user-guide.md` with complete architecture and operator documentation.
17
+
18
+ ## Boundaries
19
+
20
+ - **No Blind Cross-Repo Injection**: Tech-stack similarity alone never unlocks direct eligible prompt injection for cross-repo candidates.
21
+ - **Strict Shadow-Probe Isolation**: Shadow-probe nodes remain completely excluded from live prompt injection; they accumulate no-harm passes solely via record-only diagnostic evaluations.
22
+ - **Avoid-Step Defaults**: Avoid-step violations default to non-adoption (`non_adoption_detected` or `contra_adoption_detected`) rather than automatically triggering a causal harm verdict, unless causal failure is explicitly observed.
23
+ - **Offline Integrity**: Strict offline profiles fail loudly on missing or corrupt assets, preventing silent online fallbacks unless explicitly allowed.
24
+
25
+ ## Validation
26
+
27
+ - Comprehensive unit and integration test coverage for embedding profiles, vector migration, fingerprinting, portability scoring, trajectory compilation, causal matches, quarantine leases, shadow-probes, and CLI diagnostics.
28
+ - Validation checks completed successfully:
29
+ - `pnpm exec tsc --noEmit`
30
+ - `pnpm exec vitest run`
31
+ - `pnpm exec openspec validate add-governed-portable-experience --strict`
32
+ - `pnpm check`
33
+ - `npm pack --dry-run`
34
+ - Real-host validation completed for:
35
+ - WSL Codex CLI lifecycle wrapper and persistence.
36
+ - Claude Code MCP wiring and `experienceengine_get_capabilities` tool invocation with `nvidia/nemotron-3-super-120b-a12b:free`.
37
+ - WSL OpenClaw plugin wiring after `ee repair openclaw`, with live high-confidence scenario writeback using `openrouter/nvidia/nemotron-3-super-120b-a12b:free`.
38
+ - Known host boundary:
39
+ - Windows Codex App validation was blocked by the local WindowsApps `codex.exe` shim returning `EPERM` / `Access is denied`; WSL Codex CLI validation passed.
@@ -366,6 +366,40 @@ ee maintenance governance drain --cwd /path/to/repo
366
366
 
367
367
  That command is for explicit operator troubleshooting or catch-up. It uses the same scheduler, lease, validator, audit, and rollback snapshot path as host-attached governance. An optional keeper can wake the same drain path for stricter wall-clock schedules, but it does not bypass budgets, leases, deterministic validators, guarded execution rules, or rollback safeguards.
368
368
 
369
+ ## Governed Portable Experience
370
+
371
+ ExperienceEngine connects retrieval, attribution, and lifecycle layers to support governed, cautious sharing of experience nodes across different repositories and scopes.
372
+
373
+ ### Portability Bands & SemVer Compatibility
374
+
375
+ When retrieving experience candidates for a task, ExperienceEngine calculates a cross-repository compatibility scorecard. This scorecard determines how safe the guidance is in the current environment using the following **Portability Bands**:
376
+ - `validated_portable`: Highly compatible. The node matches the local programming language, and any shared dependencies have verified SemVer compatibility.
377
+ - `cautiously_portable`: Medium compatibility. Reused with caution, usually limited to conservative delivery state until local execution success is demonstrated.
378
+ - `incompatible`: The node requires frameworks or languages not present in the current project. Delivery is blocked.
379
+
380
+ Compatibility scoring factors in **SemVer matching penalties**:
381
+ - Framework or dependency version mismatches subtract penalty points (e.g. minor or major version drift).
382
+ - Language mismatches (e.g., trying to use Python guidance in a TypeScript codebase) flag the node as incompatible.
383
+
384
+ ### Causal Trajectory Attribution
385
+
386
+ During post-task finalization, ExperienceEngine verifies if the host agent actually adopted the expectations of an injected hint. This **Causal Trajectory Attribution** produces a precise verdict:
387
+ - `adoption_detected`: The agent successfully matched the expectations of the injected hint (e.g., specific file modifications or CLI tool executions).
388
+ - `non_adoption_detected`: The agent completed the task but did not follow the suggested guidance.
389
+ - `unverifiable`: The task ended in failure, or outcomes could not be traced clearly.
390
+
391
+ These trajectory verdicts prevent false positives in outcome attribution, ensuring that helpfulness or harm is only counted if the agent actually used the guidance.
392
+
393
+ ### Quarantine Leases & Shadow-Probe Release
394
+
395
+ To ensure quarantined nodes have a safe path back to eligibility without erasing historical lessons:
396
+ 1. **Quarantine Leases**: When a node is quarantined due to harm or invalidation, it is given a lease duration.
397
+ 2. **Lease Expiration**: Once the lease expires, the node is transitioned to a special `shadow_probe` delivery state.
398
+ 3. **Shadow Probe**: While in `shadow_probe`, the node is evaluated silently behind the scenes. If the agent finishes tasks using this guidance without any new harm, a **no-harm pass counter** is incremented.
399
+ 4. **Conservative Restoration**: Upon successfully passing the shadow probe, the node is restored to `conservative_only` delivery (rather than direct live eligibility).
400
+ 5. **Repeated-Harm Retirement**: If the node causes repeated harm during its shadow probe or live recovery, it is permanently retired.
401
+ 6. **Preservation of History**: Throughout this cycle, historical helped/harmed counts, original delivery states, and release attempt logs are strictly preserved.
402
+
369
403
  ## How MCP Interaction Works
370
404
 
371
405
  For `Codex` and `Claude Code`, ExperienceEngine is designed to keep routine review and management inside the host session first.
@@ -531,6 +565,13 @@ Notes:
531
565
  - the recommended registry for managed model downloads is `https://registry.npmjs.org`
532
566
  - `ee doctor ...` reports a first-value readiness summary so users can see how much captured evidence exists before the first durable node is promoted
533
567
 
568
+ ### Offline Profiles, Manifest Health, and Vector Migration
569
+
570
+ For air-gapped or sensitive environments, ExperienceEngine supports fully offline semantic retrieval:
571
+ - **Offline Profiles**: Set `embeddingProvider = "local"` and configure an offline profile (such as `strict-offline`) in settings. ExperienceEngine will rely entirely on local ONNX model assets.
572
+ - **Manifest Health Verification**: When running `ee doctor`, the system validates the health of the local offline profile. It reads the model manifest files, checks checksums, and detects corrupted assets. If a strict-offline profile is active but files are missing or corrupt, a warning is raised automatically.
573
+ - **Vector Migration**: Upgrading embedding models or altering dimensions requires vector migration. ExperienceEngine tracks vector migration status for every node (`migrated`, `pending`, etc.) along with timestamps and migration errors. You can inspect this status via `ee inspect node:<id>` or `ee doctor`.
574
+
534
575
  Maintenance:
535
576
 
536
577
  ```bash
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "experienceengine",
3
3
  "name": "ExperienceEngine",
4
- "version": "0.3.6",
4
+ "version": "0.4.0",
5
5
  "description": "Context-aware experience intervention controller for coding and debugging tasks.",
6
6
  "configSchema": {
7
7
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alan512/experienceengine",
3
- "version": "0.3.6",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Experience governance for coding agents: learn from real task outcomes, inject reusable hints, and retire low-value guidance.",
6
6
  "license": "MIT",