Skip to content

feat: Add authority and traceability opt-ins (COG-6279) - #4628

Open
Vasilije1990 wants to merge 4 commits into
devfrom
vasilije/cog-6279-audit-authority-optins
Open

feat: Add authority and traceability opt-ins (COG-6279)#4628
Vasilije1990 wants to merge 4 commits into
devfrom
vasilije/cog-6279-audit-authority-optins

Conversation

@Vasilije1990

Copy link
Copy Markdown
Contributor

Description

An external audit (right-to-reply issue on the article "your memory has no authority model", audited commit fd5045f6) validated four gaps in cognee's authority model. This PR is the minimal code response — three features, each default-off or default-byte-identical, so the standard pipelines are unchanged unless opted into.

1. COPY_LOCAL_FILES — original-bytes snapshot (default false)

Local file paths have always been ingested by reference: cognee records the path and content_hash, which can verify the original but not recover it once the file moves or is deleted. With the flag on, local-path originals are snapshotted content-addressed into cognee-managed storage at add time via the existing save_data_to_file_detailed machinery, so original_data_location stays resolvable and hash-verifiable. s3:// inputs remain pass-through by design.

2. ONTOLOGY_MODE=strict — entity-grounding validation (default annotate)

The ontology pass has always been annotation-only: matched entities are canonicalized and enriched, unmatched entities retained verbatim. Strict mode drops extracted entities with neither a class match on their type nor an individual match on their name, filters edges touching dropped nodes, and logs the dropped counts. Scope is entity grounding only — no domain/range/cardinality/disjointness reasoning, and relationship names are not checked against the ontology (documented in the docstrings and .env.template).

3. Assertion authority + superseded-edge filtering

  • DataPoint.assertion_source (user_stated / document_extracted / llm_inferred) is stamped under the existing COGNEE_PROVENANCE_MODE gate (default lightweight): user_stated for session-bridged facts, document_extracted otherwise; cross_connect_entities and detect_contradictions write llm_inferred. Copied onto edge properties so the conflict resolver can see it.
  • The functional_relationships temporal conflict resolver now ranks authority first, then recency — an older user-stated fact beats a newer LLM inference. Untagged/pre-existing edges rank as document_extracted and resolve byte-identically to today (locked by a regression test).
  • Graph retrieval skips edges tagged superseded=True at both choke points (CogneeGraph projection and the hybrid entity lane). Safe by construction: the tag only ever exists for callers who opted into functional_relationships. contradicts edges stay visible; precomputed triplet embeddings and raw chunk text are unfiltered (documented).

Test plan

  • 165 unit tests across the touched areas (ontology, ingestion, resolver, provenance stamping, memify, CogneeGraph, hybrid entities, catalog schema) — all pass on this branch
  • 12 ingestion integration tests, including the new snapshot test: copy survives os.remove(source) with md5 == content_hash
  • Byte-identity regression tests at every hook site (untagged edges, annotate mode, flag-off ingestion)
  • pre-commit run --all-files clean

Linear: COG-6279

🤖 Generated with Claude Code

Vasilije1990 and others added 3 commits August 23, 2026 13:01
Description: Local file paths are ingested by reference — cognee records
the path and content_hash, which can verify the original but not recover
it once the file moves or is deleted. An external audit flagged that a
returned chunk can therefore not always be traced back to source bytes.
COPY_LOCAL_FILES=true (default false, default path byte-identical)
snapshots local-path originals content-addressed into cognee-managed
storage at add time via the existing save_data_to_file_detailed
machinery, so original_data_location stays resolvable and hash-verifiable
after the source disappears. s3:// inputs remain pass-through by design.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Description: The ontology pass has always been annotation-only — matched
entities are canonicalized and enriched, unmatched entities are retained
verbatim, and nothing is ever rejected. An external audit asked whether
a strict validation mode exists; it did not. ONTOLOGY_MODE=strict
(default annotate, default pipeline byte-identical) drops extracted
entities with neither a class match on their type nor an individual
match on their name, filters edges touching dropped nodes, and logs the
dropped counts. Scope is entity grounding only: no domain/range/
cardinality/disjointness reasoning, and relationship names are not
checked against the ontology.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Description: An external audit found that no stored classification
distinguishes a user statement from an LLM-inferred fact, that no
conflict path consults provenance, and that superseded edges tagged by
the opt-in functional_relationships resolver remain fully visible to
every retriever. This adds one coherent supersession lifecycle:

