Release 1.6: universal local document imports and hardening - #130
Release 1.6: universal local document imports and hardening#130Coding-Dev-Tools wants to merge 58 commits into
Conversation
Store/Schema: - Transaction rollback safety on Windows (commits_deferred flag) - Skip redundant index.upsert when index shares store vector table - Embedding-space contract validation and direct SQL matrix path - Restore orphaned Store methods (prompt_eligibility_counts, embedding_space_health, context_savings_grouped, add_sync_bytes, get_sync_stats) and harden _logical_digest for sqlite-vec - Dashboard startup self-check for orphaned Store methods - v11 handoff column migration for sessions table Release readiness: - Strip dead TEAM scope surface (Scope.TEAM, MemoryRecord.team_id, SearchFilter.team_id/caller_id) per AGENTS.md §0 - Remove team_id column from memories table (team_members retained) - Remove conflict_aware profile (zero callers/tests/docs) - Add format/group_by params to MemoryService.context_savings() Other: - watch_repo key name, LLM fallback chain cost tracking - context_savings_grouped SELECT columns - Sync robustness, response budgets, docs sync
- MCP tool engraphis_context_savings: add format and group_by params - v2 API route /context-savings: add format and group_by query params - Read-only API /context-savings: add format and group_by query params - MCP HTTP CLI: minor fix
- eval/extractor_quality.py: offline extractor distillation quality harness - eval/handoff_quality.py: structured session-handoff eval - eval/datasets/handoff_quality.jsonl: handoff scenario fixtures - eval/datasets/sample.jsonl: 3 new entries (link_symbol, hierarchical consolidation, extractor/handoff patterns) - scripts/watch_repo.py: file-watcher for code-graph reindexing - scripts/validate_compose_contract.py: schema contract validator
- dashboard_app.py: pass workspace to stats() when allowed_workspaces is configured (prevents ValidationError on workspace-bound instances) - Sync classic_assets and static bundles with latest dashboard changes
- test_cli_entrypoints: clear ENGRAPHIS_WORKSPACES env for subprocess isolation - test_dashboard_security_headers: monkeypatch.delenv ENGRAPHIS_WORKSPACES - test_savings: adjust for context_savings format/group_by signature - test_sync: align with sync robustness hardening - test_update_check: align with 2-part semver acceptance - test_mcp_server, test_smart_mcp_gateway: align with MCP surface wiring - test_store_class_integrity: new Store method integrity coverage - test_dashboard_v2, test_release_infrastructure, test_secret_hygiene: minor alignment with pro-feature changes
The restored method from b7e80c2 predated the hash-validation contract in _public_receipt_row. Without prev_hash and receipt_hash in the SELECT, every receipt was marked invalid_payload and excluded from aggregation, causing the grouped method to return 0 groups despite valid receipts. Verified: all 4 dimensions (workspace/repo/agent/day) now return correct token aggregation. Savings ratio: 0.6 (180 saved / 300 source). Found by round-3 ContextSavingsGroupedRuntime scout.
Save JSON reports to a directory with timestamped filenames. Cherry-picked from codex/fix-pr-104-codeql-v4 (a4928c5).
… preservation, retention SQL, migration safety, port parsing - llm/client.py: _LLMProviderError accepts *args + message kwarg (fixes TypeError on cost-ceiling path) - llm/client.py: parse_provider_chain detects URL ports via :// prefix check (prevents port-as-ceiling stripping) - core/sync.py: quarantine merge preserves prior reasons alongside inherited_quarantine (audit trail) - stores/vectors.py: upsert_memory preserves existing memory_type when caller passes default semantic - routes/memory.py: AVG(stability) SQL approximation for retention stats (avoids OOM on large workspaces) - routes/vault.py: hashlib suffix on smart-import split doc_ids (collision prevention) - routes/vault.py: health_overview and find_stale bounded to 10k limit - service.py: Windows-safe two-step rename with staging file and rollback for v1->v2 migration - stores/__init__.py: graph backfill uses _replace_support_rows + per-namespace rebuild (O(N) not O(N²)) - config.py: embed_dim downstream consumers use 'is not None' pattern - 6 downstream embed_dim call sites fixed (dashboard_app, mcp_server, read_only_api, inspector, v2_api, embedder) - eval/longmemeval_v2.py: _stored_memory_type_counts scoped to workspace - eval/harness.py: --output-dir writing inside try/except for clean CLI errors - KILO_CODE_INTEGRATION.md: tool count corrected to nine; engraphis_forget row added - .env.example: ENGRAPHIS_SYNC_TOKEN_ORIGIN documented - Test file trailing newlines restored
…verage Closes P1 gap flagged by FinalA_Tests review: - port-stripping fix (localhost:8080 vs ceiling) — 5 cases - multi-provider chain parsing with mixed ceilings - empty/whitespace fallback to default chain - _LLMProviderError positional+kwarg construction contract
… harness improvements - .github/workflows/release.yml: CodeQL config-file reference added - .env.example: ENGRAPHIS_SYNC_TOKEN_ORIGIN documented for standalone sync tokens - docs/KILO_CODE_INTEGRATION.md: Smart tool count corrected to nine; engraphis_forget row added to Classic table - engraphis/dashboard_assets/vendor/d3.min.js: unified across classic/static (no new Function) - eval/longmemeval_v2.py: _stored_memory_type_counts scoped to workspace - eval/harness.py: --output-dir writing inside try/except for clean CLI errors - tests/e2e/demo.spec.js, test_dashboard_vendor_assets.py, test_documentation_contracts.py: new coverage - deploy/force-graph license/yarn lock files added - docs/benchmark-evidence offline fixtures added - Multiple doc updates: AGENTS.md, BENCHMARKS.md, CHANGELOG.md, README.md, SECURITY.md, SYNC.md - Demo infrastructure: screen demo HTML, prepare script, record script - Integration updates: hermes plugin, pi MCP client - Skills: engraphis-memory SKILL.md and references updated
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a214aef834
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: edec95a288
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f72b462b86
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1ceea5000e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 72d3b26679
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if len(candidates) != 1: | ||
| if candidates: | ||
| retire_ambiguous_links( |
There was a problem hiding this comment.
Retire links whose imported target disappears
When a previously imported target file is removed while the referring note remains unchanged, resolution reaches this branch with no candidates, but link retirement runs only when candidates is nonempty. The missing target's memory intentionally remains live, so its existing document_reference/obsidian_wikilink edge also remains live and recall can continue traversing a reference that this run reports as unresolved; invalidate prior derived links that are absent from the newly resolved target set.
AGENTS.md reference: AGENTS.md:L162-L164
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 913af10b77
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| session_id: Optional[str], scope: Scope, memory_type: MemoryType, | ||
| policy: str, vault_label: str, attachment_manifest: Optional[list[dict]], | ||
| ) -> dict: | ||
| files = list(outcomes) |
There was a problem hiding this comment.
Include unprocessed plans in terminal reports
When either document or Obsidian importing is cancelled before all plans run (including a CLI --limit pause), or an outer-loop failure stops processing, the durable job items for every unprocessed plan remain result_state="pending", but _final_report() starts only from completed outcomes and adds pending rows only for deferred missing checks. The resulting job can therefore report documents: 2 with an empty files list and no pending count even though both durable items are pending, making resumable progress inconsistent; include all plans without outcomes as pending rows.
AGENTS.md reference: AGENTS.md:L215-L219
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 39969abae3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if old_item is not None: | ||
| old_memory_id = self._live_subject_memory(str(old_item.get("subject_key") or "")) | ||
| old_memory_id = old_memory_id or str(old_item.get("memory_id") or "") or None |
There was a problem hiding this comment.
Serialize concurrent imports per source item
When two dashboard jobs or CLI processes import the same changed source concurrently, both can read the same live predecessor here before either write begins. Each transactional finalizer then closes only that shared predecessor, so both successor memories remain live under the same subject_key, the manifest points to whichever finishes last, and both jobs report completion. Serialize imports by source identity or revalidate and close the current subject memory inside the write transaction.
AGENTS.md reference: AGENTS.md:L162-L164
Useful? React with 👍 / 👎.
| "UPDATE mem_links SET valid_to=?, valid_to_recorded_at=? " | ||
| "WHERE reason=? AND valid_to IS NULL AND expired_at IS NULL " | ||
| "AND ((a=? AND b IN (" + marks + ")) " | ||
| "OR (b=? AND a IN (" + marks + ")))", | ||
| (stamp, stamp, self.LINK_REASON, source_id, *target_ids, | ||
| source_id, *target_ids), |
There was a problem hiding this comment.
Retain links with independent source support
When two notes independently support the same undirected relation and one note's reference later becomes ambiguous, this update closes their shared mem_links row even though the other note still has a uniquely resolved reference. Since Store.add_link() deduplicates either direction by pair and relation, whether the valid note reopens the link depends on filename iteration order; for example, an earlier A.md -> C.md reference is removed when later C.md -> [[Alias]] becomes ambiguous. Reconcile the complete desired support set or persist per-reference provenance before retiring the aggregate edge.
AGENTS.md reference: AGENTS.md:L171-L172
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6b4465aafe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| link_warnings = self._reconcile_links( | ||
| scan, vault_id=vault_id, job_id=job_id, cancel_check=cancel_check, | ||
| ) |
There was a problem hiding this comment.
Defer link reconciliation for incomplete scans
When a referenced target lies under a temporarily unreadable subtree, can_finalize_missing correctly prevents missing-item reconciliation, but this unconditional call still reconciles against the incomplete scan.notes. The historical target is then treated as unresolved and its last-good live link is retired; if another scanned file shares its basename, the reference can even be redirected to that file. Skip link reconciliation when the scan is incomplete, or include preserved unreadable paths in the resolution index.
AGENTS.md reference: AGENTS.md:L215-L219
Useful? React with 👍 / 👎.
| warnings.append(self._outcome( | ||
| note, "warning", | ||
| "ambiguous_wikilink" if candidates else "unresolved_wikilink", | ||
| )) |
There was a problem hiding this comment.
Persist link-reconciliation warnings in job items
For asynchronous dashboard imports containing an unresolved or ambiguous reference, this warning exists only in the transient outcomes list. _finish_job() persists the warning count, but get_document_import_job() reconstructs files exclusively from source_import_items, so polling can report counts.warning == 1 while every file has an empty reason and zero warnings. Merge these warnings into the corresponding durable job item or persist a dedicated warning item.
AGENTS.md reference: AGENTS.md:L259-L261
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4062dd3a99
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return record | ||
| if spec.name == "xml": | ||
| body, title, metadata, warnings = _xml_body(raw, fallback) | ||
| content = body |
There was a problem hiding this comment.
Preserve XML attributes before checking for secrets
When valid XML stores data in attributes, such as <server api_key="..." host="prod">name</server>, assigning content = body discards those values before the secret check; most attributes vanish entirely, while href/src values are still copied into persisted link metadata and can therefore store a credential that the check never sees. Preserve a bounded XML representation or scan every persisted attribute so imports neither lose configuration facts nor bypass the documented secret rejection.
AGENTS.md reference: AGENTS.md:L259-L261
Useful? React with 👍 / 👎.
| if self._ignored: | ||
| if lower in {"script", "style", "noscript", "template"}: | ||
| self._ignored -= 1 |
There was a problem hiding this comment.
Match ignored HTML closing tags to their opener
When malformed HTML inside noscript or template contains a mismatched ignored closing tag, this decrements _ignored regardless of which tag opened the ignored region; for example, <noscript></style>EVIL</noscript> imports EVIL even though noscript content is supposed to be excluded. Track the ignored tag stack, or only close the matching region, so hidden script/fallback content cannot become canonical memory text.
AGENTS.md reference: AGENTS.md:L259-L261
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d111842e26
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if sum(len(part) for part in parts) + len(text) + (2 if parts else 0) > MAX_CONTAINER_TEXT_CHARS: | ||
| raise DocumentParseError("document exceeds 100000 character safety limit") | ||
| parts.append(text) | ||
| title = next(("".join(item.itertext()).strip() for item in package.iter() if item.tag.endswith("title") and "".join(item.itertext()).strip()), "") |
There was a problem hiding this comment.
Validate EPUB package titles before exposing them
When an EPUB's OPF <dc:title> contains a credential-shaped value but its chapter text does not, the parser extracts that title here while the secret check validates only content and body. Document previews then return the unredacted value in each file's title before the engine's later write boundary can reject it, violating the import report's no-secret-echo contract. Include EPUB title/package metadata in the bounded secret-validation boundary.
AGENTS.md reference: AGENTS.md:L259-L261
Useful? React with 👍 / 👎.
| elif name in {"odt", "ods", "odp"}: | ||
| body, meta = _office_body(archive, "content.xml", "{urn:oasis:names:tc:opendocument:xmlns:text:1.0}", ("h", "p"), None) |
There was a problem hiding this comment.
Extract attribute-backed ODS cell values
For a valid ODS whose numeric, date, or boolean cells store their values in standard office:value-style attributes without a <text:p> display node, routing ODS through _office_body() extracts only text headings and paragraphs. A numeric-only worksheet consequently produces no readable text and is rejected, despite ODS being advertised as preserving worksheet/cell values. Parse ODS table rows and typed value attributes separately from the word-processing path.
AGENTS.md reference: AGENTS.md:L259-L261
Useful? React with 👍 / 👎.
| target_id = memory_by_path.get(candidates[0]) | ||
| if not target_id or target_id == source_id: | ||
| continue | ||
| desired_pairs.add(pair_key(source_id, target_id)) |
There was a problem hiding this comment.
Retire fallback links when exact resolution changes
When an unchanged [[foo]] initially resolves by basename to sub/foo.md and a later import adds an exact foo.md, this branch adds the new exact-target pair but the old fallback pair never enters retire_pairs, because retirement is performed only in the ambiguous/unresolved branch. Both edges remain live and recall can continue traversing the obsolete target; after building the complete desired pair set, invalidate importer-derived pairs that no source reference still supports.
AGENTS.md reference: AGENTS.md:L162-L164
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5aaa943bd5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for _ in range(repeated): | ||
| cells.append(value) | ||
| cell_count += 1 |
There was a problem hiding this comment.
Bound repeated ODS cells before materializing them
When an ODS cell combines table:number-columns-repeated="10000" with a large text value, this loop appends the value 10,000 times and joins the entire row before the size check below runs; a roughly 100 KB XML cell can therefore allocate about 1 GB and terminate the import process despite the advertised 100,000-character limit. Check the projected row size before expanding repeated cells.
AGENTS.md reference: AGENTS.md:L259-L261
Useful? React with 👍 / 👎.
| fallback = Path(relative_path).stem or "document" | ||
| return DocumentRecord( | ||
| relative_path=relative_path, format=spec.name, media_type=spec.media_type, | ||
| title=title or (headings[0] if headings else fallback), content=content, body=body, |
There was a problem hiding this comment.
Cap extracted titles before creating document records
When metadata supplies an oversized title, this assignment retains it without applying the document text bound; for example, an EPUB OPF can contain a title larger than 100,000 characters while its chapter body is tiny and still pass the content/body checks. The preview then serializes the entire title and the engine concatenates it into embedding input, allowing oversized responses and writes despite the documented cap, so bound the title before returning the record.
AGENTS.md reference: AGENTS.md:L259-L261
Useful? React with 👍 / 👎.
| if content[end] == "\\": | ||
| end += 1 | ||
| if end < len(content) and content[end].isalpha(): | ||
| while end < len(content) and content[end].isalpha(): |
There was a problem hiding this comment.
Consume complete RTF Unicode fallback controls
When an RTF Unicode fallback is encoded as a control symbol or hex escape, such as \u945\'3f, this branch advances past only the backslash because the following apostrophe is not alphabetic. The remaining parser consequently imports α'3f rather than α; consume the entire control-symbol or hex-escape fallback as the single character represented by \ucN.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0a2110b64e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if secret_kind(record.content) is not None or secret_kind(record.body) is not None: | ||
| raise DocumentParseError("source appears to contain a secret") |
There was a problem hiding this comment.
Validate adapter metadata before returning records
When a custom PDF/OCR/transcription adapter returns safe content and body but places a credential in title or metadata, this check accepts the record because it scans only the two text fields. The title is then exposed by import previews and both values can be persisted in document metadata, contradicting the import report's no-secret-echo boundary; validate every adapter-controlled field that can be returned or stored.
AGENTS.md reference: AGENTS.md:L259-L261
Useful? React with 👍 / 👎.
| rows: List[str] = [] | ||
| cell_count = 0 | ||
| total = 0 | ||
| for row in (item for item in root.iter() if item.tag.endswith("table-row")): |
There was a problem hiding this comment.
When a valid ODS uses table:number-rows-repeated on a nonempty row, this loop processes the row only once; for example, a row repeated three times is imported as one row and reported with rows == 1. This silently loses spreadsheet values and multiplicity, so account for row repetition while applying the same projected-output bound used for repeated columns.
AGENTS.md reference: AGENTS.md:L259-L261
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: df587ce27a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if raw.startswith((b"\xff\xfe", b"\xfe\xff")): | ||
| return _decode_text(raw) | ||
| head = raw[:65536] | ||
| match = _HTML_CHARSET_RE.search(head) or _HTML_CONTENT_CHARSET_RE.search(head) |
There was a problem hiding this comment.
Parse charset only from actual HTML metadata
When valid HTML contains meta-like text inside a comment before its real declaration, such as <!-- <meta charset=windows-1252> --><meta charset=utf-8>, this raw-byte regex selects the ignored declaration. UTF-8 text is then decoded as Windows-1252 and persisted incorrectly (for example, Café becomes Café) without a warning; charset detection needs to ignore comments and other non-metadata content.
Useful? React with 👍 / 👎.
Pre-PR Code Review — engraphis#130Structural AssessmentThis is a massive release PR (17,354 additions / 4,830 deletions across 62 files, 50+ commits) covering:
CI Status: ✅ All 23 checks greenCodeQL, test+lint (Python 3.9–3.14), coverage, typecheck, encryption driver gates, Pi extension (Ubuntu + Windows), browser accessibility, docker build/smoke, wheel build, repo hygiene — all passing. Security ObservationsThe PR body documents thorough safety measures:
Hard Gate Blocks (cannot approve yet)
Verdict: REQUEST_CHANGESThe code quality appears high based on CI coverage and iterative review history. Once the contributor gate is satisfied (e.g., another agent authors a refinement commit or the contributor requirement is waived for release PRs), this is approvable. Suggested next steps:
|
Summary
engraphis import documents PATHwith preview, trusted-local confirmation, resumable per-document writes, temporal revisions, idempotent re-import, conflict policies, and complete reportsWhy
Users need to bring existing unsorted documents into Engraphis locally and repeatedly without creating a hidden AI-only copy, invoking an LLM extractor, or making network requests. Existing generic folder ingestion did not provide safe preview, stable source identity, temporal updates, resumability, rich Markdown links, or durable per-file reporting.
Safety and correctness
vlt_sourceSupported inputs
Native parsers cover Markdown/Obsidian, plain text and logs, RST, HTML, JSON/JSONL, CSV/TSV, YAML, TOML, INI-style files, XML, RTF, common source code, DOCX, ODT, EPUB, XLSX, PPTX, ODS, and ODP. PDF, image, audio, and video use optional configured local extractors. Legacy OLE, encrypted/DRM, and unknown binary formats are rejected and reported rather than guessed.
Validation
tests/suite passedruff check .pyright— 0 errors, 0 warningsIntentional limitations
Conservative top-level YAML is parsed rather than arbitrary executable YAML. Dataview/plugins/macros/scripts and transclusion expansion are not executed. Attachments are catalogued but not copied. Scanned PDFs do not gain automatic OCR, ambiguous links remain warnings, and legacy OLE/encrypted/DRM files remain unsupported.