Skip to content

Drift & enforcement

For org admins.

Beyond checking docs against your standard (see Standards & conformance), Hekkos tracks drift — when the code and the docs disagree — and gives you the levers to act on it. This guide covers drift findings and their severity model, how drift surfaces on open PRs, the optional merge gate, documentation-authority tiers, the standing drift PR, and the Drift tab on the Sources page.

Three lenses: drift, principle, advisory

Hekkos surfaces three kinds of finding, unified as violations on the Violations tab of Home (there’s no standalone Violations page — the old /violations bookmark redirects into that tab). Each answers a different question:

LensQuestionExample
DriftDoes the code match what’s documented?a route the docs describe no longer exists
PrincipleDoes the code obey your declared rules?an /orgs/{id} route is missing its tenant-auth middleware
AdvisoryIs the code good practice, even when the docs agree?the code uses AES-128 — and so do the docs, but it’s legacy

Category. Every violation carries a subject categorysecurity, tenancy, architecture, dependencies, … — inherited from the rule it belongs to. That’s what lets you ask “is password encryption respected throughout the app?” and filter to the security category.

Principles are rules you author (Standards → Principles): a scope (which code they apply to) plus a condition, with a polarityrequired (the code must satisfy it) or discouraged (flag any code that matches). Curated best-practice principles (weak crypto, deprecated dependencies) ship enabled and advisory by default:

Where a rule comes from. A principle is authored one of three ways: turned on from the catalogue (a curated preset), hand-written in the CEL editor, or extracted from a standards doc. Extraction reads a source you opt in as a standards source (a repo’s docs or a Confluence space), turns each normative statement (“must use…”, “do not use…”) into an advisory rule — binding a catalogue preset where one fits, else a generated check — and links it back to the document. An extracted rule carries a Source chip on its card; like every rule it starts advisory, and only becomes enforcing if you flip its Blocking toggle. A statement that maps to no precise check is skipped (not silently turned into a vague rule). Extraction, the standards documents behind it, and the Org → Initiative → Repo scope tree all live on the dedicated Policies page.

AI-judged rules (advisory). Some rules can’t reduce to a precise check — “error messages must not leak internals”, “services own their persistence”. For those, an admin can opt the org into the judged lane (Standards → Principles, off by default): during a scan, an AI judges the org’s semantic principles against the code and raises advisory findings. Every verdict is grounded (a finding citing code not shown to the model is discarded) and the lane never blocks a merge — only a deterministic rule can. Turning it on sends code context to the model; self-hosted deployments route it to a local model.

Scope: org, initiative, or project. A rule applies org-wide by default. Group repos into an initiative (a department’s platform, say) and scope a rule to it in the editor’s Applies to selector — it then applies only to that group. A repo is judged against the union of its org-wide rules plus every initiative it belongs to; deleting an initiative reverts its rules to org-wide. they comment, never block, and you can disable any of them or ignore an individual finding as not-applicable (it won’t come back until the code changes). When you want one enforced, flip its Blocking toggle — a deterministic principle can then gate merges through the hekkos/principles gate (fuzzy rules stay advisory). Principle and advisory findings are kept out of the Trust/Accuracy score and the drift posture — those remain a pure code-vs-docs measure.

On the Violations tab, each finding’s code location is a deep-link to the offending line on the default branch, and a principle finding links its rule to the matching card on the Principles page. A Blocking badge marks the findings that can actually stop a merge (the hekkos/principles gate), distinct from severity. When a code area concentrates the violations — at least 40% of at least 10 — a one-line nudge names that folder so you can clear the most at once; it’s read-only (the fix is the code, so there’s no ignore-folder action here, unlike the Drift tab).

Real-state drift — when the code and the docs disagree

Beyond section conformance, Hekkos tracks whether your docs match the actual code. It extracts the real state (routes, env vars, functions, types, CLI flags) and diffs it against what the docs claim, producing drift findings with a severity model:

  • critical — a doc points users at a route / env var / flag that no longer exists (they’ll hit failures).
  • warning — a documented symbol is gone, or a documented signature no longer matches the code.
  • info — real state that nothing documents.

