@equationalapplications/core-llm-wiki 5.3.1 → 5.5.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;
@@ -619,6 +673,79 @@ declare class PrunePartialFailureError extends Error {
619
673
  constructor(deleted: number, failedAt: string, remaining: number, cause: Error, deletedTasks?: number, deletedEvents?: number);
620
674
  }
621
675
  declare const HOOK_TIMEOUT_MARKER: unique symbol;
676
+ /**
677
+ * Failure record for a single chunk that could not be ingested. Produced by
678
+ * `IngestionService.ingestDocument` and surfaced on the result's `parseFailures`
679
+ * array. `source === 'parse'` for `WikiParseError`-shaped failures (then `tier`
680
+ * is set); `source === 'llm'` for `generateText` rejections (no `tier`).
681
+ *
682
+ * Note: `'ontologyContext'` is reserved but NEVER produced. `buildPromptContext`
683
+ * failures are DB-systemic and propagate as raw throws, not as `parseFailures`.
684
+ */
685
+ interface ChunkFailure {
686
+ chunkIndex: number;
687
+ sourceRef: string;
688
+ source: 'parse' | 'llm';
689
+ tier?: 'strict' | 'repair' | 'all';
690
+ position: number | null;
691
+ message: string;
692
+ }
693
+ /**
694
+ * Result of `IngestionService.ingestDocument`. The shape WIDENS relative to
695
+ * earlier versions: `ingestedChunks` and `failedChunks` are always present;
696
+ * `parseFailures` is present iff at least one chunk failed.
697
+ *
698
+ * Hosts that destructure `{ truncated, chunks }` see no behavior change.
699
+ * Hosts pattern-matching the entire shape must update to the new fields.
700
+ */
701
+ interface IngestDocumentResult {
702
+ truncated: boolean;
703
+ chunks: number;
704
+ ingestedChunks: number;
705
+ failedChunks: number;
706
+ duplicateOf?: string;
707
+ parseFailures?: ChunkFailure[];
708
+ }
709
+ /**
710
+ * Thrown by `parseJsonResponse` when both the strict scanner pass and the
711
+ * container-aware repair pass fail to produce a parsable candidate.
712
+ *
713
+ * - `tier: 'strict'` — the existing scanner produced no usable slice (no
714
+ * `{`/`[` at all, or no balanced close). `slice` is the full input text;
715
+ * `position` is the offset of the first `{`/`[`, or `null` when none found.
716
+ * - `tier: 'repair'` — tier 1 found a slice but `JSON.parse` failed; tier 2
717
+ * produced no parsable candidate within `MAX_REPAIR_CANDIDATES`. `slice` is
718
+ * the largest balanced span the walker found; `position` is the offset of
719
+ * the first parse failure within that candidate when known.
720
+ * - `tier: 'all'` — generic catch-all.
721
+ */
722
+ declare class WikiParseError extends Error {
723
+ readonly tier: 'strict' | 'repair' | 'all';
724
+ readonly position: number | null;
725
+ readonly slice: string;
726
+ constructor(message: string, opts: {
727
+ tier: WikiParseError['tier'];
728
+ position?: number | null;
729
+ slice?: string;
730
+ });
731
+ }
732
+ /**
733
+ * Thrown by `IngestionService.ingestDocument` when every chunk of a document
734
+ * failed (either `parseJsonResponse` or `generateText`). A silent zero-fact
735
+ * ingest is a worse regression than a typed throw — matches existing host
736
+ * semantics (`aws-cloud-agent` Writer Lambda today sees `ingestDocument`
737
+ * throw on any failure).
738
+ */
739
+ declare class WikiIngestEmptyError extends Error {
740
+ readonly parseFailures: ChunkFailure[];
741
+ readonly sourceRef: string;
742
+ readonly chunks: number;
743
+ constructor(params: {
744
+ parseFailures: ChunkFailure[];
745
+ sourceRef: string;
746
+ chunks: number;
747
+ });
748
+ }
622
749
 
