release: v1.6 hardening — bounded imports, fleet hygiene, fail-able eval gates - #159
Conversation
…, CI wiring eval/harness.py gains a per-dataset floor registry (sample/codemem >= 0.9 recall/hit at k=5) enforced by main() with exit 1; grounded.py now gates on decision accuracy == 1.0; ablation.py exits non-zero on violated invariants. ASCII-safe harness output plus the previously omitted mrr/ndcg lines. ci.yml runs the grounded + code-arm gates and adds the codemem step to the py39 floor lane; AGENTS.md section 1 and its documentation-contract test stay pinned together.
New deterministic offline eval proving the code retrieval arm reaches call-bridge answers that vector and lexical arms cannot (arm-isolated recall@5: vector 0.0, lexical 0.0, code 1.0; pipeline balanced 0.0 vs code 1.0). Bridges form only via index_repo + write-time memory-code linking; distractors carry the query's surface words so text arms fill top-k with near-misses. Exit 1 without strict lift.
… notice scripts/cli.py maps sqlite3/OSError/ImportError/RuntimeError from service startup to one redacted actionable line instead of a traceback; scripts/consolidate.py --db now defaults to settings.db_path like every other entrypoint; engraphis-init prints an explicit notice when pinning the trusted config to a CWD-relative database path.
engraphis-sync console script plus 'engraphis sync' verb route to scripts.sync:main; new --status prints local-only sync state (device, generation, state hash, counts, configured remote/relay) read via a read-only SQLite URI — no network I/O, no mutation, nothing fabricated. Entry-point smoke manifest stays pinned to pyproject.
…edger sizes sqlite-vec visibility post-filtering now batches ids at IN_CLAUSE_CHUNK parity (500) instead of 8, removing ~60x SQL round-trip amplification on filtered native search. Auto-mode fallback to the NumPy index logs one warning naming both backends. Recall capabilities gain additive vector_index_backend / reranker_mode keys; stats() reports operation_receipts/events/audit row counts so append-only ledger growth is observable.
…build _repair_conflicts logs and audits conflict-detection failures instead of silently disabling repair; consolidation safety-label inheritance writes an audit row like every engine-side sensitivity change; memories_mentioning gets a bounded SQL window against sparse-eligibility full-repo scans; embedding rebuild pages only rows missing or stale in mem_vectors so interrupts resume instead of restarting; duplicated graph-arm entity seeding extracted into one helper; dead RecallEngine._pack shim removed; Store now asserted against the exported LexicalIndex protocol.
… warning sync dict_to_record validates workspace_id/repo_id symmetric with every other clamped field (both are re-homed locally, so this is hardening only); http_security logs one startup warning when ENGRAPHIS_CSP is explicitly emptied so a CSP-less deployment is observable.
CHANGELOG [Unreleased] records the source-import hardening landing via #154 (deterministic missing detection, denial-guard supersession, generation-guarded finalization, keyset manifest paging, constant-time finalized check). Skills reference documents the fast retrieval profile and every engraphis_answer parameter; update-check docs state the opt-in default the code actually has; README gains ENGRAPHIS_UPDATE_CHECK/_URL rows, the new sync CLI surface, and the facade note; ARCHITECTURE_V3 qualifies schema-3 as historical; SYNC.md documents the shared-folder workspace_name residual risk and append-only ledger growth. Skill-asset manifest re-pinned; offline evidence artifact re-issued binding the edited eval/grounded.py (numbers unchanged) across all pinned surfaces.
Removes integrations/hermes/ (provider plugin + README), its integration test, the README Hermes-provider section, and the packaging test's plugin-version pin. Files are archived locally under _archive/engraphis-hermes-integration-20260821/ and remain recoverable from v1.5 release history. CHANGELOG [Unreleased] records the withdrawal. The negative guard asserting other integration surfaces stay Hermes-free (test_packaging) is retained.
FactSpec batch assembly in MemoryEngine.remember_many: one shared transaction, each fact resolved against already-resolved siblings (NOOP-reinforce on near-duplicates, keyed supersession), atomic rollback on any failure, and evidence-labeled related edges between siblings sharing a subject_key or declared provenance source. MemoryService.remember_many wraps it with shared-batch provenance and a 500-item cap; exposed as MCP tool engraphis_remember_many (classic surface 34 -> 35 tools) with docs, skill reference, and asset hashes synchronized. New tests/test_remember_many.py covers ordering, within-batch dedup/supersession, evidence wiring, rollback, caps.
…zero-width validity window Windows clock granularity (~15.6 ms ticks) can tie a memory's creation stamp to the sweep's `now`, collapsing [valid_from, archived_at) to an empty half-open interval that no as_of read can see; the test then fails ~60-70% of runs depending on host timing. Back-date creation and advance the sweep instant so the assertion exercises archival semantics, not host clock granularity. Complements the consolidate-side non-degenerate close clamp for degenerate caller timestamps.
… coarse clocks A consolidation sweep whose now ties a memory's ingest instant (~15.6 ms Windows clock ticks) closed [valid_from, valid_to) to zero width, making the archived row invisible to every as_of read — including the historical recall archiving exists to serve. Clamp the archive close instant to valid_from + 1us when now <= valid_from; the store-level close contract for exact caller-supplied instants is untouched.
Windows wall-clock resolution can tie _mark_authoritative_denial to the reconnect bootstrap's entitlement_checked_at, so the strict > in _clear_superseded_denial never fires and the guard sticks. Back-date the denial stamp so the test asserts supersession logic, not host clock granularity.
…dow width A sweep whose now ties a memory's ingest instant (~15.6 ms Windows clock ticks) cannot separate 'live' from 'historical': closing [t, t) hides the fact from every as_of read, while clamping valid_to forward (valid_from + 1us, the previous approach) leaves the just-archived row live-visible to every read sampling the remainder of the same tick — observed as an intermittent failure of test_consolidate_archives_decayed_transients_but_ not_pinned (~1/8 module runs). Defer instead: the tied sweep leaves the memory live and reports it via report['archive_deferred']; a strictly later sweep closes it with ordinary half-open semantics. Production sweeps run minutes apart, so deferral is unobservable there. Tests asserting closed-path archives now back-date valid_from deterministically rather than racing the host clock.
…rphan sessions FastMCP's default stateful streamable-HTTP mode tracks session ids in memory, so every engraphis-mcp-http bounce (pm2 resurrect, watchdog, manual restart) invalidated all live sessions: the client's next request got a 404, the mcp SDK raised 'Session terminated', and Hermes gateway clients parked for their full 300s retry interval with zero registered tools (60+ such restart cycles in hermes-logs/engraphis-mcp-http.log). Set stateless_http on the standalone launcher's server so every POST is self-contained per the MCP spec; spec-compliant clients skip the absent GET SSE stream (405). stdio and dashboard mounts keep their defaults.
- dependabot.yml: group github/codeql-action/* so init+analyze always bump in one PR; a mixed-version pair deterministically fails CodeQL's analyze post-action step (seen on open dependabot #157/#158) - .gitignore: schema-migration flock (.*.migration.lock, held live while the server runs) plus regenerable root-level diagnostic dumps (/404_paths.txt /disk_report.txt /large_files.txt /stats_pm2.txt /venv_status.txt /r3.txt)
… an LLM extractor MemoryService picks up the owner's ~/.engraphis/config.env, so on machines with ENGRAPHIS_EXTRACTOR=llm every engine.ingest() call in the suite made a live extraction request (10-90s through the local proxy). That starved test_session_close_linearizes_before_delayed_memory_write[ingest]'s 10s worker budget and made the 'offline gate' network-dependent. Force ENGRAPHIS_EXTRACTOR=none via os.environ.setdefault, mirroring the existing ENGRAPHIS_UPDATE_CHECK=0 posture: real shell overrides still win (config.env itself uses setdefault), and tests exercising extraction opt in explicitly. Idempotent file: 41s+ with live calls -> 8.9s offline.
- Raise MAX_IMPORT_FILES 500 -> 1,500 and total bytes to 750 MB; document/Obsidian scanner budgets move in lockstep. - Parse dashboard wizard uploads through _BoundedUploadRoute under the advertised ceiling: >1,000-file batches no longer fail inside Starlette's hidden 1,000-part default; oversized batches return a clean 413 and large vault uploads clear the 8 MB body limit. - Surface folder-import truncation via truncated/matched_total/unreadable instead of silently importing an alphabetically-first slice. - Degrade one unreadable/pathological file to a per-file error instead of rolling back the whole batch with a 500. - Mark worker-dead import jobs failed (worker_lease_expired) on next status poll instead of reporting running forever. - Hydrate OneDrive Files-On-Demand placeholders via new fsutil.is_link_indirection; symlinks and junctions remain blocked.
Thought synthesis default 512 -> 4096 tokens and availability ping 5 -> 1024: reasoning models spend completion budget on hidden reasoning tokens first, so tiny caps returned empty replies that read as false negatives.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c23d6d5ed7
ℹ️ 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".
Eval fixtures seed via tempfile.TemporaryDirectory(), which lands under the runner tempdir — not under GITHUB_WORKSPACE/RUNNER_TEMP roots exported to pytest legs, so index_repo rejected them ('repo root is outside approved local roots'). Append tempfile.gettempdir() to ENGRAPHIS_INDEX_ROOTS on the three full-suite legs.
…oad test - test_service: staticmethod wrapper is not callable on 3.9; patch the module attribute with a plain callable. - test_remember_many: importorskip mcp so the numpy-only core floor skips instead of failing. - test_bounded_uploads: assert the bounded route class on router.routes — newer FastAPI wraps included routers in one composite object instead of flattening per-path routes onto app.routes; also fix the too-many-fields payload (dict comprehension collapsed 64 fields to one key) and pin the strict 400 contract.
Raw str(ResourceExtractionError) reached import API responses (CodeQL py/stack-trace-exposure, 15 sinks in routes/v2_api.py). Reports now carry canned labels from core.documents._safe_reason; the full message stays in server logs only.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f3b9202135
ℹ️ 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".
The dependency-free indexer only produced 'defines' edges, so on the numpy-only floor (no tree-sitter) the code-arm bridge could not hop a caller to its callee and code-arm recall collapsed to 0.0 (core-floor CI). The fallback now emits caller→callee 'calls' edges for references to same-file symbols inside detected function bodies — bounded to indexed symbol names, one edge per pair, best-effort by design; the AST backend stays authoritative where a grammar exists.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 99759197d7
ℹ️ 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".
- Filter batch sibling context passed to resolution by matching memory type, mirroring _resolve_against_neighbors' own candidate visibility and scoring's per-type weights: a cross-type restatement can no longer drive ADD/NOOP/INVALIDATE. - Derive per-fact trusted_write from the normalized provenance envelope (prompt_eligible) exactly like the single-write path: untrusted batches (api/web/import ingress) stay passive pending evidence instead of resolving against live memory or minting trusted hints. - Queue post-commit vector publications only for ops that inserted a record (add/invalidate/relate); noop results reference the pre-existing memory id and must never republish a candidate vector over it. - Revalidate session liveness inside the batch transaction via store.begin_session_write under the write lock, so a session ended between pre-checks and commit rejects cleanly instead of attaching writes.
_recover_stale_import_jobs borrowed the graph-index 60s lease, so any single document taking longer than a minute (large PDF, OCR image, transcription) got killed by its own status poll mid-file. Imports now carry IMPORT_JOB_LEASE_SECONDS=900 while keeping the existing progress-aware expiry: the per-document heartbeat resets lease age, live workers are never failed, dead workers still expire with worker_lease_expired.
… status - _consolidation_evidence now appends non-record endpoints of consolidates/profiles links through the visible-append gate, so legacy/repaired digests carrying sources only in the link tables still yield complete grounding evidence. - scripts.sync --status --repo <name> scopes memory/tombstone counts to the requested repository (_scoped_counts); absent repos report an empty scope instead of workspace totals; no-repo invocations keep workspace-wide totals.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…hema, error routing
- sync --status --repo: a repository absent from the workspace now reports an explicitly empty scope ('repo: <name> (not found locally)', zero counts) and no longer presents the workspace checkpoint as the requested scope's cursor.
- mcp_server: the remember_many per-fact schema documents evidence_source and states the link predicate (subject_key or evidence_source) matching the engine, so schema-driven agents actually get wired siblings.
- cli: only MemoryService construction maps through _startup_error via a dedicated _ServiceStartupError marker; command-phase OSError/RuntimeError/etc. report honestly as command failures with a value-free strerror instead of advising engraphis-init --check for a directory argument.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
The release workflow's ENGRAPHIS_INDEX_ROOTS omitted tempfile.gettempdir(), which ci.yml has included since #159. Two suites seed fixtures under the system temp dir and call index_repo against them: - eval/code_arm.py (via test_eval_code_arm.py) - tests/test_documents.py XML/container attack fixtures Both failed on every Python leg of the v1.6 release run (run 32852978103), blocking the PyPI publish. Same root cause silently blocked the Aug 15 v1.7/v1.6.1 tag runs. Ported the third approved root to all five pytest invocations that run the full suite or fixture-dependent modules; the encryption job keeps the narrower two-root set, matching ci.yml. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Summary
Verification
Notes