The drift count is actionable drift only. The Drift severity on Home → Repositories, the repo’s Drift tab, and the drift posture on Insights all count findings where the docs disagree with the code (critical + warning). The info class — code nothing documents — is a coverage signal, not drift: it’s reflected in your doc-coverage score, not in the drift number. (Otherwise every undocumented internal helper would read as “critical drift,” which is what used to make the count balloon into the thousands.)

Wrapper-read env vars and flags aren’t false positives

An env var or flag your code reads through a config-loader wrapper — e.g. get("DATABASE_URL") rather than a literal os.Getenv("DATABASE_URL") — used to get flagged as a critical “documented but no longer in the code” finding, even though the variable is very much still in use. It no longer does. Hekkos now treats the bare name appearing anywhere in the code text as evidence the code still uses it, so a documented-but-wrapper-read variable is quietly matched instead of reported. Build-time env vars read the Vite/Astro/SvelteKit way — import.meta.env.PUBLIC_APP_URL — are recognized as a direct read too, alongside process.env.X and os.getenv, so a documented var read only through import.meta.env is matched, not flagged. Only a documented env var or flag whose name appears nowhere in the code stays a loud critical — that’s the genuinely-removed case you actually want to hear about.

What no longer counts as drift

A drift finding is only useful if it names something real. Hekkos now discards a range of look-alikes before they can raise a finding, so the loud slots go to genuine mismatches:

  • Placeholders. Backticked stand-ins in a template or example — REPLACE_ME, CHANGE_ME, YOUR_TOKEN, EXAMPLE_KEY, TODO, and the like — are never treated as env-var contracts.
  • Ordinary words and SQL. Common prose verbs (get, set, run, use) and SQL keywords (EXISTS, COUNT, IN, ON, DISTINCT, …) quoted in a doc are no longer mistaken for function or symbol names that “went missing”.
  • Filenames. A token that is shaped like a file (budget_rescan.go) is no longer read as a code symbol that vanished.
  • Library calls. A symbol that belongs to a dependency you import (chi.URLParam) is matched against that dependency, not flagged as a missing symbol of your own.
  • Platform config. An UPPER_SNAKE name mentioned right beside GitHub-platform wording — a repository secret, an Actions variable, an environment secret — is recognized as CI/platform configuration, not an application env var your code must still read.

Env vars in workflows and ops files count as evidence

An env var doesn’t have to appear in your application code to count as “still used”. Hekkos now reads your ops surface as corroborating evidence — GitHub Actions workflow YAML, shell scripts, Dockerfiles, Makefiles, .env files, and Terraform / OpenTofu (.tf, .tf.json, and .tofu). A variable a doc mentions that lives only in .github/workflows/deploy.yml (say GCP_PROJECT_ID) is matched there instead of being reported as “documented but gone”.

Infrastructure is checked for drift too: a doc that names a Terraform resource, variable, output, or module address is matched against the actual .tf / .tf.json / .tofu files. The address can be written the way you’d reference it in HCL — bare (aws_s3_bucket.assets), module-qualified (module.vpc.aws_subnet.private), or for a for_each/count instance (aws_instance.web[0]) — and all resolve to the same resource, so a doc pointing at a resource that was renamed or removed is caught however it’s addressed.

The unverified tier

Not every doc reference can be checked with confidence. A bare, unfalsifiable anchor — a plain lowercase word a doc happens to backtick, with nothing in the code to confirm or deny it — used to be able to render as a loud “Critical”. It no longer can. Hekkos sorts findings into two tiers:

  • Verified findings are the ones it can stand behind. These lead the PR, count in the headline, can be acknowledged, and can block (where blocking is enabled).
  • Unverified findings are low-confidence guesses. They are collapsed into a single closed “Unverified” section, excluded from every headline count, never written into your docs, never given an acknowledge handle, and they never block a merge — because Hekkos genuinely can’t tell whether they’re drift. They’re shown only so nothing is hidden from you.

