{% extends "base.html" %} {% block title %}DocDrift β€” Document Intelligence β€” ICDEVβ„’{% endblock %} {% block content %}
CUI // SP-CTI

πŸ›°οΈ DocDrift β€” Is this document still true?

Drift β†’ document impact β†’ grounded regeneration (HITL) β†’ NIST 800-53 re-map. Fed by every docmod pack: network, crypto, software, policy, approved changes, and cited evidence.

Tech Writer β†’ ← Back to DIC
How this page gets data

There is nothing to upload here. DocDrift is fed automatically: docmod packs check your documents against reality β€” network topologies, EOL catalogs, crypto rules, approved change records and the evidence each document cites β€” and tell you which documents and NIST 800-53 controls that drift made wrong. To upload a document use Document Intelligence; to write one use Tech Writer; to rebuild one from multiple sources use DocGen.

Drift is a comparison, so it needs a baseline: (1) a topology in the Network Canvas, (2) a saved baseline version of it, (3) a later change to that topology. The ndc_topology_drift reflex then runs every 4h β€” or use the button below.

{% if topologies and baselines_saved == 0 %}
⚠ None of your {{ topologies|length }} topologies has a saved baseline yet, so no drift can be detected. Pick one below and choose Save as baseline to start tracking it.
{% endif %}
{% for stage in ['Drift detected','Impact scored','Regen queued','NIST re-mapped'] %}
Stage {{ loop.index }}
{{ stage }}
{% if not loop.last %}
β†’
{% endif %} {% endfor %}
{# One place where a state's colour, label and meaning are declared. A second copy anywhere in this file would let the legend and the badges disagree. #} {% set state_style = { 'current': {'fg': '#8fd08f', 'bg': '#11261a', 'bd': '#2f6b42', 'icon': 'βœ”', 'label': 'Current', 'meaning': 'A domain pack checked it and it is up to date.'}, 'deprecated': {'fg': '#e0c080', 'bg': '#2a2313', 'bd': '#6d5a24', 'icon': 'β–²', 'label': 'Deprecated', 'meaning': 'Past its life. No successor was named.'}, 'superseded': {'fg': '#f0a06a', 'bg': '#2c1c12', 'bd': '#7a4522', 'icon': '⇄', 'label': 'Superseded', 'meaning': 'Stale, and a cited replacement exists.'}, 'unknown': {'fg': '#c0a8f0', 'bg': '#1d1832', 'bd': '#4c3c86', 'icon': '?', 'label': 'Unknown', 'meaning': 'We asked and NOTHING could answer. This is a finding, not a pass.'}, 'not_resolved': {'fg': '#8ba0bd', 'bg': 'transparent', 'bd': '#3a5170', 'icon': 'β€”', 'label': 'Not checked', 'meaning': 'Nobody has asked yet. This is not a clean bill of health.'}, 'refused': {'fg': '#f08a8a', 'bg': '#2b1414', 'bd': '#7d2f2f', 'icon': 'βœ•', 'label': 'Refused', 'meaning': 'The resolution was assembled and REJECTED (an uncited claim).'} } %} {% set health_style = { 'ok': {'fg': '#8fd08f', 'icon': '●', 'label': 'All backends answered'}, 'degraded': {'fg': '#e0c080', 'icon': '◐', 'label': 'Some retrieval backends died'}, 'failed': {'fg': '#f08a8a', 'icon': 'β—‹', 'label': 'Every backend died β€” retrieval is down'}, 'unmeasured': {'fg': '#6a7f9c', 'icon': 'β€”', 'label': 'Never measured (nothing was resolved)'} } %} {% set advisory_style = { 'not_consulted': {'fg': '#7a8cb0', 'label': 'Not consulted', 'meaning': 'No expert was asked. This is NOT "the expert had no concerns".'}, 'unavailable': {'fg': '#e0c080', 'label': 'Consulted β€” unavailable', 'meaning': 'An expert was asked and the rung errored. An outage, not an absence of opinion.'}, 'no_opinion': {'fg': '#8ba0bd', 'label': 'Consulted β€” no opinion', 'meaning': 'An expert was asked, answered, and offered nothing.'}, 'opinion': {'fg': '#9bd0ff', 'label': 'Advisory opinion', 'meaning': 'An ACE domain expert gave an opinion. Advisory only.'} } %} {% macro state_badge(state, size='normal') %} {% set s = state_style.get(state, state_style['not_resolved']) %} {{ s.icon }} {{ s.label }} {% endmacro %} {% macro health_badge(health, failed) %} {% set h = health_style.get(health, health_style['unmeasured']) %} {{ h.icon }} {%- if health == 'unmeasured' %} not measured {%- elif health == 'ok' %} evidence ok {%- elif health == 'failed' %} retrieval down {%- else %} {{ failed|length }} backend{{ '' if failed|length == 1 else 's' }} down {%- endif %} {% endmacro %}

Drift Findings β€” verdict, evidence & unknowns

{% if currency.unavailable %}
βœ• The currency panel could not load: {{ currency.unavailable }}. This is a panel outage β€” it is not a statement that these findings are fine.
{% endif %}

Each finding carries three independent answers from cortex.resolve(), and they are kept apart deliberately. Verdict is deterministic β€” a domain pack derived it from catalog rows, EOL dates and rulebook matches; no model produced it, and no backend outage can change it. Evidence says whether the retrieval fan-out behind the citations worked, which is a different question: a verdict can be solid while the sweep behind it is degraded. Advisory is an opinion an LLM authored at query time β€” it is shown, it is never evidence, and it never reaches the verdict.

Verdict β€” deterministic, from the domain packs
{% for state in ['current','deprecated','superseded','unknown','not_resolved','refused'] %} {% set s = state_style[state] %}
{{ currency.summary.get(state, 0) }}
{{ s.icon }} {{ s.label }}
{{ s.meaning }}
{% endfor %}
Evidence sweep β€” a separate axis; a dead backend is not a verdict
{% for health in ['ok','degraded','failed','unmeasured'] %} {% set h = health_style[health] %}
{{ currency.evidence_health.get(health, 0) }}
{{ h.icon }} {{ h.label }}
{% endfor %}
{{ currency.total }} finding{{ '' if currency.total == 1 else 's' }}, {{ currency.distinct_entities }} distinct entit{{ 'y' if currency.distinct_entities == 1 else 'ies' }} Β· batch capped at {{ currency.batch_cap }} Β· stale after {{ currency.stale_after_hours }}h {%- if not currency.enabled %} Β· panel disabled in args/dic_docdrift_config.yaml{% endif %}
{% for f in currency.findings %} {% set r = f.resolution %} {# An entity can appear on SEVERAL drift events (TLS 1.1 is on three of the 72 live ones), so a row is paired with its detail row by INDEX, not by entity. Keying on entity meant `querySelector` matched the first duplicate only: the second and third rows toggled somebody else's panel open and kept a stale one of their own. #} {% else %} {% endfor %}
Source Entity Severity Verdict Evidence Detected
{{ f.source }} {{ f.entity }} {{ f.severity }} {{ state_badge(r.state) }} {% if r.stale %}stale{% endif %} {{ health_badge(r.evidence_health, r.backends_failed) }} {{ f.detected_at }}
No drift events yet. Drift is detected by comparing a topology against its saved baseline β€” {% if baselines_saved == 0 %}and no baseline is saved yet, so there is nothing to compare. Use Save as baseline above, change the topology, then run the check. {% else %}nothing has changed since the last baseline.{% endif %} Open Network Canvas

Regeneration Queue (HITL)

{% for r in regen_queue %} {% else %} {% endfor %}
DocumentImpact StateQueued
{{ r.document_id }}{{ r.impact_level }} {{ r.state }}{{ r.queued_at }}
Queue empty. A document is queued here only when drift is detected and that topology maps to a DIC collection (tag mapping lives in args/dic_canvas_integrations.yaml). Nothing is regenerated without your approval. Tag a collection

NIST 800-53 Re-map & SSP Fragments

{% for s in ssp_fragments %} {% else %} {% endfor %}
ControlDocument Status
{{ s.control_id }}{{ s.document_id }} {{ s.status }}
No control re-maps yet. SSP fragments are drafted from a queued regeneration item and always land as pending_review β€” CoD-verified, AI-labeled, and never auto-published.
{% set iqe_canvas = "dic" %} {% set iqe_api_route = "/document-intelligence/api/iqe-query" %} {% set iqe_title = "IQE Query β€” DocDrift" %} {% set iqe_examples = [ {"label": "Critical drift", "query": "foreach d in dic.drift_events where d.severity == \"critical\" select d.source, d.entity, d.detected_at"}, {"label": "Open regen", "query": "foreach r in dic.regen_queue where r.state == \"queued\" select r.document_id, r.impact_level"} ] %} {% include "includes/iqe_query_widget.html" %} {% endblock %}