@equationalapplications/core-llm-wiki 5.3.1 → 5.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.
@@ -1,3 +1,4 @@
1
+ import { OkfSource, OkfVerifiedEntry, OkfSourceUsageWindow } from '@equationalapplications/core-okf';
1
2
  import { SearchResult } from 'minisearch';
2
3
 
3
4
  /**
@@ -250,6 +251,38 @@ interface WikiFact {
250
251
  * OKF import. Distinct from `source_type`, which governs immutability rules.
251
252
  */
252
253
  okf_type?: string | null;
254
+ /** OKF v0.2 lifecycle state. Defaults to 'stable' on the SQL side. */
255
+ lifecycle_status?: 'draft' | 'stable' | 'deprecated';
256
+ /** Absolute YYYY-MM-DD cutoff for staleness. NULL = never stale. */
257
+ stale_after?: number | null;
258
+ /** Actor string per OKF v0.2 §7 (`<producer>/<version>`, `human:<id>`, `process:<id>`). */
259
+ generated_by?: string | null;
260
+ /** Full list of OKF v0.2 sources (provenance). */
261
+ okf_sources?: OkfSource[];
262
+ /** Chronological list of verification events. */
263
+ okf_verified?: OkfVerifiedEntry[];
264
+ /** Sibling-of-sources usage window object. */
265
+ okf_usage_window?: OkfSourceUsageWindow | null;
266
+ /** Convenience: epoch ms of the latest verifier. Derived; mirrored for query speed. */
267
+ last_verified_at?: number | null;
268
+ /** Convenience: actor string of the latest verifier. Derived; mirrored for query speed. */
269
+ last_verified_by?: string | null;
270
+ /**
271
+ * Hydrated at read time: `isStaleAfter(stale_after, now)`. `true` once the
272
+ * absolute stale_after cutoff has passed; `false` otherwise (including when
273
+ * stale_after is null). Surfaced per spec §2.7 + §5.3 so hosts can render
274
+ * staleness without recomputing. Always present on read paths that go
275
+ * through `mapRowToFact` / `mapRowToTask`.
276
+ */
277
+ isStale?: boolean;
278
+ /**
279
+ * Hydrated at read time: `deriveTrustTier(okf_verified)`. `'human-reviewed'`
280
+ * when any entry has `by: 'human:...'`, `'machine-confirmed'` when entries
281
+ * exist without a human verifier, `'unverified'` when okf_verified is empty.
282
+ * Surfaced per spec §2.7 + §5.3. Always present on read paths that go through
283
+ * `mapRowToFact` / `mapRowToTask`.
284
+ */
285
+ trustTier?: 'unverified' | 'machine-confirmed' | 'human-reviewed';
253
286
  }