If a later extractor improvement lets Hekkos confirm one of these, it graduates into the verified tier on the next scan and starts counting normally.

On an open PR

When a pull request introduces new drift (its code change leaves a doc stale), Hekkos posts one comment summarizing what drifted. Findings are grouped by the doc file each stale claim lives in — the unit you’d actually open to fix them — under an honest headline count (e.g. 4 doc claims out of date · across 2 docs), not sorted into a wall of severity buckets.

  • The comment is self-updating: it is edited in place on every push (never restacked into a wall of duplicate reviews) and is deleted once the drift is resolved.
  • Each doc’s findings collapse into an expandable group, worst-affected doc first. When a single push turns up more than a screenful, the surplus is folded into one “+N more claims across M more docs” line that points at the repo’s Drift tab — so the comment stays skimmable no matter how much drifted.
  • Lower-confidence guesses never lead: they are held in a separate collapsed Unverified section and left out of the headline count (see The unverified tier below).
  • By default this is comment-only — Hekkos never blocks your merge.
  • An org can opt into blocking (pr_open_blocking_enabled in the drift config), in which case a canonical-tier finding at or above pr_open_block_severity requests changes (reference/spec sources never block — see authority below).

A suggested doc fix, written for you

Separately from the drift summary, when a PR’s code change leaves an existing doc stale, Hekkos may post a second comment — “Documentation Drift Detected” — containing the doc update it recommends, drafted from your diff. This is the same suggestion engine behind suggestion PRs, applied to the change you’re already making so the doc fix can ride along in review.

  • Trivial PRs never get one. Doc-only, test-only, and dependency-bump changes are filtered out before any analysis, as are diffs with no visible doc impact — most PRs see no comment at all.
  • One comment, refreshed in place. On each push Hekkos edits that same comment with its latest draft rather than stacking a new one, so the PR carries exactly one suggestion comment and it always reflects the current diff.
  • Dismiss it the same way as any suggestion, by commenting on the PR: /hekkos dismiss <reason>. If the merge gate (below) is enabled, dismissal also clears the hekkos/doc-drift status so the PR can merge.

When the stale line is in your diff, Accept is one click. If the doc claim that went stale sits on a line your PR actually edited, Hekkos posts a native GitHub suggested change on that line — Accept is the Commit suggestion button, no command to type. This only works for the minority case where the stale line is in the PR’s diff; the common case (the stale doc is elsewhere) is handled off-diff by the standing drift PR’s committed fix (below) and the dashboard, not an inline suggestion. A suggestion is only offered for a high-confidence fix with a single, unambiguous locus — Hekkos never rewrites a line it can’t pinpoint.

The merge gate (optional)

If a repo’s .hekkos.yml (see per-repo overrides) sets enforcement.merge_gate: true, Hekkos posts a hekkos/doc-drift commit status on PRs that introduce documentation drift. Combined with a GitHub branch-protection rule requiring that check, this blocks a merge until the drift is addressed or the suggestion is dismissed.

The doc-standards merge gate

Separately from the drift gate above, you can make a doc type block merges when it’s set to Blocking on the Standards page. This is the enforcement side of conformance, and it posts its own status context, hekkos/doc-standards — independent of hekkos/doc-drift, so the two gates never clear or block each other.

What it blocks, and when. On each PR open/update, for every doc type you set to Blocking, Hekkos checks the doc at the PR’s head and fails the hekkos/doc-standards check only when a required doc or section is missing (Missing / Sections missing). It is deliberately narrow and safe:

  • Change-scoped — it only evaluates a Blocking doc type whose file the PR actually touches, so an unrelated PR is never blocked, and an external contributor’s PR can’t fan the check out across every doc type.
  • Fails open — an off-standard/stale doc, a doc type with no authored standard, an unresolved path, a fresh repo not yet scanned, or any transient read error all pass. Editing a standard never retroactively blocks in-flight PRs.
  • Org policy wins — a repo’s .hekkos.yml may tighten enforcement but can never relax an org Blocking doc type.