623
750
  interface WikiOutboxEvent<T = unknown> {
624
751
  id: string;
@@ -697,6 +824,21 @@ declare class EntryRepository extends BaseRepository {
697
824
  * Upsert a WikiFact. Nullable fields set to null when fact value is null.
698
825
  * Returns { changes, lastInsertRowId }.
699
826
  * `tx` is REQUIRED to ensure atomic outbox staging.
827
+ *
828
+ * OKF metadata discipline (spec §2.5 + DAO checklist §8): content writes
829
+ * MUST NOT silently overwrite OKF metadata that a caller didn't supply. The
830
+ * previous CASE WHEN IS NULL guards around `lifecycle_status`, etc. were
831
+ * dead because the bind always supplied a non-null value (notably
832
+ * `fact.lifecycle_status ?? 'stable'`), so excluded.X was never NULL and
833
+ * an UPDATE rewrote an existing `deprecated` row to `stable`. We now omit
834
+ * these columns from the SET clause entirely — SQLite's ON CONFLICT UPDATE
835
+ * leaves untouched columns at their existing row value, which is exactly
836
+ * the preservation semantics we want for content writes. Callers that
837
+ * intentionally want to overwrite OKF metadata should use
838
+ * {@link upsertForImport} (always-replace semantics for that path).
839
+ * New-row INSERTs still bind a value so the column is populated; if the
840
+ * caller supplied nothing, the schema DEFAULT 'stable' applies when the
841
+ * bind is bound as 'stable' explicitly here.
700
842
  */
701
843
  upsert(fact: WikiFact, tx: SQLiteAdapter): Promise<{
702
844
  changes: number;
@@ -1422,11 +1564,7 @@ declare class IngestionService {
1422
1564
  promptOverride?: string;
1423
1565
  }, opts?: {
1424
1566
  onDuplicateHash?: 'ingest' | 'skip' | 'throw';
1425
- }): Promise<{
1426
- truncated: boolean;
1427
- chunks: number;
1428
- duplicateOf?: string;
1429
- }>;
1567
+ }): Promise<IngestDocumentResult>;
1430
1568
  /**
1431
1569
  * Implements spec §2 data flow steps a–j with the host-facing
1432
1570
  * `{sourceRef, sourceHash, nodes, edges}` parameter shape. Shared between
@@ -1470,6 +1608,38 @@ declare class IngestionService {
1470
1608
  edgesWritten: number;
1471
1609
  superseded: number;
1472
1610
  }>;
1611
+ /**
1612
+ * Full supersession + ownership path: identical to the pre-issue-#92 behavior
1613
+ * for the happy path. Called from the `failedChunks === 0` branch of
1614
+ * `ingestDocument`. Runs INSIDE the caller's tx.
1615
+ *
1616
+ * Returns `{ deletedSourceFactIds, insertedFacts }` so the caller can fire
1617
+ * post-commit hooks (embedding lifecycle) AFTER the transaction commits.
1618
+ * On the partial path the caller never invokes this method; the empty
1619
+ * arrays stay empty.
1620
+ */
1621
+ private runFullUpsertGraph;
1622
+ /**
1623
+ * Partial-commit path for `ingestDocument`. Inserts ONLY facts whose
1624
+ * normalized title is not already in the live set for this (entityId,
1625
+ * sourceRef). Does NOT call `entryRepo.softDeleteBySource` (no
1626
+ * supersession) and does NOT call `sourceRefIndexRepo.upsert` (no
1627
+ * ownership). Edges are NOT resolved on this path (no ontology-context
1628
+ * build, no `resolveEdges`, no `mergeEmergentUpdates`). Stale edges from
1629
+ * prior attempts are recovered on the next full run's supersession.
1630
+ *
1631
+ * Source-hash is stored as NULL on partial rows. `findLatestSourceHash`
1632
+ * reads from the most recently updated live row for the sourceRef, so
1633
+ * storing the incoming hash here would cause `hasChanged` to return
1634
+ * `false` on a same-hash retry — the failed chunks would never get a
1635
+ * second chance. With NULL, a retry sees `storedHash === null`, `hasChanged`
1636
+ * returns `true`, and the partial commit's sibling rows remain live
1637
+ * (deduped, not superseded) for the next attempt to extend.
1638
+ *
1639
+ * Runs INSIDE the caller's `tx`. Does not open a nested transaction.
1640
+ * Returns `{ inserted, skippedDuplicate }` for observability.
1641
+ */
1642
+ private appendPartialFacts;
1473
1643
  }