- assertion_source (user_stated / document_extracted / llm_inferred) is
  stamped on DataPoints under the existing COGNEE_PROVENANCE_MODE gate
  (user_stated for session-bridged facts), copied onto edge properties,
  and written as llm_inferred by cross_connect_entities and
  detect_contradictions.
- The temporal conflict resolver ranks authority-then-recency, so an
  older user-stated fact beats a newer LLM-inferred one; untagged edges
  rank as document_extracted and resolve byte-identically to before
  (locked by a regression test).
- Graph retrieval skips edges tagged superseded=True at both choke
  points (CogneeGraph projection and the hybrid entity lane). Safe by
  construction: the tag exists only for callers who opted into
  functional_relationships. contradicts edges stay visible; triplet
  embeddings and raw chunk text are unfiltered (documented).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## Description

Stacked on #4628 — documentation follow-up to the audit-response
features. Retarget to `dev` (or let GitHub retarget automatically) once
#4628 merges.

An external audit validated that cognee's docs overclaimed in places and
left the temporal/authority model undocumented. This PR makes the
documentation state the boundaries precisely:

- **CLAUDE.md**: new *Temporal Model & Assertion Authority* subsection —
uni-temporal default (ingestion clock), opt-in event time via
`temporal_cognify`, authority-then-recency supersession with
retrieval-side filtering of `superseded` edges, dormant `valid_to`/Zep
`valid_at` fields — plus the `ONTOLOGY_MODE` line in the ontology config
block.
- **`DataPoint.valid_to` comment**: states that no default pipeline
writes it (`close_node` has no in-pipeline caller).
- **`close_node.py` docstring**: application-level helper only; requires
a backend with partial node updates (currently Ladybug).

No behavior change; documentation and comments only.

## Test plan
- [x] `pre-commit run --all-files` clean
- [x] Comments/docs only — no production code touched

Linear: COG-6279

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@NMZivkovic

Copy link
Copy Markdown
Collaborator

ontology_env_config.py:40 breaks ontology-via-env outright. The dict returned by to_dict() exists only to be splatted into get_ontology_resolver_from_env(ontology_resolver, matching_strategy, ontology_file_path), which takes exactly three parameters and no **kwargs, so adding the fourth key means anyone with ONTOLOGY_FILE_PATH set gets TypeError: got an unexpected keyword argument 'ontology_mode' out of cognee.cognify() — before any ingestion work. That's the setup documented in .env.template, CLAUDE.md and catalog/entries/use-cases/custom-ontology.yaml, and it's what examples/demos/comprehensive_example does at line 34. Drop the key from to_dict() (the mode is already read separately via get_ontology_env_config().ontology_mode), or pass the three fields explicitly at the call site.

CI didn't catch it because test_none_config_uses_env_when_configured patches get_ontology_env_config to a hand-written 3-key to_dict and mocks get_ontology_resolver_from_env, so neither half of the mismatch can surface. Worth a test that builds the real config and stubs only the resolver's body.

Two other things. ontology_mode: Literal["annotate", "strict"] with no fallback means ONTOLOGY_MODE=Strict raises a pydantic ValidationError that kills every cognify run, including runs with no ontology at all, since the config object is constructed before the file-path guard. get_provenance_config() already has the pattern for this — plain str, lowercase, warn and fall back. And the stale-superseded case: nothing ever clears the tag, and Neo4j's SET rel += edge.properties merges rather than replaces, so an edge that gets re-asserted and later wins keeps superseded=True from the earlier round. Now that CogneeGraph.py:198 skips superseded edges, both edges in a group can end up hidden and the current, correct fact disappears from GRAPH_COMPLETION while sitting right there in the graph. Having tag_superseded_edges write the winner back with superseded=False would close it.

Minor: supersession_reason is hardcoded to "superseded by a more recent ... assertion", but with authority-first ranking the winner is often the older edge, so test_older_user_stated_edge_beats_newer_llm_inferred is producing an audit string that contradicts the timestamps on the same edges.

Also worth correcting the PR description — assertion_source stamping is default-on, not opt-in. _stamp_provenance runs whenever provenance isn't disabled, and the default mode is lightweight, so every user on defaults gets the new property on every node and edge. Storage-wise it's safe and the ranking is byte-identical for untagged edges, so it isn't a bug, but "each default-off or default-byte-identical" isn't true of that write path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants