Drift Dashboard

Adapted from docs/admin/DriftDashboard.md in the Wikantik repository; current as of release 2.4.56.

This page is for curators and administrators who keep the wiki's metadata healthy. It explains the Drift screen at /admin/drift, what each row means, and what to do about it, and it covers two related checks that have no dashboard panel: structural conflicts and stale citations. Drift is metadata that has wandered away from the schema: it never breaks a page, but it weakens search, the Knowledge Graph and the ontology, so the dashboard tracks the count over time as a burn-down.

Read the dashboard

The screen (AdminDriftPage.jsx, backed by AdminDriftResource) shows the latest sweep and compares it to the one before.

ElementMeaning
Last sweep, Triggered by, Pages scanned, Durationwhen the latest sweep ran, whether it was scheduled or manual, and how much it covered
SHACL not checked badgethe ontology subsystem was off or failed during the sweep, so the shacl rows are missing, not zero
Table rowsone row per (family, code, severity): Family, Code, Severity, Count
Δchange in count since the previous sweep (+n or -n); an em dash means the code did not exist in the previous sweep
Trendsparkline of that code's count across the sweeps of the last 30 days
Pages (Show/Hide)expands a live list of the offending pages for that code
Run sweep nowstarts a sweep in the background; the screen shows progress until it finishes

The page list under Show is recomputed when you open it, never read from the snapshot, so it shows what is wrong now. If you already fixed the pages it says so (No pages currently affected). Each entry links to the page's editor and shows the field, the message and, where one exists, a suggested replacement.

How sweeps run

A sweep validates the frontmatter of every page and, when the ontology is enabled, checks the materialised graph against the SHACL shapes. Only one sweep runs at a time; a second request returns HTTP 409 and the screen shows "A sweep is already running." Sweeps run in two ways:

You can also poll the REST endpoints directly:

curl -u admin:... http://localhost:8080/admin/drift/summary
curl -u admin:... 'http://localhost:8080/admin/drift/trend?days=30'
curl -u admin:... 'http://localhost:8080/admin/drift/pages?family=frontmatter&code=tags.kebab'
curl -u admin:... http://localhost:8080/admin/drift/status

days is clamped to 1-365.

Fix each kind of drift

Family frontmatter

These come from SchemaDrivenFrontmatterValidator, the same validator that runs on every save, so a code you see here is also what an editor sees in the frontmatter editor. Fix the page and the count drops at the next sweep. Most are warnings, so pages still save. The Severity column tells you which.

CodeSeverityWhat it meansWhat to do
yaml.parseERRORthe page's frontmatter block is not valid YAMLopen the page, fix the YAML (quote values that contain a colon); GET /admin/frontmatter-issues lists these with line and column
type.noncanonical, status.noncanonicalWARNING (ERROR if wikantik.frontmatter.enum.nonCanonical.severity=error)value is outside the canonical set (type: article, hub, reference, runbook, design; status: draft, active, archived)change to a canonical value; the message names a suggested one when it knows it
audience.enum.invalidERRORaudience holds a value other than humans, agents or bothcorrect the value or list
summary.lengthWARNINGsummary is outside 50-160 charactersrewrite it to fit
aliases.length, aliases.list, aliases.blankWARNINGan alias is too long (over 100), the field is not a list, or an entry is blankclean up the alias list
cluster.slug.malformedWARNINGcluster is not lowercase kebab-caseuse the suggested slug, or rename the cluster everywhere with rename_cluster
cluster.undeclaredWARNINGthe page names a cluster that no hub page declarescreate the hub (type: hub plus cluster: <path>) or move the page to a declared cluster
cluster.hub.multivaluedERRORa hub page lists more than one cluster; a hub declares exactly onereduce the hub's cluster to a single value
tags.kebabWARNINGa tag is not lowercase kebab-caserewrite the tag
related.unresolvedWARNINGan entry in related does not match an existing pagefix the name or remove the entry
date.date.malformed, verified_at.date.malformedWARNINGdate is not an ISO date, or verified_at is not an ISO timestamprewrite as YYYY-MM-DD or ISO-8601
verified_by.untrustedWARNINGverified_by is not on the trusted-authors list, so confidence may be reducedverify the page as a trusted author, or add the author to the registry
runbook.* (missing_block, malformed_block, when_to_use_empty, steps_too_few, pitfalls_empty, related_tool_invalid, reference_unresolvable)ERRORa type: runbook page whose runbook: block fails the schemacomplete the runbook: block