254
287
  interface WikiTask {
255
288
  id: string;
@@ -263,6 +296,27 @@ interface WikiTask {
263
296
  deleted_at: number | null;
264
297
  /** Verbatim OKF `type:` frontmatter value when this task originated from an OKF bundle. */
265
298
  okf_type?: string | null;
299
+ lifecycle_status?: 'draft' | 'stable' | 'deprecated';
300
+ stale_after?: number | null;
301
+ generated_by?: string | null;
302
+ okf_sources?: OkfSource[];
303
+ okf_verified?: OkfVerifiedEntry[];
304
+ okf_usage_window?: OkfSourceUsageWindow | null;
305
+ last_verified_at?: number | null;
306
+ last_verified_by?: string | null;
307
+ /**
308
+ * Hydrated at read time: `isStaleAfter(stale_after, now)`. Mirrors
309
+ * {@link WikiFact.isStale} (spec §2.5 + §5.3 treat stale_after symmetrically
310
+ * for tasks and facts). Always present on read paths that go through
311
+ * `mapRowToTask`.
312
+ */
313
+ isStale?: boolean;
314
+ /**
315
+ * Hydrated at read time: `deriveTrustTier(okf_verified)`. Mirrors
316
+ * {@link WikiFact.trustTier}. Always present on read paths that go through
317
+ * `mapRowToTask`.
318
+ */
319
+ trustTier?: 'unverified' | 'machine-confirmed' | 'human-reviewed';
266
320
  }
267
321
  interface WikiEvent {
268
322
  id: string;
@@ -697,6 +751,21 @@ declare class EntryRepository extends BaseRepository {
697
751
  * Upsert a WikiFact. Nullable fields set to null when fact value is null.
698
752
  * Returns { changes, lastInsertRowId }.
699
753
  * `tx` is REQUIRED to ensure atomic outbox staging.
754
+ *
755
+ * OKF metadata discipline (spec §2.5 + DAO checklist §8): content writes
756
+ * MUST NOT silently overwrite OKF metadata that a caller didn't supply. The
757
+ * previous CASE WHEN IS NULL guards around `lifecycle_status`, etc. were
758
+ * dead because the bind always supplied a non-null value (notably
759
+ * `fact.lifecycle_status ?? 'stable'`), so excluded.X was never NULL and
760
+ * an UPDATE rewrote an existing `deprecated` row to `stable`. We now omit
761
+ * these columns from the SET clause entirely — SQLite's ON CONFLICT UPDATE
762
+ * leaves untouched columns at their existing row value, which is exactly
763
+ * the preservation semantics we want for content writes. Callers that
764
+ * intentionally want to overwrite OKF metadata should use
765
+ * {@link upsertForImport} (always-replace semantics for that path).
766
+ * New-row INSERTs still bind a value so the column is populated; if the
767
+ * caller supplied nothing, the schema DEFAULT 'stable' applies when the
768
+ * bind is bound as 'stable' explicitly here.
700
769
  */
701
770
  upsert(fact: WikiFact, tx: SQLiteAdapter): Promise<{
702
771
  changes: number;
@@ -1494,6 +1563,12 @@ declare class TaskRepository extends BaseRepository {
1494
1563
  * Uses ON CONFLICT(id) DO UPDATE (not INSERT OR REPLACE).
1495
1564
  * Stages an outbox entry in the same transaction.
1496
1565
  * `tx` is REQUIRED.
1566
+ *
1567
+ * OKF metadata (lifecycle_status, sources, etc.) is intentionally excluded
1568
+ * from the SET clause so content writes preserve any existing row values.
1569
+ * See {@link EntryRepository.upsert} for the rationale — the same applies
1570
+ * symmetrically to tasks (spec §2.5). Callers that need to overwrite OKF
1571
+ * metadata should use {@link upsertForImport}.
1497
1572
  */
1498
1573
  upsert(task: WikiTask, tx: SQLiteAdapter, updatedAt?: number): Promise<void>;
1499
1574
  upsertForImport(task: WikiTask, tx: SQLiteAdapter, updatedAt?: number): Promise<void>;
@@ -1835,6 +1910,7 @@ declare class WikiMemory {
1835
1910
  private promptService;
1836
1911
  private ontologyService;
1837
1912
  private graphTraversalService;
1913
+ private readonly okfTrustWrites;
1838
1914
  constructor(db: SQLiteAdapter, options: WikiOptions);
1839
1915
  /**
1840
1916
  * Explicit escape hatch for test suites: typed access to composed services for mocks/spies.
@@ -2101,6 +2177,34 @@ declare class WikiMemory {
2101
2177
  setOntologyManifest(entityId: string, manifest: OntologyManifest, options?: {
2102
2178
  mode?: OntologyMode;
2103
2179
  }): Promise<void>;
2180
+ /** Append a verification event to a fact. Does NOT touch `updated_at`. */
2181
+ writeOkfTrust(entryId: string, entityId: string, verified: {
2182
+ by: string;
2183
+ at: string;
2184
+ }[]): Promise<void>;
2185
+ /** Replace a fact's source list. Does NOT touch `updated_at`. */
2186
+ writeOkfSources(entryId: string, entityId: string, sources: Array<{
2187
+ resource: string;
2188
+ [k: string]: unknown;
2189
+ }>): Promise<void>;
2190
+ /** Set a fact's OKF v0.2 lifecycle status. Does NOT touch `updated_at`. */
2191
+ setLifecycleStatus(entryId: string, entityId: string, status: 'draft' | 'stable' | 'deprecated'): Promise<void>;
2192
+ /** Set a fact's stale_after (epoch ms) or clear it. Does NOT touch `updated_at`. */
2193
+ setStaleAfter(entryId: string, entityId: string, date: number | null): Promise<void>;
2194
+ /** Set a fact's generated_by actor string. Does NOT touch `updated_at`. */
2195
+ setGeneratedBy(entryId: string, entityId: string, actor: string): Promise<void>;
2196
+ /** Task variants of the same five DAO methods. Symmetric per spec §2.5. */
2197
+ writeOkfTrustTask(taskId: string, entityId: string, verified: {
2198
+ by: string;
2199
+ at: string;
2200
+ }[]): Promise<void>;
2201
+ writeOkfSourcesTask(taskId: string, entityId: string, sources: Array<{
2202
+ resource: string;
2203
+ [k: string]: unknown;
2204
+ }>): Promise<void>;
2205
+ setLifecycleStatusTask(taskId: string, entityId: string, status: 'draft' | 'stable' | 'deprecated'): Promise<void>;
2206
+ setStaleAfterTask(taskId: string, entityId: string, date: number | null): Promise<void>;
2207
+ setGeneratedByTask(taskId: string, entityId: string, actor: string): Promise<void>;
2104
2208
  }
2105
2209
 
2106
2210
  export { ImportExportService as $, WikiBusyError as A, type WikiBusyOperation as B, type WikiCheckpoint as C, type WikiConfig as D, type EntityStatus as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, WikiDuplicateHashError as I, type WikiEdge as J, type WikiEvent as K, type LLMProvider as L, type MemoryBundle as M, type WikiFact as N, type OntologyManifest as O, type PromptOverrides as P, type WikiMemoryTestAccess as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiOutboxEvent as T, WikiSourceRefHashCollision as U, type VectorRanker as V, type WikiOptions as W, WikiStrictOntologyViolation as X, type WikiTask as Y, WikiTransactionError as Z, EmbeddingService as _, type MemoryDump as a, IngestionService as a0, JobManager as a1, MaintenanceService as a2, RetrievalService as a3, SearchService as a4, WriteService as a5, type FormattedMemoryDump as b, WikiMemory as c, type ExtractedFact as d, type ExtractedFactEdge as e, type ExtractedFactWithOntology as f, type ExtractedTask as g, type GraphTraversalOptions as h, HEAL_RECHECK_MS as i, HOOK_TIMEOUT_MARKER as j, type HealResult as k, ONTOLOGY_BACKFILL_BATCH_SIZE as l, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as m, ONTOLOGY_BACKFILL_RECHECK_MS as n, type OntologyBackfillResult as o, type OntologyConfig as p, type OntologyEdgeType as q, type OntologyMode as r, type OntologyNodeType as s, type OntologyPromptContext as t, type OntologyUpdates as u, PromptService as v, PrunePartialFailureError as w, type VectorRankerFallback as x, type VectorRankerRankArgs as y, type VectorRankerSemanticResult as z };
@@ -1,3 +1,4 @@
1
+ import { OkfSource, OkfVerifiedEntry, OkfSourceUsageWindow } from '@equationalapplications/core-okf';
1
2
  import { SearchResult } from 'minisearch';
2
3
 
3
4
  /**
@@ -250,6 +251,38 @@ interface WikiFact {
250
251
  * OKF import. Distinct from `source_type`, which governs immutability rules.
251
252
  */
252
253
  okf_type?: string | null;
254
+ /** OKF v0.2 lifecycle state. Defaults to 'stable' on the SQL side. */
255
+ lifecycle_status?: 'draft' | 'stable' | 'deprecated';
256
+ /** Absolute YYYY-MM-DD cutoff for staleness. NULL = never stale. */
257
+ stale_after?: number | null;
258
+ /** Actor string per OKF v0.2 §7 (`<producer>/<version>`, `human:<id>`, `process:<id>`). */
259
+ generated_by?: string | null;
260
+ /** Full list of OKF v0.2 sources (provenance). */
261
+ okf_sources?: OkfSource[];
262
+ /** Chronological list of verification events. */
263
+ okf_verified?: OkfVerifiedEntry[];
264
+ /** Sibling-of-sources usage window object. */
265
+ okf_usage_window?: OkfSourceUsageWindow | null;
266
+ /** Convenience: epoch ms of the latest verifier. Derived; mirrored for query speed. */
267
+ last_verified_at?: number | null;
268
+ /** Convenience: actor string of the latest verifier. Derived; mirrored for query speed. */
269
+ last_verified_by?: string | null;
270
+ /**
271
+ * Hydrated at read time: `isStaleAfter(stale_after, now)`. `true` once the
272
+ * absolute stale_after cutoff has passed; `false` otherwise (including when
273
+ * stale_after is null). Surfaced per spec §2.7 + §5.3 so hosts can render
274
+ * staleness without recomputing. Always present on read paths that go
275
+ * through `mapRowToFact` / `mapRowToTask`.
276
+ */
277
+ isStale?: boolean;
278
+ /**
279
+ * Hydrated at read time: `deriveTrustTier(okf_verified)`. `'human-reviewed'`
280
+ * when any entry has `by: 'human:...'`, `'machine-confirmed'` when entries
281
+ * exist without a human verifier, `'unverified'` when okf_verified is empty.
282
+ * Surfaced per spec §2.7 + §5.3. Always present on read paths that go through
283
+ * `mapRowToFact` / `mapRowToTask`.
284
+ */
285
+ trustTier?: 'unverified' | 'machine-confirmed' | 'human-reviewed';
253
286
  }
254
287
  interface WikiTask {
255
288
  id: string;
@@ -263,6 +296,27 @@ interface WikiTask {
263
296
  deleted_at: number | null;
264
297
  /** Verbatim OKF `type:` frontmatter value when this task originated from an OKF bundle. */
265
298
  okf_type?: string | null;
299
+ lifecycle_status?: 'draft' | 'stable' | 'deprecated';
300
+ stale_after?: number | null;
301
+ generated_by?: string | null;
302
+ okf_sources?: OkfSource[];
303
+ okf_verified?: OkfVerifiedEntry[];
304
+ okf_usage_window?: OkfSourceUsageWindow | null;
305
+ last_verified_at?: number | null;
306
+ last_verified_by?: string | null;
307
+ /**
308
+ * Hydrated at read time: `isStaleAfter(stale_after, now)`. Mirrors
309
+ * {@link WikiFact.isStale} (spec §2.5 + §5.3 treat stale_after symmetrically
310
+ * for tasks and facts). Always present on read paths that go through
311
+ * `mapRowToTask`.
312
+ */
313
+ isStale?: boolean;
314
+ /**
315
+ * Hydrated at read time: `deriveTrustTier(okf_verified)`. Mirrors
316
+ * {@link WikiFact.trustTier}. Always present on read paths that go through
317
+ * `mapRowToTask`.
318
+ */
319
+ trustTier?: 'unverified' | 'machine-confirmed' | 'human-reviewed';
266
320
  }
267
321
  interface WikiEvent {
268
322
  id: string;
@@ -697,6 +751,21 @@ declare class EntryRepository extends BaseRepository {
697
751
  * Upsert a WikiFact. Nullable fields set to null when fact value is null.
698
752
  * Returns { changes, lastInsertRowId }.
699
753
  * `tx` is REQUIRED to ensure atomic outbox staging.
754
+ *
755
+ * OKF metadata discipline (spec §2.5 + DAO checklist §8): content writes
756
+ * MUST NOT silently overwrite OKF metadata that a caller didn't supply. The
757
+ * previous CASE WHEN IS NULL guards around `lifecycle_status`, etc. were
758
+ * dead because the bind always supplied a non-null value (notably
759
+ * `fact.lifecycle_status ?? 'stable'`), so excluded.X was never NULL and
760
+ * an UPDATE rewrote an existing `deprecated` row to `stable`. We now omit
761
+ * these columns from the SET clause entirely — SQLite's ON CONFLICT UPDATE
762
+ * leaves untouched columns at their existing row value, which is exactly
763
+ * the preservation semantics we want for content writes. Callers that
764
+ * intentionally want to overwrite OKF metadata should use
765
+ * {@link upsertForImport} (always-replace semantics for that path).
766
+ * New-row INSERTs still bind a value so the column is populated; if the
767
+ * caller supplied nothing, the schema DEFAULT 'stable' applies when the
768
+ * bind is bound as 'stable' explicitly here.
700
769
  */
701
770
  upsert(fact: WikiFact, tx: SQLiteAdapter): Promise<{
702
771
  changes: number;
@@ -1494,6 +1563,12 @@ declare class TaskRepository extends BaseRepository {
1494
1563
  * Uses ON CONFLICT(id) DO UPDATE (not INSERT OR REPLACE).
1495
1564
  * Stages an outbox entry in the same transaction.
1496
1565
  * `tx` is REQUIRED.
1566
+ *
1567
+ * OKF metadata (lifecycle_status, sources, etc.) is intentionally excluded
1568
+ * from the SET clause so content writes preserve any existing row values.
1569
+ * See {@link EntryRepository.upsert} for the rationale — the same applies
1570
+ * symmetrically to tasks (spec §2.5). Callers that need to overwrite OKF
1571
+ * metadata should use {@link upsertForImport}.
1497
1572
  */
1498
1573
  upsert(task: WikiTask, tx: SQLiteAdapter, updatedAt?: number): Promise<void>;
1499
1574
  upsertForImport(task: WikiTask, tx: SQLiteAdapter, updatedAt?: number): Promise<void>;
@@ -1835,6 +1910,7 @@ declare class WikiMemory {
1835
1910
  private promptService;
1836
1911
  private ontologyService;
1837
1912
  private graphTraversalService;
1913
+ private readonly okfTrustWrites;
1838
1914
  constructor(db: SQLiteAdapter, options: WikiOptions);
1839
1915
  /**
1840
1916
  * Explicit escape hatch for test suites: typed access to composed services for mocks/spies.
@@ -2101,6 +2177,34 @@ declare class WikiMemory {
2101
2177
  setOntologyManifest(entityId: string, manifest: OntologyManifest, options?: {
2102
2178
  mode?: OntologyMode;
2103
2179
  }): Promise<void>;
2180
+ /** Append a verification event to a fact. Does NOT touch `updated_at`. */
2181
+ writeOkfTrust(entryId: string, entityId: string, verified: {
2182
+ by: string;
2183
+ at: string;
2184
+ }[]): Promise<void>;
2185
+ /** Replace a fact's source list. Does NOT touch `updated_at`. */
2186
+ writeOkfSources(entryId: string, entityId: string, sources: Array<{
2187
+ resource: string;
2188
+ [k: string]: unknown;
2189
+ }>): Promise<void>;
2190
+ /** Set a fact's OKF v0.2 lifecycle status. Does NOT touch `updated_at`. */
2191
+ setLifecycleStatus(entryId: string, entityId: string, status: 'draft' | 'stable' | 'deprecated'): Promise<void>;
2192
+ /** Set a fact's stale_after (epoch ms) or clear it. Does NOT touch `updated_at`. */
2193
+ setStaleAfter(entryId: string, entityId: string, date: number | null): Promise<void>;
2194
+ /** Set a fact's generated_by actor string. Does NOT touch `updated_at`. */
2195
+ setGeneratedBy(entryId: string, entityId: string, actor: string): Promise<void>;
2196
+ /** Task variants of the same five DAO methods. Symmetric per spec §2.5. */
2197
+ writeOkfTrustTask(taskId: string, entityId: string, verified: {
2198
+ by: string;
2199
+ at: string;
2200
+ }[]): Promise<void>;
2201
+ writeOkfSourcesTask(taskId: string, entityId: string, sources: Array<{
2202
+ resource: string;
2203
+ [k: string]: unknown;
2204
+ }>): Promise<void>;
2205
+ setLifecycleStatusTask(taskId: string, entityId: string, status: 'draft' | 'stable' | 'deprecated'): Promise<void>;
2206
+ setStaleAfterTask(taskId: string, entityId: string, date: number | null): Promise<void>;
2207
+ setGeneratedByTask(taskId: string, entityId: string, actor: string): Promise<void>;
2104
2208
  }
2105
2209
 
2106
2210
  export { ImportExportService as $, WikiBusyError as A, type WikiBusyOperation as B, type WikiCheckpoint as C, type WikiConfig as D, type EntityStatus as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, WikiDuplicateHashError as I, type WikiEdge as J, type WikiEvent as K, type LLMProvider as L, type MemoryBundle as M, type WikiFact as N, type OntologyManifest as O, type PromptOverrides as P, type WikiMemoryTestAccess as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiOutboxEvent as T, WikiSourceRefHashCollision as U, type VectorRanker as V, type WikiOptions as W, WikiStrictOntologyViolation as X, type WikiTask as Y, WikiTransactionError as Z, EmbeddingService as _, type MemoryDump as a, IngestionService as a0, JobManager as a1, MaintenanceService as a2, RetrievalService as a3, SearchService as a4, WriteService as a5, type FormattedMemoryDump as b, WikiMemory as c, type ExtractedFact as d, type ExtractedFactEdge as e, type ExtractedFactWithOntology as f, type ExtractedTask as g, type GraphTraversalOptions as h, HEAL_RECHECK_MS as i, HOOK_TIMEOUT_MARKER as j, type HealResult as k, ONTOLOGY_BACKFILL_BATCH_SIZE as l, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as m, ONTOLOGY_BACKFILL_RECHECK_MS as n, type OntologyBackfillResult as o, type OntologyConfig as p, type OntologyEdgeType as q, type OntologyMode as r, type OntologyNodeType as s, type OntologyPromptContext as t, type OntologyUpdates as u, PromptService as v, PrunePartialFailureError as w, type VectorRankerFallback as x, type VectorRankerRankArgs as y, type VectorRankerSemanticResult as z };
@@ -1,2 +1,3 @@
1
- export { _ as EmbeddingService, $ as ImportExportService, a0 as IngestionService, a1 as JobManager, a1 as JobManagerType, a2 as MaintenanceService, a3 as RetrievalService, a4 as SearchService, a4 as SearchServiceType, Q as WikiMemoryTestAccess, a5 as WriteService } from './testing-zf3Jp_RL.mjs';
1
+ export { _ as EmbeddingService, $ as ImportExportService, a0 as IngestionService, a1 as JobManager, a1 as JobManagerType, a2 as MaintenanceService, a3 as RetrievalService, a4 as SearchService, a4 as SearchServiceType, Q as WikiMemoryTestAccess, a5 as WriteService } from './testing-C71mnMxq.mjs';
2
+ import '@equationalapplications/core-okf';
2
3
  import 'minisearch';
package/dist/testing.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- export { _ as EmbeddingService, $ as ImportExportService, a0 as IngestionService, a1 as JobManager, a1 as JobManagerType, a2 as MaintenanceService, a3 as RetrievalService, a4 as SearchService, a4 as SearchServiceType, Q as WikiMemoryTestAccess, a5 as WriteService } from './testing-zf3Jp_RL.js';
1
+ export { _ as EmbeddingService, $ as ImportExportService, a0 as IngestionService, a1 as JobManager, a1 as JobManagerType, a2 as MaintenanceService, a3 as RetrievalService, a4 as SearchService, a4 as SearchServiceType, Q as WikiMemoryTestAccess, a5 as WriteService } from './testing-C71mnMxq.js';
2
+ import '@equationalapplications/core-okf';
2
3
  import 'minisearch';
package/dist/testing.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var coreOkf = require('@equationalapplications/core-okf');
3
4
  var MiniSearch = require('minisearch');
4
5
 
5
6
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -459,8 +460,6 @@ function normalizeTitleKey(title) {
459
460
  function entitySummaryMetaKey(entityId) {
460
461
  return `entity_summary:${entityId}`;
461
462
  }
462
-
463
- // src/services/ImportExportService.ts
464
463
  var MAX_EMBEDDING_BLOB_BYTES = 32 * 1024;
465
464
  var IMPORT_TITLE_MAX = 500;
466
465
  var IMPORT_BODY_MAX = 8e3;
@@ -680,7 +679,23 @@ var ImportExportService = class {
680
679
  access_count: fact.access_count,
681
680
  deleted_at: fact.deleted_at,
682
681
  embedding_blob: blobData ?? void 0,
683
- okf_type: fact.okf_type ?? null
682
+ okf_type: fact.okf_type ?? null,
683
+ // OKF v0.2 — forwarded as-is; parseOkfBundle / the caller is responsible for
684
+ // populating these per spec, importDump does not derive or validate them.
685
+ lifecycle_status: fact.lifecycle_status ?? "stable",
686
+ stale_after: fact.stale_after ?? null,
687
+ generated_by: fact.generated_by ?? null,
688
+ okf_sources: fact.okf_sources ?? [],
689
+ okf_verified: fact.okf_verified ?? [],
690
+ okf_usage_window: fact.okf_usage_window ?? null,
691
+ // last_verified_at/_by are DERIVED mirrors of `okf_verified` per spec
692
+ // §5.3. Recomputing here (instead of trusting the payload field) closes
693
+ // the invariant that a raw importDump cannot carry a
694
+ // last_verified_by that contradicts its own okf_verified list.
695
+ ...(() => {
696
+ const latest = coreOkf.latestVerified(fact.okf_verified ?? [], Date.now());
697
+ return { last_verified_at: latest?.at ?? null, last_verified_by: latest?.by ?? null };
698
+ })()
684
699
  };
685
700
  await this.entryRepo.upsertForImport(factObj, tx);
686
701
  if (blobData != null) {
@@ -730,7 +745,19 @@ var ImportExportService = class {
730
745
  updated_at: safeUpdatedAt,
731
746
  resolved_at: task.resolved_at,
732
747
  deleted_at: task.deleted_at,
733
- okf_type: task.okf_type ?? null
748
+ okf_type: task.okf_type ?? null,
749
+ // OKF v0.2
750
+ lifecycle_status: task.lifecycle_status ?? "stable",
751
+ stale_after: task.stale_after ?? null,
752
+ generated_by: task.generated_by ?? null,
753
+ okf_sources: task.okf_sources ?? [],
754
+ okf_verified: task.okf_verified ?? [],
755
+ okf_usage_window: task.okf_usage_window ?? null,
756
+ // Derived mirrors — see fact branch above; same invariant.
757
+ ...(() => {
758
+ const latest = coreOkf.latestVerified(task.okf_verified ?? [], Date.now());
759
+ return { last_verified_at: latest?.at ?? null, last_verified_by: latest?.by ?? null };
760
+ })()
734
761
  },
735
762
  tx,
736
763
  safeUpdatedAt