GOVERNANCE
ZARA’s Hecate
Purpose
Hecate is the governance and integrity subsystem of Ask ZARA.
Its role is not to answer questions, modify documentation, or approve changes —
its role is to detect, surface, and explain governance drift across the ZAYAZ knowledge system.
Hecate exists to protect institutional truth, not to enforce authority.
Core Principle
Hecate does not decide what is correct.
Hecate detects when the system is drifting away from its own rules.
Hecate is:
- A sentinel
- An early-warning system
- A guardian of boundaries
Hecate is not:
- An editor
- An approver
- A reviewer
- A process owner
Position in the System
Hecate operates alongside Ask ZARA — not inside it.
| Component | Responsibility |
|---|---|
| Docusaurus Docs | Stable system of record |
| Jira | Execution & traceability |
| Confluence | Evolving engineering narrative |
| HubSpot (RFCs) | Change governance |
| Ask ZARA | Knowledge retrieval & explanation |
| Hecate | Governance integrity & drift detection |
Ask ZARA answers questions.
Hecate flags when answers may be compromised.
What Hecate Monitors
Hecate evaluates relationships, not raw content.
Primary Inputs
- Documentation metadata (Spec IDs, sections, last_updated)
<JiraNeed />tags- Jira issue metadata (status, links, Spec ID)
- Jira epic structure
- RFC metadata from HubSpot (status, page_url)
- (Later) Confluence metadata and Jira ↔ Confluence links
Hecate does not interpret intent or rewrite content.
What Is a Violation?
A violation is not an error.
A violation is a signal that a governance rule may have been breached.
Hecate treats violations as traffic signals, not enforcement actions.
Violation Classes
1. Source-of-Truth Violations (Highest Severity)
Rule
Docusaurus Docs are the system of record.
Examples
- Jira or Confluence contradicts documented contracts
- Acceptance criteria redefine documented behavior
- External narrative diverges from published specs
Hecate Behavior
- Raises explicit warning
- Cites the Playbook
- Defers to Docs as authoritative
2. Traceability Violations
Rule
Every execution item must trace back to Docs.
Examples
<JiraNeed />without a Jira issue- Jira issue without Spec ID
- Epic exists but has no children
- Doc references a missing epic
3. Freshness & Drift Signals
Rule
Stabilized work must be promoted to Docs.
Examples
- Jira marked Done but Docs unchanged
- RFC marked Published but no doc update
- High Jira churn with stale documentation
These are warnings, not accusations.
4. Governance Process Signals
Rule
If it’s not approved via RFC, it does not exist in the Manual.
Examples
- Doc changes without an RFC
- RFC approved but not deployed
- RFC rejected but execution continues
Severity Model
Hecate uses a graduated severity model to avoid alarm fatigue.
| Level | Meaning | Nature |
|---|---|---|
| P0 | Governance broken | Hard violation |
| P1 | High risk of wasted work | Immediate warning |
| P2 | Growing entropy | Soft warning |
| P3 | Hygiene / quality | Advisory signal |
| P4 | Observational insight | Informational |
Only P0–P2 are considered violations.
P3–P4 are system health signals.
How Hecate Surfaces Issues
Hecate never blocks workflows.
Primary Channels
- Inline trust notices in Ask ZARA answers
- System dashboards (e.g.
/system/violations) - Cross-spec governance views
Optional Channels (Severity-based)
- Slack notifications
- Email summaries
- Jira “Validation” issues (optional, explicit)
Example Hecate Signal
⚠️ Possible source-of-truth violation
The ZAYAZ Manual states that API contracts are immutable once published,
but Jira issue ZYZ-281 describes a breaking change.
According to the Documentation & Delivery Playbook,
Docs take precedence.
Hecate does not fix — it flags.
What Hecate Must Never Do
🚫 Modify Docs
🚫 Approve changes
🚫 Override human decisions
🚫 Invent governance rules
🚫 Treat Jira or Confluence as authoritative over Docs
Hecate protects the system — it does not control it.
Relationship to Ask ZARA
Ask ZARA may:
- Reference Hecate signals
- Adjust confidence indicators
- Surface governance context
Ask ZARA must never:
- Suppress answers because of violations
- Rewrite content to “fix” violations
Design Philosophy
Most AI systems answer questions.
Hecate ensures the questions remain answerable.
This transforms Ask ZARA from:
- A search assistant
Into:
- A governance-aware system
- A drift detector
- An institutional memory guardian
North Star
Truth degrades silently.
Hecate exists to make drift visible.
Hecate Audit Rules v1
| # | Rule (type) | What it detects | Severity | Deterministic check | Inputs | Suggested routing |
|---|---|---|---|---|---|---|
| 1 | missing-jira-key | JiraTaskUnlinked(unlinked) | P0 | Parse MDX → find <JiraNeed …/>→ if jiraKey missing/blank | Docs MDX | Dashboard + Slack/Email |
| 2 | jira-issue-not-found | jiraKey exists in MDX but not found in progress.json(or Jira API) | P0 | Validate key pattern + lookup in progress.json issues (optional: Jira /issue/{key}) | Docs MDX + progress.json(+ Jira API) | Dashboard + Slack/Email |
| 3 | jira-missing-doc-backlink | Jira issue exists but has no remote link back to Specs | P1 | Jira issue remotelinks contain no URL starting with DOCS_BASE_URL | Jira API (remotelink) | Dashboard + Slack/Email (to governance owner) |
| 4 | epic-declared-in-docs-but-not-found | Docs frontmatter declares jira.epic, but progress index has no matching epic entry | P0 | Docs specId has epicKey, but progress.epics[specId] missing or epicKey mismatch | Docs frontmatter + progress.json | Dashboard + Slack/Email |
| 5 | epic-has-no-children | Doc has jira.epic but epic has 0 children | P1 | progress.epics[specId].counts.total === 0 | Docs frontmatter + progress.json | Dashboard (Slack if >24h / “age” threshold) |
| 6 | jira-not-linked-to-doc-epic | A doc’s jiraKey exists but is under a different epic/spec in the progress index | P1 | jiraIssueByKey[jiraKey].specId !== doc.specId OR epicKey mismatch | Docs MDX + progress.json | Dashboard + Slack/Email (PM + governance owner) |
| 7 | done-but-doc-stale | Epic has recent activity / done work but docs appear stale | P2 | if epic.updatedMax > doc.last_updated + staleDays | progress.json+ Docs frontmatter | Dashboard (digest optional) |
| 8 | rfc-published-but-docs-unchanged | HubSpot RFC is Published for a page_url but doc unchanged since RFC updated | P2(raise later if needed) | Compare hubspot.updatedAt(stage=Published)vs doc.last_updated | HubSpot metadata export + Docs frontmatter | Dashboard + Slack/Email (owner) |
Notes on severity (why P0/P1/P2)
- P0 = traceability is broken at the leaf level (the thing you think is being executed can’t even be reliably located).
- P1 = execution is disconnected from authority (Jira exists but isn’t anchored to Docs properly, or specs/IDs don’t line up).
- P2 = system drift / staleness (not necessarily wrong, but risky and should be surfaced).
Violation object shape (with Canonical violation types v1)
This format is designed to:
- work for dashboards + sorting + filtering
- be “attachable” to Ask ZARA answers
- survive growth (Confluence, richer refs) without breaking
/workspaces/zayaz-docs/docusaurus/src/shared/hecate-types.ts:
// src/shared/hecate-types.ts
export type Severity = "P0" | "P1" | "P2" | "P3" | "P4";
export type ViolationType =
| "missing-jira-key"
| "jira-issue-not-found"
| "jira-missing-doc-backlink"
| "jira-not-linked-to-doc-epic"
| "epic-declared-in-docs-but-not-found"
| "epic-has-no-children"
| "spec-has-epic-but-epic-empty"
| "done-but-doc-stale"
| "orphan-jira-specid"
| "rfc-published-but-docs-unchanged";
export type Violation = {
id: string; // stable hash
severity: Severity;
type: ViolationType;
title: string;
message: string;
ruleRef?: string;
scope: "need" | "section" | "page" | "spec" | "system";
specId?: string;
doc?: {
id?: string;
path?: string;
sourceFile?: string;
lastUpdated?: string;
};
section?: {
anchor?: string;
heading?: string;
needId?: string;
};
jira?: {
key?: string;
epicKey?: string;
url?: string;
updated?: string;
};
rfc?: {
pageUrl?: string;
ticketId?: string;
stage?: string;
updated?: string;
};
evidence?: Array<{
source: "docs" | "jira" | "hubspot" | "confluence";
note: string;
}>;
suggestedFix?: {
kind: "run-script" | "edit-doc" | "update-jira" | "review-rfc";
label: string;
command?: string;
link?: string;
};
detectedAt: string;
};
Severity mapping
| ViolationType | Severity |
|---|---|
| missing-jira-key | P0 |
| jira-issue-not-found | P0 |
| epic-declared-in-docs-but-not-found | P0 |
| jira-not-linked-to-doc-epic | P1 |
| epic-has-no-children | P1 |
| jira-missing-doc-backlink | P2 |
| done-but-doc-stale | P2 |
| rfc-published-but-docs-unchanged | P3 |
Rule definitions with title/message templates + suggestedFix
1. missing-jira-key (P0)
When: <JiraNeed /> exists but no jiraKey.
- scope: need (or section if you don’t want per-need granularity)
- title: Unlinked JiraNeed
- message template:
- A tag exists but has no jiraKey. Execution cannot be traced to Jira. JiraTaskUnlinked(unlinked)
- A tag exists but has no jiraKey. Execution cannot be traced to Jira.
- evidence:
- source=docs: Found without jiraKey in section
{anchor}JiraTaskUnlinked(unlinked)
- source=docs: Found without jiraKey in section
- suggestedFix:
- run-script: npm run sync:jira-needs
- edit-doc: “Add jiraKey to tag”
{
"type": "missing-jira-key",
"severity": "P0",
"title": "Unlinked JiraNeed",
"message": "A <JiraNeed/> tag exists but has no jiraKey. Execution cannot be traced to Jira."
}
2. jira-issue-not-found (P0)
When: MDX contains jiraKey, but it’s not present in your Jira index (progress.json or dedicated Jira ETL).
- scope: need
- title: Jira issue not found
- message:
- The docs reference Jira key
{jiraKey}, but it was not found in the Jira index.
- The docs reference Jira key
- evidence:
- docs:
jiraKey={jiraKey}present in MDX - jira: key not found in progress.json
- docs:
- suggestedFix:
- run-script: npm run jira:progress-index (and/or Jira ETL)
- update-jira: verify key exists / permissions
3. jira-missing-doc-backlink (P1)
When: Jira issue exists but has no remote link back to Docs (DOCS_BASE_URL).
- scope: need
- title: Jira issue missing Docs link
- message:
- Jira issue
{jiraKey}has no web link back to the ZAYAZ Manual. Traceability is incomplete.
- Jira issue
- evidence:
- jira: No remotelink matching
${DOCS_BASE_URL}
- jira: No remotelink matching
- suggestedFix:
- run-script: npm run jira:backfill-links (or sync:jira-needs for new)
- update-jira: add remote link manually
4. jira-missing-spec-id (P1)
When: Jira issue exists but Spec ID custom field is empty.
- scope: need
- title: Jira issue missing Spec ID
- message:
- Jira issue
{jiraKey}is missing Spec ID. It cannot be reliably tied to a spec.
- Jira issue
- suggestedFix:
- update-jira: set Spec ID field
- run-script: (optional) re-sync/patch script if you automate it later
5. spec-has-epic-but-epic-empty (P1)
When: doc has jira.epic, but epic has 0 children.
- scope: spec
- title: Epic has no child issues
- message:
- Spec
{specId}links to epic{epicKey}, but no child issues were found.
- Spec
- evidence:
- progress: counts.total = 0
- suggestedFix:
- update-jira: add/link child issues to epic
- run-script: npm run jira:progress-index (after fixing)
6. done-but-doc-stale (P2)
When: Jira activity is newer than doc last_updated by threshold (e.g. 7 days), OR epic has done work but doc hasn’t moved.
- scope: spec (or page)
- title: Documentation may be stale
- message:
- Jira activity for
{specId}is newer than the doc update timestamp. Consider promoting stabilized outcomes to Docs.
- Jira activity for
- evidence:
- docs:
last_updated={...} - jira:
updatedMax={...}
- docs:
- suggestedFix:
- edit-doc: update page + bump last_updated
- (optional) create Jira “Validation” task later
7. orphan-jira-specid (P1)
When: Jira issue has Spec ID that does not exist in docs index.
- scope: need (or system if you group)
- title: Orphaned Jira Spec ID
- message:
- Jira issue
{jiraKey}references Spec ID{specId}, but no matching doc id was found.
- Jira issue
- suggestedFix:
- edit-doc: add/create doc with that id, or correct Spec ID in Jira
- update-jira: fix Spec ID
8. rfc-published-but-docs-unchanged (P2)
When: HubSpot RFC ticket is Published (or equivalent stage), but doc last_updated is older than RFC updated time.
- scope: page
- title: RFC published but docs not updated
- message:
- An RFC for this page is marked Published in HubSpot, but the manual has not been updated since.
- suggestedFix:
- review-rfc: open HubSpot ticket
- edit-doc: implement approved change
Architecture
- hecate-types.ts = law
- knowledge-audit.ts = enforcement
- ZARA = messenger
/workspaces/zayaz-docs
│
├─ docusaurus/
│ └─ src/
│ └─ shared/
│ ├─ jira-types.ts
│ ├─ hecate-types.ts
│ └─ trust-types.ts
│
├─ scripts/
│ └─ audit/
│ └─ knowledge-audit.ts ← imports hecate-types
│
└─ docusaurus/static/
└─ system/
└─ violations.json ← generated
How imports should look
From Docusaurus / React / Ask ZARA
import type { Violation, Severity } from "@site/src/shared/hecate-types";
(Docusaurus aliases @site → docusaurus root)
From audit scripts (Node)
import type { Violation } from "../../docusaurus/src/shared/hecate-types";
(relative path from scripts/audit/knowledge-audit.ts)
Explicit rule
If a type is used by both build scripts and the site, it lives in docusaurus/src/shared/.