Because it computes at the PR head, pushing an empty commit or reopening the PR can’t clear a genuine gap — only adding the missing documentation (or waiving the doc type) does.

Turn it on for a repo. Add hekkos/doc-standards to the repo’s required status checks in branch protection — but, as with the drift gate, only after you’ve set at least one doc type to Blocking and seen Hekkos post the check once.

Dismissing the gate

The hekkos/doc-standards check is a real merge blocker, so clearing it is authorized. Comment:

/hekkos dismiss standards <reason>

Only a commenter with write/admin access to the repo (or a Hekkos org admin) can clear it — a read/triage collaborator’s dismiss is refused. The standards token routes the dismiss to this context only; a plain /hekkos dismiss <reason> (or /hekkos dismiss drift …) clears the softer drift nudge and never the standards gate.

The policy conformance gate

The Principle lens (Standards → Principles) checks whether your code obeys a declared rule — “all database access goes through the service layer”, “no weak or outdated encryption”, “routes under /orgs/{id} are tenant-guarded”. Every principle finding is advisory by default: it shows on the PR and the Violations tab, but does not block. When you want one enforced, flip its Blocking toggle, and it gates merges through a third, independent status context — hekkos/principles (named for the Principles page, since it checks code, not documents; separate from hekkos/doc-drift and hekkos/doc-standards).

What it blocks, and when. On each PR open/update, Hekkos evaluates your enabled principles at the PR’s head and fails hekkos/principles only when the PR introduces a violation of a rule you set to Blocking. It is deliberately narrow and safe — the same posture as the other two gates:

  • Introduced-only — a violation already present on the base branch is never gated (you can’t be blocked by pre-existing debt, and touching that file doesn’t trap the PR); only a newly-introduced violation blocks.
  • Deterministic-only — only a rule Hekkos can prove (a value or graph check — Go AST, the import graph) may be set Blocking. Fuzzy/LLM-judged rules stay advisory forever, and a low-confidence (e.g. regex-tier) detection lands in the unverified tail and never gates.
  • Fails open — no enabled blocking rules, no facts, an unsupported language, a fresh repo, or any transient scan error all pass. A scan error can never self-inflict a merge block.

The blocked PR’s Hekkos comment carries a Policy conformance section naming each introduced violation, its severity, and a link to the offending line.

Turn it on for a repo. Set at least one principle to Blocking on the Principles page, let Hekkos post the check once, then add hekkos/principles to the repo’s required status checks. As with the other gates, fork PRs are not gated (Hekkos posts the repo’s own statuses), so scope the required check to repos whose PRs come from branches.

The three gates, side by side

ContextChecksBlocks when
hekkos/doc-driftdocs vs. code (drift)a documented route/env/symbol no longer exists (org opt-in)
hekkos/doc-standardsdocument structurea required doc or section is missing (per-doc-type Blocking)
hekkos/principlescode vs. a declared rulea PR introduces a violation of a Blocking principle

Documentation authority — stop old plans reading as drift

Hekkos tracks documentation wherever your repo keeps it — the root README, the docs/ tree, a package-level site/README.md or web/README.md, .github/ notes — not just the root and docs/. (Anything under vendor/, node_modules/, dist/, build/, or testdata/ is excluded.) That means a real contract in a package README — say a marketing site’s env-var table — is checked for drift like any other doc, but it also means internal notes and review reports now fall in scope.

Not every Markdown file is a current-state contract. A repo often keeps historical plans, specs, and design notes (e.g. under docs/), and by default a route or env var mentioned in a superseded plan — or in an internal scratch note now in scope — would be flagged as drift because the code moved on. Source-tier authority fixes that without deleting anything: tier those paths historical or ignored and they stop raising drift.