Frontmatter fields themselves are described in docs/user/Frontmatter.md.

Family shacl

These rows come from checking the ontology graph against its SHACL shapes. The Code is the property path of the violated shape (for example a wk: predicate), and the severity is always ERROR. A violation that is not tied to a property path is counted under node-scoped. Expand a row to see the focus node and the message; the usual fix is in the Knowledge Graph (correct or remove the offending edge in Admin → Knowledge Graph) or in the page that produced it. The write-time gate refuses new non-conformant edges for the predicates that have shapes, so this family mostly measures edges that predate it or that no shape guards at write time. The same list is at GET /admin/ontology/violations; see Ontology Management.

Structural conflicts

Structural conflicts are defects in the Page Graph's structural index: missing identifiers and a malformed cluster taxonomy. They are not shown on the Drift screen. List them with:

curl -u admin:... http://localhost:8080/admin/page-graph/conflicts
curl -u admin:... 'http://localhost:8080/admin/page-graph/conflicts?kind=HEADLESS_CLUSTER'

Each entry has slug, canonical_id (when present), kind and detail. The kind filter is case-insensitive. The endpoint returns 503 when the structural index is unavailable. The seven kinds (StructuralConflict.Kind):

KindMeaningWhat to do
MISSING_CANONICAL_IDthe page has no canonical_id and was indexed under a synthesised idsave the page again; the save filter assigns a canonical_id automatically
RELATION_ISSUEthe page declares a relation the validator rejectedcorrect the relation in the page's frontmatter
DUPLICATE_CLUSTER_DECLARATIONtwo or more hubs declare the same cluster; reported once per losing hub (the lowest slug wins)keep one hub per cluster and change or retire the others
HEADLESS_CLUSTERa cluster has member pages but no hub declares it (subject is the cluster path)create a type: hub page with that cluster: value
UNDECLARED_CLUSTERa sub-cluster (parent/child) names a parent that no hub declaresdeclare the parent with a hub page
CLUSTERLESS_HUBa type: hub page carries no cluster:, so it declares nothingadd cluster: <path> to the hub
MULTI_CLUSTER_HUBa hub names more than one cluster; only the first is treated as declaredleave a single cluster on the hub

Duplicate declarations are refused at save time only when wikantik.cluster_declaration.enforcement.enabled is true; it defaults to false, so duplicates can still exist. Enable it only after you confirm none do.

Stale citations

A page can ground a claim in another page's section with inline cite:// markup. At save time those citations are parsed into the citations table, pinned to the target's version and hashed by span, and graded as the target changes. Staleness is a patient curation task, never a save-time error, and a changed target version alone does not make a citation stale. Citation tracking is on by default (wikantik.citations.enabled) and needs a datasource. The dashboard has no citation panel; use the endpoint, or the list_stale_citations tool on /knowledge-mcp (see MCP Integration):

curl -u admin:... http://localhost:8080/admin/drift/citations
curl -u admin:... 'http://localhost:8080/admin/drift/citations?page=SomePage'

Without page you get the corpus-wide counts. With page you also get that page's outbound citations (claims it makes) and its inbound citations that are no longer current (other pages relying on it). Each entry has the source and target canonical ids, target heading path, span text, claim text, status and pinned version.

StatusMeaningWhat to do
currentthe pinned span is still present in the target's cited sectionnothing
stalespan drift: the pinned span no longer appears in that section (the text changed, moved, or the heading was renamed)re-read the target; if the claim still holds, edit the citation's quoted span or repoint it, which creates a new citation pinned to the target's current version; if not, correct the claim
target_missingthe target page was truly deleted (a rename does not count)point the citation at the new location, or remove the claim

When citations are disabled the endpoint answers 200 with zero counts and empty lists. Citations are a third edge type, separate from Page Graph wikilinks and Knowledge Graph entities; the design is in docs/superpowers/specs/2026-06-14-phase-3-citation-edges-design.md.

Where to go next


Cluster Hub: Wikantik Platform Development Hub: Architecture, MCP Protocols, and Agent Briefings (wikantik-development)