1474
1644
 
1475
1645
  declare class TaskRepository extends BaseRepository {
@@ -1494,6 +1664,12 @@ declare class TaskRepository extends BaseRepository {
1494
1664
  * Uses ON CONFLICT(id) DO UPDATE (not INSERT OR REPLACE).
1495
1665
  * Stages an outbox entry in the same transaction.
1496
1666
  * `tx` is REQUIRED.
1667
+ *
1668
+ * OKF metadata (lifecycle_status, sources, etc.) is intentionally excluded
1669
+ * from the SET clause so content writes preserve any existing row values.
1670
+ * See {@link EntryRepository.upsert} for the rationale — the same applies
1671
+ * symmetrically to tasks (spec §2.5). Callers that need to overwrite OKF
1672
+ * metadata should use {@link upsertForImport}.
1497
1673
  */
1498
1674
  upsert(task: WikiTask, tx: SQLiteAdapter, updatedAt?: number): Promise<void>;
1499
1675
  upsertForImport(task: WikiTask, tx: SQLiteAdapter, updatedAt?: number): Promise<void>;
@@ -1835,6 +2011,7 @@ declare class WikiMemory {
1835
2011
  private promptService;
1836
2012
  private ontologyService;
1837
2013
  private graphTraversalService;
2014
+ private readonly okfTrustWrites;
1838
2015
  constructor(db: SQLiteAdapter, options: WikiOptions);
1839
2016
  /**
1840
2017
  * Explicit escape hatch for test suites: typed access to composed services for mocks/spies.
@@ -2022,11 +2199,7 @@ declare class WikiMemory {
2022
2199
  promptOverride?: string;
2023
2200
  }, opts?: {
2024
2201
  onDuplicateHash?: 'ingest' | 'skip' | 'throw';
2025
- }): Promise<{
2026
- truncated: boolean;
2027
- chunks: number;
2028
- duplicateOf?: string;
2029
- }>;
2202
+ }): Promise<IngestDocumentResult>;
2030
2203
  /**
2031
2204
  * Deterministic write path into the graph. Accepts caller-supplied
2032
2205
  * `(nodes, edges)` and writes them under `(sourceRef, sourceHash)`
@@ -2101,6 +2274,34 @@ declare class WikiMemory {
2101
2274
  setOntologyManifest(entityId: string, manifest: OntologyManifest, options?: {
2102
2275
  mode?: OntologyMode;
2103
2276
  }): Promise<void>;
2277
+ /** Append a verification event to a fact. Does NOT touch `updated_at`. */
2278
+ writeOkfTrust(entryId: string, entityId: string, verified: {
2279
+ by: string;
2280
+ at: string;
2281
+ }[]): Promise<void>;
2282
+ /** Replace a fact's source list. Does NOT touch `updated_at`. */
2283
+ writeOkfSources(entryId: string, entityId: string, sources: Array<{
2284
+ resource: string;
2285
+ [k: string]: unknown;
2286
+ }>): Promise<void>;
2287
+ /** Set a fact's OKF v0.2 lifecycle status. Does NOT touch `updated_at`. */
2288
+ setLifecycleStatus(entryId: string, entityId: string, status: 'draft' | 'stable' | 'deprecated'): Promise<void>;
2289
+ /** Set a fact's stale_after (epoch ms) or clear it. Does NOT touch `updated_at`. */
2290
+ setStaleAfter(entryId: string, entityId: string, date: number | null): Promise<void>;
2291
+ /** Set a fact's generated_by actor string. Does NOT touch `updated_at`. */
2292
+ setGeneratedBy(entryId: string, entityId: string, actor: string): Promise<void>;
2293
+ /** Task variants of the same five DAO methods. Symmetric per spec §2.5. */
2294
+ writeOkfTrustTask(taskId: string, entityId: string, verified: {
2295
+ by: string;
2296
+ at: string;
2297
+ }[]): Promise<void>;
2298
+ writeOkfSourcesTask(taskId: string, entityId: string, sources: Array<{
2299
+ resource: string;
2300
+ [k: string]: unknown;
2301
+ }>): Promise<void>;
2302
+ setLifecycleStatusTask(taskId: string, entityId: string, status: 'draft' | 'stable' | 'deprecated'): Promise<void>;
2303
+ setStaleAfterTask(taskId: string, entityId: string, date: number | null): Promise<void>;
2304
+ setGeneratedByTask(taskId: string, entityId: string, actor: string): Promise<void>;
2104
2305
  }
2105
2306
 
2106
- 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 };
2307
+ export { WikiStrictOntologyViolation as $, WikiBusyError as A, type WikiBusyOperation as B, type ChunkFailure as C, type WikiCheckpoint as D, type EntityStatus as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, type IngestDocumentResult as I, type WikiConfig as J, WikiDuplicateHashError as K, type LLMProvider as L, type MemoryBundle as M, type WikiEdge as N, type OntologyManifest as O, type PromptOverrides as P, type WikiEvent as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiFact as T, WikiIngestEmptyError as U, type VectorRanker as V, type WikiOptions as W, type WikiMemoryTestAccess as X, type WikiOutboxEvent as Y, WikiParseError as Z, WikiSourceRefHashCollision as _, type MemoryDump as a, type WikiTask as a0, WikiTransactionError as a1, EmbeddingService as a2, ImportExportService as a3, IngestionService as a4, JobManager as a5, MaintenanceService as a6, RetrievalService as a7, SearchService as a8, WriteService as a9, 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;
@@ -619,6 +673,79 @@ declare class PrunePartialFailureError extends Error {
619
673
  constructor(deleted: number, failedAt: string, remaining: number, cause: Error, deletedTasks?: number, deletedEvents?: number);
620
674
  }
621
675
  declare const HOOK_TIMEOUT_MARKER: unique symbol;
676
+ /**
677
+ * Failure record for a single chunk that could not be ingested. Produced by
678
+ * `IngestionService.ingestDocument` and surfaced on the result's `parseFailures`
679
+ * array. `source === 'parse'` for `WikiParseError`-shaped failures (then `tier`
680
+ * is set); `source === 'llm'` for `generateText` rejections (no `tier`).
681
+ *
682
+ * Note: `'ontologyContext'` is reserved but NEVER produced. `buildPromptContext`
683
+ * failures are DB-systemic and propagate as raw throws, not as `parseFailures`.
684
+ */
685
+ interface ChunkFailure {
686
+ chunkIndex: number;
687
+ sourceRef: string;
688
+ source: 'parse' | 'llm';
689
+ tier?: 'strict' | 'repair' | 'all';
690
+ position: number | null;
691
+ message: string;
692
+ }
693
+ /**
694
+ * Result of `IngestionService.ingestDocument`. The shape WIDENS relative to
695
+ * earlier versions: `ingestedChunks` and `failedChunks` are always present;
696
+ * `parseFailures` is present iff at least one chunk failed.
697
+ *
698
+ * Hosts that destructure `{ truncated, chunks }` see no behavior change.
699
+ * Hosts pattern-matching the entire shape must update to the new fields.
700
+ */
701
+ interface IngestDocumentResult {
702
+ truncated: boolean;
703
+ chunks: number;
704
+ ingestedChunks: number;
705
+ failedChunks: number;
706
+ duplicateOf?: string;
707
+ parseFailures?: ChunkFailure[];
708
+ }
709
+ /**
710
+ * Thrown by `parseJsonResponse` when both the strict scanner pass and the
711
+ * container-aware repair pass fail to produce a parsable candidate.
712
+ *
713
+ * - `tier: 'strict'` — the existing scanner produced no usable slice (no
714
+ * `{`/`[` at all, or no balanced close). `slice` is the full input text;
715
+ * `position` is the offset of the first `{`/`[`, or `null` when none found.
716
+ * - `tier: 'repair'` — tier 1 found a slice but `JSON.parse` failed; tier 2
717
+ * produced no parsable candidate within `MAX_REPAIR_CANDIDATES`. `slice` is
718
+ * the largest balanced span the walker found; `position` is the offset of
719
+ * the first parse failure within that candidate when known.
720
+ * - `tier: 'all'` — generic catch-all.
721
+ */
722
+ declare class WikiParseError extends Error {
723
+ readonly tier: 'strict' | 'repair' | 'all';
724
+ readonly position: number | null;
725
+ readonly slice: string;
726
+ constructor(message: string, opts: {
727
+ tier: WikiParseError['tier'];
728
+ position?: number | null;
729
+ slice?: string;
730
+ });
731
+ }
732
+ /**
733
+ * Thrown by `IngestionService.ingestDocument` when every chunk of a document
734
+ * failed (either `parseJsonResponse` or `generateText`). A silent zero-fact
735
+ * ingest is a worse regression than a typed throw — matches existing host
736
+ * semantics (`aws-cloud-agent` Writer Lambda today sees `ingestDocument`
737
+ * throw on any failure).
738
+ */
739
+ declare class WikiIngestEmptyError extends Error {
740
+ readonly parseFailures: ChunkFailure[];
741
+ readonly sourceRef: string;
742
+ readonly chunks: number;
743
+ constructor(params: {
744
+ parseFailures: ChunkFailure[];
745
+ sourceRef: string;
746
+ chunks: number;
747
+ });
748
+ }
622
749
 
623
750
  interface WikiOutboxEvent<T = unknown> {
624
751
  id: string;
@@ -697,6 +824,21 @@ declare class EntryRepository extends BaseRepository {
697
824
  * Upsert a WikiFact. Nullable fields set to null when fact value is null.
698
825
  * Returns { changes, lastInsertRowId }.
699
826
  * `tx` is REQUIRED to ensure atomic outbox staging.
827
+ *
828
+ * OKF metadata discipline (spec §2.5 + DAO checklist §8): content writes
829
+ * MUST NOT silently overwrite OKF metadata that a caller didn't supply. The
830
+ * previous CASE WHEN IS NULL guards around `lifecycle_status`, etc. were
831
+ * dead because the bind always supplied a non-null value (notably
832
+ * `fact.lifecycle_status ?? 'stable'`), so excluded.X was never NULL and
833
+ * an UPDATE rewrote an existing `deprecated` row to `stable`. We now omit
834
+ * these columns from the SET clause entirely — SQLite's ON CONFLICT UPDATE
835
+ * leaves untouched columns at their existing row value, which is exactly
836
+ * the preservation semantics we want for content writes. Callers that
837
+ * intentionally want to overwrite OKF metadata should use
838
+ * {@link upsertForImport} (always-replace semantics for that path).
839
+ * New-row INSERTs still bind a value so the column is populated; if the
840
+ * caller supplied nothing, the schema DEFAULT 'stable' applies when the
841
+ * bind is bound as 'stable' explicitly here.
700
842
  */
701
843
  upsert(fact: WikiFact, tx: SQLiteAdapter): Promise<{
702
844
  changes: number;
@@ -1422,11 +1564,7 @@ declare class IngestionService {
1422
1564
  promptOverride?: string;
1423
1565
  }, opts?: {
1424
1566
  onDuplicateHash?: 'ingest' | 'skip' | 'throw';
1425
- }): Promise<{
1426
- truncated: boolean;
1427
- chunks: number;
1428
- duplicateOf?: string;
1429
- }>;
1567
+ }): Promise<IngestDocumentResult>;
1430
1568
  /**
1431
1569
  * Implements spec §2 data flow steps a–j with the host-facing
1432
1570
  * `{sourceRef, sourceHash, nodes, edges}` parameter shape. Shared between
@@ -1470,6 +1608,38 @@ declare class IngestionService {
1470
1608
  edgesWritten: number;
1471
1609
  superseded: number;
1472
1610
  }>;
1611
+ /**
1612
+ * Full supersession + ownership path: identical to the pre-issue-#92 behavior
1613
+ * for the happy path. Called from the `failedChunks === 0` branch of
1614
+ * `ingestDocument`. Runs INSIDE the caller's tx.
1615
+ *
1616
+ * Returns `{ deletedSourceFactIds, insertedFacts }` so the caller can fire
1617
+ * post-commit hooks (embedding lifecycle) AFTER the transaction commits.
1618
+ * On the partial path the caller never invokes this method; the empty
1619
+ * arrays stay empty.
1620
+ */
1621
+ private runFullUpsertGraph;
1622
+ /**
1623
+ * Partial-commit path for `ingestDocument`. Inserts ONLY facts whose
1624
+ * normalized title is not already in the live set for this (entityId,
1625
+ * sourceRef). Does NOT call `entryRepo.softDeleteBySource` (no
1626
+ * supersession) and does NOT call `sourceRefIndexRepo.upsert` (no
1627
+ * ownership). Edges are NOT resolved on this path (no ontology-context
1628
+ * build, no `resolveEdges`, no `mergeEmergentUpdates`). Stale edges from
1629
+ * prior attempts are recovered on the next full run's supersession.
1630
+ *
1631
+ * Source-hash is stored as NULL on partial rows. `findLatestSourceHash`
1632
+ * reads from the most recently updated live row for the sourceRef, so
1633
+ * storing the incoming hash here would cause `hasChanged` to return
1634
+ * `false` on a same-hash retry — the failed chunks would never get a
1635
+ * second chance. With NULL, a retry sees `storedHash === null`, `hasChanged`
1636
+ * returns `true`, and the partial commit's sibling rows remain live
1637
+ * (deduped, not superseded) for the next attempt to extend.
1638
+ *
1639
+ * Runs INSIDE the caller's `tx`. Does not open a nested transaction.
1640
+ * Returns `{ inserted, skippedDuplicate }` for observability.
1641
+ */
1642
+ private appendPartialFacts;
1473
1643
  }
1474
1644
 
1475
1645
  declare class TaskRepository extends BaseRepository {
@@ -1494,6 +1664,12 @@ declare class TaskRepository extends BaseRepository {
1494
1664
  * Uses ON CONFLICT(id) DO UPDATE (not INSERT OR REPLACE).
1495
1665
  * Stages an outbox entry in the same transaction.
1496
1666
  * `tx` is REQUIRED.
1667
+ *
1668
+ * OKF metadata (lifecycle_status, sources, etc.) is intentionally excluded
1669
+ * from the SET clause so content writes preserve any existing row values.
1670
+ * See {@link EntryRepository.upsert} for the rationale — the same applies
1671
+ * symmetrically to tasks (spec §2.5). Callers that need to overwrite OKF
1672
+ * metadata should use {@link upsertForImport}.
1497
1673
  */
1498
1674
  upsert(task: WikiTask, tx: SQLiteAdapter, updatedAt?: number): Promise<void>;
1499
1675
  upsertForImport(task: WikiTask, tx: SQLiteAdapter, updatedAt?: number): Promise<void>;
@@ -1835,6 +2011,7 @@ declare class WikiMemory {
1835
2011
  private promptService;
1836
2012
  private ontologyService;
1837
2013
  private graphTraversalService;
2014
+ private readonly okfTrustWrites;
1838
2015
  constructor(db: SQLiteAdapter, options: WikiOptions);
1839
2016
  /**
1840
2017
  * Explicit escape hatch for test suites: typed access to composed services for mocks/spies.
@@ -2022,11 +2199,7 @@ declare class WikiMemory {
2022
2199
  promptOverride?: string;
2023
2200
  }, opts?: {
2024
2201
  onDuplicateHash?: 'ingest' | 'skip' | 'throw';
2025
- }): Promise<{
2026
- truncated: boolean;
2027
- chunks: number;
2028
- duplicateOf?: string;
2029
- }>;
2202
+ }): Promise<IngestDocumentResult>;
2030
2203
  /**
2031
2204
  * Deterministic write path into the graph. Accepts caller-supplied
2032
2205
  * `(nodes, edges)` and writes them under `(sourceRef, sourceHash)`
@@ -2101,6 +2274,34 @@ declare class WikiMemory {
2101
2274
  setOntologyManifest(entityId: string, manifest: OntologyManifest, options?: {
2102
2275
  mode?: OntologyMode;
2103
2276
  }): Promise<void>;
2277
+ /** Append a verification event to a fact. Does NOT touch `updated_at`. */
2278
+ writeOkfTrust(entryId: string, entityId: string, verified: {
2279
+ by: string;
2280
+ at: string;
2281
+ }[]): Promise<void>;
2282
+ /** Replace a fact's source list. Does NOT touch `updated_at`. */
2283
+ writeOkfSources(entryId: string, entityId: string, sources: Array<{
2284
+ resource: string;
2285
+ [k: string]: unknown;
2286
+ }>): Promise<void>;
2287
+ /** Set a fact's OKF v0.2 lifecycle status. Does NOT touch `updated_at`. */
2288
+ setLifecycleStatus(entryId: string, entityId: string, status: 'draft' | 'stable' | 'deprecated'): Promise<void>;
2289
+ /** Set a fact's stale_after (epoch ms) or clear it. Does NOT touch `updated_at`. */
2290
+ setStaleAfter(entryId: string, entityId: string, date: number | null): Promise<void>;
2291
+ /** Set a fact's generated_by actor string. Does NOT touch `updated_at`. */
2292
+ setGeneratedBy(entryId: string, entityId: string, actor: string): Promise<void>;
2293
+ /** Task variants of the same five DAO methods. Symmetric per spec §2.5. */
2294
+ writeOkfTrustTask(taskId: string, entityId: string, verified: {
2295
+ by: string;
2296
+ at: string;
2297
+ }[]): Promise<void>;
2298
+ writeOkfSourcesTask(taskId: string, entityId: string, sources: Array<{
2299
+ resource: string;
2300
+ [k: string]: unknown;
2301
+ }>): Promise<void>;
2302
+ setLifecycleStatusTask(taskId: string, entityId: string, status: 'draft' | 'stable' | 'deprecated'): Promise<void>;
2303
+ setStaleAfterTask(taskId: string, entityId: string, date: number | null): Promise<void>;
2304
+ setGeneratedByTask(taskId: string, entityId: string, actor: string): Promise<void>;
2104
2305
  }
2105
2306
 
2106
- 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 };
2307
+ export { WikiStrictOntologyViolation as $, WikiBusyError as A, type WikiBusyOperation as B, type ChunkFailure as C, type WikiCheckpoint as D, type EntityStatus as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, type IngestDocumentResult as I, type WikiConfig as J, WikiDuplicateHashError as K, type LLMProvider as L, type MemoryBundle as M, type WikiEdge as N, type OntologyManifest as O, type PromptOverrides as P, type WikiEvent as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiFact as T, WikiIngestEmptyError as U, type VectorRanker as V, type WikiOptions as W, type WikiMemoryTestAccess as X, type WikiOutboxEvent as Y, WikiParseError as Z, WikiSourceRefHashCollision as _, type MemoryDump as a, type WikiTask as a0, WikiTransactionError as a1, EmbeddingService as a2, ImportExportService as a3, IngestionService as a4, JobManager as a5, MaintenanceService as a6, RetrievalService as a7, SearchService as a8, WriteService as a9, 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 { a2 as EmbeddingService, a3 as ImportExportService, a4 as IngestionService, a5 as JobManager, a5 as JobManagerType, a6 as MaintenanceService, a7 as RetrievalService, a8 as SearchService, a8 as SearchServiceType, X as WikiMemoryTestAccess, a9 as WriteService } from './testing-D0RnZjyW.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 { a2 as EmbeddingService, a3 as ImportExportService, a4 as IngestionService, a5 as JobManager, a5 as JobManagerType, a6 as MaintenanceService, a7 as RetrievalService, a8 as SearchService, a8 as SearchServiceType, X as WikiMemoryTestAccess, a9 as WriteService } from './testing-D0RnZjyW.js';
2
+ import '@equationalapplications/core-okf';
2
3
  import 'minisearch';