How-to
Detect contradictions and track knowledge gaps
Use memory_check to find inconsistencies and memory_gap to log questions the memory couldn't answer.
Two intelligence-layer surfaces work together: check finds places where the memory disagrees with itself, and gap records questions it couldn’t answer well. This recipe shows how to use both.
Run a contradiction check
Section titled “Run a contradiction check”quaid check people/alice --db ~/memory.db # one pagequaid check --all --db ~/memory.db # every pagequaid check --all --type assertion_conflict --db ~/memory.db # filter by contradiction typecheck re-extracts the page’s assertions, then flags pairs of subject-predicate-object triples about the same (subject, predicate) whose objects differ while their valid_from / valid_until windows overlap. Detection is exact-match over the assertions table — it does not infer conflicts from arbitrary prose, timelines, or unstructured frontmatter.
Output is human-readable by default (each line starts with the contradiction id, e.g. #3 [a] ↔ [b]: …), JSON with --json. Materialized contradictions live in the contradictions table.
Where assertions come from
Section titled “Where assertions come from”Assertions are populated three ways:
-
A small frontmatter allowlist — the scalar fields
is_a,works_at, andfounded:---title: Alicetype: personworks_at: River AI--- -
Parsed from a
## Assertionssection in the body. Three sentence patterns are recognized:X works at Y,X is a Y, andX founded Y. Prose outside this section is never scanned. -
Mirrored from extracted-fact pages. Facts the conversation pipeline writes under
extracted/(kindsdecision,preference,fact,action_item) are mirrored automatically as(type key, kind, summary)triples withasserted_by = 'extraction'— no## Assertionsheading needed. Superseded fact pages are skipped, so resolved history doesn’t conflict with current truth.
You can also insert rows into assertions directly (asserted_by = 'manual'); re-running check preserves manual rows.
Resolve a contradiction
Section titled “Resolve a contradiction”Each contradiction has an id (shown by quaid check and memory_check). Resolve it with:
# Keep one page: the other page in the pair is superseded by it,# and the contradiction is stamped resolved.quaid check --resolve 3 --keep people/alice --db ~/memory.db
# Dismiss without superseding (e.g. false positive):quaid check --resolve 3 --db ~/memory.dbOver MCP, pass resolve (and optionally keep) to memory_check:
quaid call memory_check '{"resolve":3,"keep":"people/alice"}' --db ~/memory.dbResolution semantics:
- Resolving stamps
contradictions.resolved_at; resolved rows disappear fromquaid check,memory_check, and thememory_statscontradiction count. - A resolved contradiction is a durable dismissal: the identical conflict (same pages, same description) is not re-raised on later checks. A materially different conflict (e.g. a new object value) is still detected as a new contradiction.
- Superseding a page — via
--keep, asupersedes:write, or the extraction pipeline — auto-resolves open contradictions touching the superseded page.
When neither side should win outright, fix the data instead:
- Both true at different times? Add
valid_untilto the older assertion so the intervals don’t overlap. - Both true now, but seem to disagree? Edit the predicate; the contradiction was lexical, not semantic.
Log a knowledge gap
Section titled “Log a knowledge gap”When a query returns weak results — or you anticipate one will — log it:
quaid call memory_gap '{"query":"who covers our European compliance?","context":"weekly briefing"}' --db ~/memory.dbBy default this stores the hash of the query, plus the context, plus a default sensitivity = internal. The raw text is not persisted unless explicitly approved. See the Sensitivity contract.
List unresolved gaps
Section titled “List unresolved gaps”quaid gaps --db ~/memory.db # unresolved (default)quaid gaps --resolved --db ~/memory.db # include resolvedquaid gaps --limit 100 --db ~/memory.dbResolve a gap
Section titled “Resolve a gap”You add a page that answers the question, then either:
- Manually mark it resolved — set
resolved_atandresolved_by_sluginknowledge_gapsdirectly. - Use the
researchskill — the embedded skill has a documented workflow for ingesting new material in response to a gap and marking the gap resolved when novelty passes a threshold.
The skill is the recommended path; it keeps the gap → research → ingest → resolve loop legible.
Common workflow
Section titled “Common workflow”- Daily:
quaid statsshows unresolved gap counts;quaid gaps --limit 5is the work queue. - Weekly:
quaid check --allto catch contradictions you’ve introduced. - Monthly:
quaid validateto catch link integrity issues. See Manage skills for analertsskill that wires this into your day automatically.
How agents use this
Section titled “How agents use this”Agents log gaps when their memory_query results don’t meet a confidence threshold. The hash-only default means the memory accumulates a privacy-safe ledger of “where can’t we answer well?” over time, even when individual queries are sensitive.
When an agent escalates a gap to external research (e.g. via the enrich skill), it must transition the sensitivity tier with explicit approval — see Sensitivity contract.