Under Settings → Documentation Authority, an admin adds ordered (path glob → tier) rules that apply org-wide — to every repo — first match wins, and anything unmatched stays canonical, so the feature is fully automatic until you tune it:

TierOn a code–doc mismatchCounts as coverageIn chat answersIngested
canonicalLoud drift findings — can block, where blocking is enabledYes — the document of recordYesYes
referenceWarnings only — never blocksYesYesYes
historicalRaises no driftNoNo — excluded, so a cut feature in an old plan won’t resurface in chatYes — kept for lineage
ignoredRaises no drift — never checkedNoNoNo — not ingested, never fetched

For example, docs/rfcs/** → historical quiets an entire back-catalogue of implemented RFCs in one rule. A dry-run button previews the effect against a repo (“N findings, was M”) before you save. Rules live in the org’s drift config (authority_rules) and take effect immediately — no re-scan needed.

This table governs org-wide rules only. To quiet a folder on a single repo rather than everywhere, open that repo on the Sources page and switch to the Drift tab — the concentration banner and /hekkos ignore both write a repo-scoped ignore (below), which does not appear in this table. Documentation Authority shows a read-only count of any repo-scoped ignores and points you to the Drift tab to manage them; the editable list here is org-wide rules only.

Planning trees are historical by default. Documents under plans/, proposals/, or rfcs/at any depth, whether a repo-root plans/ or a nested docs/plans/ — describe intended state, so a mismatch there is expected rather than drift, and Hekkos tiers those trees historical automatically, with no rule needed. (A leading **/ in one of your own authority globs matches the same way: **/plans/** → historical covers both the root and nested plans/ trees, while still respecting segment boundaries — it won’t catch a myplans/.) Your own rules always win (first match), so if a plans/ folder is actually a current-state contract for you, add an explicit … → canonical rule to override. specs/, design/, and adr/ are not defaulted — a spec or ADR can be a live contract, and API specs are matched against code as structured route sources regardless.

Route paths reconcile across mount prefixes automatically: a doc’s full orgs-scoped path (the .../widgets route under /api/v1/orgs/{id}) matches the code’s handler-local registration for /widgets (any framework’s prefixing), so you won’t see a route flagged as both “documented but missing” and “in code but undocumented” just because of an API prefix. (These illustrative paths deliberately omit the HTTP method: the extractor only treats a METHOD /path pair as a route claim, so a guide explaining route matching never accidentally flags its own examples.)

What the detector does NOT flag

The extractor is built to read contracts, not incidental text, so several kinds of mention never become drift:

  • Fenced examples. A route in a diagram fence (```mermaid) or a func signature in a non-Go fence is illustrative, not a contract — only prose, inline code, untagged fences, and route/Go-bearing fences (```http, ```bash, ```go) are read as claims.
  • Removed / planned mentions. A symbol on the same line as an unambiguous removal phrase (“was removed”, “no longer exists”), under a Known Stubs / Planned / Roadmap heading, or under a changelog Removed / Deprecated section (Keep-a-Changelog style), is demoted to the PR’s unverified tail rather than raised as a confident finding. A Changed section is not demoted — a modified-but-live contract still drifts loudly.
  • Example & snippet docs. A code reference in a doc under an examples/ or snippets/ path is illustrative usage, not a live contract, so it is demoted to the unverified tail. Only the doc anchors are demoted — example source code is still inventoried, so a sample never becomes a false “undocumented”.
  • Non-code idioms that share a shape. CSS custom properties (a double-dash design token), CSS or crypto call forms (rgba(…), HKDF(…)), and GitHub webhook events written as a dotted provider.event pair are recognised as collisions and dropped or demoted. (These examples are written without code formatting on purpose — spelled as literal backticked tokens, they would be extracted as the very claims this section says are ignored.)
  • Its own notes. The <!-- hekkos-drift --> note Hekkos injects (below) is stripped before the next scan re-reads the doc, so a note never re-generates the finding it describes.

The standing hekkos/drift PR

Hekkos maintains one standing pull request per repo, on the hekkos/drift branch, that collects the current drift and annotates the affected doc sections with a note. For a changed signature the note shows the current signature (“signature changed to …”); for a removed route/symbol/flag there is no current value to show, so the note states plainly that the reference no longer exists in the code. Its title says what and how many (e.g. docs: 12 doc claims out of date (4 docs)), and its body groups the findings by doc, leads with a short executive summary, separates confident findings from an unverified (lower-confidence) tail, and — when the drift clusters in one folder — nudges you toward the fastest cleanup. Each finding line carries a deep link (↗) to the exact doc line the stale claim sits on, and a signature-change finding also links the code location it now disagrees with, so you can jump straight to what to fix. It is human-safe: if you push your own commits to that branch, Hekkos will not overwrite them — it comments instead.

When the PR first appears

The first drift PR for a repo is introduced gently, so a fresh install doesn’t open with a repo-wide rewrite:

  • It follows your rollout pace. If your org is on the default pilot-first rollout (see Rollout pace), the very first drift PR for a repo is held back until that repo’s baseline scan is a couple of days old, mirroring how suggestion PRs are paced. It opens automatically once that window passes — you don’t have to do anything, and /hekkos regenerate bypasses the wait if you want it now.
  • Volume is capped on first contact. When a repo turns up a large backlog of drift, the first PR writes in-doc notes for only the handful of most-affected docs and tells you how to author the rest — the body still lists the full count, but the diff stays reviewable.
  • Concentration is surfaced, not annotated. If most of the drift is concentrated in one folder, the first PR annotates docs outside that folder and points you at ignoring the concentrated folder first (below) — rather than writing notes into the very folder it’s suggesting you quiet.

A real fix, committed for you

For the deterministic slice of drift — a documented signature that no longer matches the code, where Hekkos knows the exact correct value and can find the stale text unambiguously — the standing PR doesn’t just leave a note asking you to fix it. It rewrites the stale text to the correct value right in the PR’s diff, so Accept is merging the PR. The committed change replaces the note it would otherwise have written; you review a concrete one-line fix, not a “please update this” reminder.

This is held to a strictly higher bar than a note: it only fires on a high-confidence finding with a concrete replacement value and a single, unambiguous occurrence of the stale text. Anything Hekkos can’t rewrite safely — a deletion or rename with no replacement value, a claim buried in prose, an ambiguous match — stays a note for you to resolve by hand or acknowledge. Everything else that can’t be committed in-diff on your PR (the off-diff majority) lands here, on the standing PR, rather than as an inline suggestion.

One interaction model — Accept, Dismiss, Ignore

Everything Hekkos surfaces — a drift finding, a doc suggestion, a principle or advisory — takes the same three verbs, and each is recorded once (on a shared outcome log) so Hekkos learns what you accept. The verbs mean the same thing on every surface: the PR conversation, the standing drift PR, the Drift tab, the Violations tab, and the Suggestions panel on Home.

  • Accept — “yes, keep/do this.” For a drift finding: /hekkos ack (below), the Acknowledge button on the Drift tab, or a checkbox on the standing PR (see below). For a doc-fix suggestion: merge it — and for the deterministic slice Hekkos has already written the fix into the diff for you (see A real fix, committed for you below), so Accept is a one-click merge.
  • Dismiss — “no, this is wrong,” with a reason: /hekkos dismiss <reason> on a suggestion comment, or Dismiss on the Suggestions panel. (A bare /hekkos dismiss now replies with usage instead of doing nothing — a reason is required so the dismissal is auditable.)
  • Ignore — “not here”: /hekkos ignore <folder-glob> (admin), the Drift tab’s folder ignore, the per-finding “not applicable” on the Violations surface, or Ignore on the Suggestions panel.

Snooze — “not now.” A drift finding can also be snoozed for a set number of days (default 7): it drops off the active surfaces exactly like an Accept, but Hekkos automatically re-surfaces it when the timer expires if the drift is still real. It’s the honest middle ground between accepting a finding you haven’t decided on and ignoring one you don’t want to forget.

On the dashboard. The Suggestions panel on Home carries the same Accept / Dismiss / Ignore buttons for queued doc suggestions, so you can triage from the dashboard without opening each PR. Every one of these is written to the same outcome log as the PR-side verbs.

One-click checkbox accept. The standing drift PR renders a checkbox next to each note-residue finding — tick it to accept that finding, no command to type. Only org members can accept this way (Hekkos authorizes the person who ticked it).

Driving it from the PR conversation

  • /hekkos regenerate — rebuild the PR from the current real state (this does override your edits, since you asked). Org members only.
  • /hekkos ack <handle> — accept a specific drift finding so it stops surfacing until the underlying code changes again. Each finding line prints a short 12-character handle (not a long UUID) that you copy straight into the command. /hekkos accept <handle> is an accepted synonym. Org members only.
  • /hekkos ack <doc-path>bulk-accept every verified finding in one doc at once (e.g. /hekkos ack docs/config.md or /hekkos ack README.md), instead of acking them one line at a time. Only the verified findings are acked; any unverified guesses in that doc are left untouched (they never blocked or counted anyway), and Hekkos tells you how many of each it handled. Org members only.
  • /hekkos ignore <folder-glob> — tier a whole doc folder historical so it stops raising drift, straight from the PR (e.g. /hekkos ignore docs/rfcs/**). The ignore is scoped to this repo only — the one the PR lives in — so other repos in your org are unaffected, even if they have a folder at the same path. Org admins only — a non-admin who tries it is refused with a reply. On success Hekkos appends the rule, quiets that repo’s matching findings, and replies with exactly what it ignored, the fact that those docs also drop out of Hekkos chat answers for this repo (see the RAG trade-off below), and how to undo it — on this repo’s Drift tab → Ignored folders (open the repo on the Sources page). This is the same repo-scoped ignore as the Drift tab’s concentration banner, driven from GitHub.

Old full-UUID /hekkos ack commands from previously-opened PRs keep working, so nothing you’ve bookmarked breaks. (/hekkos dismiss is not a drift command — it belongs on suggestion comments; used here, Hekkos replies pointing you to ack/accept instead.)

Acting on a finding refreshes the PR

/hekkos ack and /hekkos ignore now rebuild the standing PR in the same job. Accept or ignore a finding and the PR’s body, title, and count update to match immediately — no more acking a finding and watching it linger in a PR that never changed. (An ignore that clears hundreds of findings updates the PR in one pass.) The human-safe guard still holds: if you own the branch, Hekkos refreshes nothing and comments instead.

When everything clears

Once the last finding behind the PR is resolved, acknowledged, or ignored, Hekkos closes the loop instead of leaving a stale PR open forever:

  • If your docs still carry drift notes from a previously merged rebuild, Hekkos flips the PR to a note-removal PR (retitled docs: remove N stale drift notes — all clear) and leaves it open — merging it strips those now-orphaned notes out of your docs.
  • If there’s nothing left to clean up, Hekkos closes the PR with an all-clear comment. If drift regresses later, a fresh PR opens.
  • Notes that were merged into your default branch and later acknowledged or ignored are treated as orphaned and removed on the next rebuild, so a merged note never outlives the finding it described.

Hekkos won’t heckle its own PRs

The drift check is skipped on Hekkos’ own branches (hekkos/drift and hekkos/doc-improvements). Refreshing the standing PR — even a force-push — will never make Hekkos post a drift comment on its own pull request. (The guard is an exact branch-name match on Hekkos-pushed branches in your own repo, so naming a branch hekkos/… yourself, or opening one from a fork, doesn’t dodge analysis.)

The Drift tab

The Drift tab on the Sources page (open a repo from the list and switch to the Drift tab, visible to every member) is where a repo’s drift lives — it renders whether or not you’ve adopted a doc standard.

Open Documentation Drift is deliberately distinct from Real-State Trust: it’s a count of doc/code mismatches, not a Trust score, so a big raw number here does not by itself drag your Trust down.

Pick a repo from the list and its Drift tab shows its findings worst-first, each with its effective severity and its source (built-in default, an org severity rule, or a per-finding override), the code file:line, and the doc location — with acknowledge and override-severity controls on every finding. Acknowledging a finding stops it surfacing until the underlying code changes again. The list shows honest empty, error, loading, and not-yet-scanned states — an error is never dressed up as “no drift”.

The repo’s detail header also carries a live Real-State Trust badge — a small SVG scored the same way as the org-wide Trust number and banded green / amber / red. By default it’s authenticated and in-app: it renders for a signed-in member viewing the repo, and the same URL returns nothing to anyone without a session.

Sharing the badge in a public README. If you want the badge on a public README, an admin can flip Public badge to on in the repo’s detail header. This is off by default and opt-in per repo — Hekkos never exposes a repo publicly on your behalf. Once on, the header shows a copy-ready markdown snippet (![docs trust](…/badge/{org}/{repo}/trust.svg)) that loads from an unauthenticated URL, so it works in an open repo. That public badge exposes only the numeric Trust percent and its band color — never file paths, doc or finding text, entity content, or even the fact that the repo exists: a repo that isn’t published (or any unknown id) returns an identical neutral docs trust: private badge, so the public URL can’t be used to probe your private repos. Flip it back to off at any time and the public URL immediately reverts to that neutral badge.

Priority to document

Above a repo’s findings, a Priority to document panel ranks its undocumented code by call-graph centrality — how many other entities and routes depend on each one — so you document the highest-impact gaps first instead of triaging a flat list. Each row names the entity and its file:line, how many callers point at it, and a route badge when an HTTP route targets it directly. A clean or not-yet-scanned repo shows a short teaching empty state rather than looking broken.

The same call graph is exposed to agents as two MCP tools — risk_ranking (this ranking) and impact_of (the blast radius of a single entity) — so you can ask the same questions from your IDE (see Ask Hekkos & MCP).

The concentration banner — ignore a whole folder at once

When a single doc folder dominates a repo’s findings (the classic case: hundreds of stale references all coming from docs/rfcs/** or an internal planning directory), the Drift tab shows a concentration banner naming that folder. An admin gets a one-click Ignore this folder button; a member sees the nudge but is told to ask an admin.

Ignoring the folder tiers it historical as a repo-scoped ignore — scoped to the selected repo, not org-wide — so it stops raising drift on this repo while leaving every other repo untouched. It is the same repo-scoped action as /hekkos ignore typed on that repo’s PR. Before anything is saved you get a dry-run preview of the exact effect on that repo (“this repo would show NM drift findings”) and a plainly-stated trade-off you must confirm:

Ignored folders (per repo)

Once a repo has at least one repo-scoped ignore — created by the concentration banner above or by /hekkos ignore on one of its PRs — the Drift tab lists them under Ignored folders for the selected repo, each with an Un-ignore control (admins only). This is the single place to review and undo a repo’s ignores; they deliberately do not appear in Settings → Documentation Authority, which is org-wide rules only. The list shows only the selected repo’s ignores, so switching repos in the picker changes what you see.

Un-ignoring a folder removes the rule, but its findings don’t reappear instantly — they return on the next scan of that repo. The row disappears right away, so don’t read the still-quiet findings as “un-ignore didn’t work”; they’ll come back once Hekkos next scans the repo.

Tuning severity defaults

Under Settings → Drift Severity Rules, an admin can override the built-in default severity for a drift kind — the key is a drift kind (doc_only) or a kind:entity_kind pair (doc_only:env_var). For example, set doc_only:env_var to info if that class is noisy for your org. This sits alongside the per-finding override on the Drift tab (which retunes a single finding) and the source-tier authority rules (which quiet whole doc paths).