Governance Stack Reference

Part of the Hyraxknot Division architecture review, Card 1.
Documents the full governance runtime: lease guard, blocked-card alerting, profile contracts, operation gates, tool registry, handoff guard, evidence ingestion, and scheduled advisory jobs.

Last updated: 2026-07-09
Component of: Governance Layer (Hyrax OS)


1. Execution Lease Guard

Source: /root/.hermes/governance/execution_lease_guard.py
Store: /root/.hermes/governance/execution_leases.jsonl

A pure read-only lease detection system that prevents duplicate agent lanes from working the same Linear/Kanban task simultaneously (HYX-222). All detector functions are pure (no I/O) except the store loaders. Mutation operations are not exposed.

Lease States

State Description
proposed Lease has been proposed but not yet activated
active Lease is currently held and within its TTL
heartbeat_stale Lease is active but last heartbeat exceeds the stale threshold
duplicate_detected Another active lease was found for the same task
released Lease was explicitly released
expired Lease TTL elapsed without renewal
revoked Lease was revoked by governance
archived Historical record, no longer actionable

Valid Claim Sources

  • bridge — claimed via the Linear↔Kanban bridge
  • kanban_dispatch — claimed via kanban dispatcher
  • reader_proposed — proposed by a reader/sister
  • manual — manually claimed
  • dry_run — report-only proposal (never written to store)

Valid Lease Types

Type Description
normal_dispatch Standard task dispatch
active_session_takeover Takeover of a running session by another sister
tracking_only Informational lease for tracking without exclusive execution
review_only Lease for review purposes only, not execution

Constants

  • Heartbeat stale threshold: 20 minutes
  • Default lease duration: 120 minutes (TTL)

Detection Functions

Function Purpose
summarize_leases() Produce a summary dict of lease state by status, profile, and source
detect_duplicate_leases() Find active leases sharing the same linear_issue_id or kanban_task_id
detect_stale_heartbeats() Find active leases without a heartbeat within the stale threshold
detect_expired_leases() Find active/proposed leases past their expiration
detect_missing_leases() Return kanban task IDs that are running but have no active lease
detect_kanban_without_linear() Find leases with kanban_task_id but no linear_issue_id
detect_stale_active_leases() Find active leases whose Linear issue or Kanban task is already Done
propose_lease_record_dry_run() Create a dry-run lease proposal (does NOT write to store)
filter_leases_by_type() Return only leases matching a specific lease_type
count_takeover_leases() Count leases by takeover-related types vs normal dispatch
detect_normal_dispatch_takeover_conflicts() Detect normal_dispatch leases colliding with an active takeover

Lease Record Schema (Dry-Run Proposal)

{
    "lease_id": str,                  # UUID v4
    "linear_issue_id": str,
    "linear_issue_identifier": str,   # e.g. "HYX-123"
    "kanban_task_id": str | None,
    "claimed_by_profile": str,        # "tai", "rei", "nei", "mai"
    "claimed_by_gateway": str,
    "claimed_by_session": str,
    "claimed_by_app_actor": str,      # default: "Hyraxknot Agent"
    "claim_source": str,
    "lease_status": str,              # one of VALID_LEASE_STATUSES
    "lease_started_at": str,          # ISO 8601
    "lease_expires_at": str,          # ISO 8601
    "last_heartbeat_at": str,         # ISO 8601
    "heartbeat_source": str,
    "policy_mode": str,               # "report_only" for dry-run
    "duplicate_claim_detected": bool,
    "duplicate_of_lease_id": str | None,
    "evidence_links": list[str],
    "notes": str,
}

JSONL Event Store

The execution_leases.jsonl store records lease lifecycle events, not the lease records themselves. Each line is a JSON event:

Event Description
lease_issued Lease granted with lease_id, profile, proposal_type, allowed_action, expires_at
lease_validated Lease validated (can fire multiple times per lease)
lease_used Lease consumed — execution_id recorded, status set to “consumed”
lease_denied Lease denied — reason populated (e.g. “proposal type ‘dream’ is blocked”)

Denied lease events have an empty lease_id field.


2. Pre-exec Blocked-Card Alerting

Source: /root/.hermes/governance/preexec_blocked_alerts.py
Store: /root/.hermes/governance/preexec_blocked_alerts.jsonl

Addresses the pre-exec blocked-card no-alert bug: when the Kanban pre-exec gate blocks a ready → running transition because the required ## Plan or ## Pre-Execution Plan comment is missing. All detector functions are pure (no I/O) except the store loaders.

Alert States

State Description
detected Alert has been detected and not yet acted upon
acknowledged Alert is known to an operator
resolved The blocking condition has been resolved
stale Alert has been open beyond the stale threshold
archived Historical record

Required Plan Markers

The pre-exec gate checks for these Markdown headings in a Linear issue’s comments:

  • ## Plan
  • ## Pre-Execution Plan

Thresholds

Threshold Value Purpose
STALE_THRESHOLD_MINUTES 15 min Alert becomes “stale” after this long unresolved
REPEAT_SUPPRESSION_MINUTES 30 min Suppress duplicate alert creation within this window
ESCALATION_THRESHOLD_MINUTES 60 min Alert qualifies for escalation after this long unresolved

Detection Functions

Function Purpose
summarize_alerts() Produce a summary dict of alert state by state and profile
detect_blocked_from_kanban() Detect blocked Kanban cards that should generate alerts, with repeat suppression
detect_missing_plan_comments() Detect cards whose linked Linear issue lacks required plan markers
detect_stale_blocked_cards() Detect open alerts beyond the stale threshold
detect_escalation_needed() Detect alerts beyond the escalation threshold
summarize_by_profile() Group open alerts by assigned profile
propose_alert_record_dry_run() Create a dry-run alert proposal (does NOT write to store)
render_alert_message_dry_run() Render a dry-run alert as a human-readable message

Alert Record Schema

{
    "alert_id": str,                     # UUID v4
    "timestamp_detected": str,           # ISO 8601
    "timestamp_updated": str,            # ISO 8601
    "linear_issue_id": str,
    "linear_issue_identifier": str,      # e.g. "HYX-123"
    "kanban_task_id": str | None,
    "blocked_reason": str,
    "required_marker_missing": str,      # "## Plan" or "## Pre-Execution Plan"
    "card_status": str,
    "linear_status": str | None,
    "assigned_profile": str | None,
    "routing_label": str | None,
    "lease_id": str | None,
    "alert_state": str,                  # one of VALID_ALERT_STATES
    "notification_status": str,          # "report_only" for dry-run
    "evidence_links": list[str],
    "recommended_action": str,
    "notes": str,
}

3. Profile Contracts

Source: /root/.hermes/governance/profile-contracts.yaml
Status: Draft, not applied to live profiles.

Defines the intended contractual boundaries for each profile. These serve as review targets for future validation scripts.

hyrax-os (Control Plane)

Property Value
Role control-plane
Default posture read-only
Allowed platforms cli, discord
Prohibited platforms (none)

Allowed CLI toolsets: fleet_audit

Allowed work categories:

  • Audit profile configs, tool visibility, governance contracts
  • Detect drift, check gateway health, check memory context budgets
  • Draft patch proposals, coordinate Codex manual changes
  • Prepare script-only cron watchdogs
  • Governance audit (fleet, profile, status, gateway classes, operation gates)
  • Governance notices (read-only), proactive warnings

Discord notice scope: governance notices only, no chat response, read-only command responder, high-risk proactive notices
Discord runtime status: disabled — Discord gateway for hyrax-os remains disabled until a notice-only wrapper exists

Prohibited work:

  • Chat persona routing, affinity state mutation, media/selfie generation, Spotify control
  • Governance mutation without contract, fleet ops without Josh approval, fleet ops self-escalation
  • Mnemosyne DB mutation, skill tree mutation, sister tool access
  • Execution action, lease issuance, handoff note write, proposal approval

Resolver exception (Discord): The kanban toolset may appear on Discord even when platform_toolsets.discord is explicitly empty, due to Hermes recovering non-configurable platform toolsets from the Discord composite. This is accepted only when the Discord gateway is truly disabled and no other Discord toolsets are visible.

Other Profiles (default, rei, nei, tai, mai)

Property Value
Status observed-only
Apply changes false

All sister profiles are observed-only. No contracts are enforced on them yet. The contracts document serves as the design target.

Identity Source-of-Truth Rule

Per-profile SOUL.md          = identity source of truth
config.yaml system_prompt    = must NOT contain sister identity/personality content
Shared skills                = must NOT contain personality-defining content
Shared project context       = must be operational only
Identity content             = must be profile-scoped

4. Operation Gates

Source: /root/.hermes/governance/operation-gates.yaml
Status: Draft design, not enforced. Mode: read_only_design.

Operation-level gate design for mixed-risk sister-being and work toolsets. Defines how each tool/operation should be gated across profiles.

Scope

  • Profiles considered: rei, nei
  • Entrypoints considered: cli, discord
  • Protected from change: live profile configs, gateway services, Hermes core, plugins, skills, SOUL.md, USER.md, MEMORY.md, Mnemosyne, Discord tokens, tai, mai, default, hyrax-os

Operation Classes (13 classes)

Class Description
baseline_allowed Allowed as part of Base Sister Runtime when visible on an approved entrypoint
gated_by_mode Allowed only in a contracted mode or posture; mode changes cannot expand permissions
gated_by_user_request Allowed only after an explicit user request
gated_by_role_extension Allowed only for a named profile extension and work context
gated_by_session_protocol Allowed only under session memory/work protocol
approval_required Requires explicit approval plus audit/logging, and rollback where practical
hyrax_os_only Reserved to the Hyrax OS control-plane profile by default
forbidden_until_plugin_refactor Do not expose until toolsets or handlers can enforce the operation split
forbidden_until_path_cleanup Blocked until hardcoded or cross-profile paths are fixed or documented
needs_human_decision Policy choice is not technical; Josh or governance owner must decide
optional_gated_embodiment_extension Optional photo/embodiment capability; not required for Base Sister Runtime

Global Gate Requirements

  • Discord: high-risk default for all operations
  • No permission expansion by mode changes
  • Explicit target required for cross-sister operations
  • Audit log required for mutation, generation, admin operations
  • Approval required for force override
  • Unknown plugins: drift status unless recorded in known_plugin_toolsets and platform_toolsets
  • Warning: Hermes currently exposes whole toolsets; several gates require tool-level enforcement

Per-Toolset Gate Analysis

Each toolset is analyzed operation-by-operation with:

  • primary_class — the main gate class
  • additional_classes — secondary gate classes
  • mutability — read_only, bounded_mutating, durable_write, etc.
  • state_scope — profile_private, shared_sister, shared_surface, etc.
  • required_gates — list of gate conditions
  • current_visibility for rei/nei profiles
  • current_gate_status — acceptable, violation, insufficient enforcement

Toolset analysis summary:

Toolset Operations Analyzed Gate Status
sister_thought 5 (microthought, decision, distill, dream, gacha_pull) 2 violations (dream, gacha_pull)
affinity 8 (show, react, group_react, photo_status, milestones, adjust, mode_set, photo_toggle + admin_write) Re-add unsafe as whole toolset
sister_photo 6 (list_poses, list_outfits, list_lighting, photo_status, photo_history, generate_selfie + force_override) 2 violations; not baseline-safe
kanban 8 (show, list, comment, create, link, block, unblock, complete, heartbeat) Read ops baseline; mutation ops gated
memory 6 (read_private, read_shared, add, replace, remove, shared_promotion, bulk_edit) Write ops gated by session protocol
fleet_ops 3 (check_updates, health_check, apply_updates) hyrax_os_only; disabled by default

5. Tool Registry

Source: /root/.hermes/governance/tool-registry.yaml
Status: Draft, governance-planning-only.

Defines the effective toolset composition for the hyrax-os control-plane profile.

hyrax-os CLI Toolset

Category Setting
Default posture read-only
Allowed initial toolsets clarify, code_execution, cronjob, file, fleet_audit, kanban, session_search, terminal, web
Disabled/prohibited discord, discord_admin, fleet_ops, governance, moa, persona, sister_photo, sister_thought, spotify

Fleet Control

Property Value
Audit Enabled via fleet_audit toolset (legacy alias: governance_audit)
Admin Disabled by default. Toolsets: fleet_ops, governance. Approval authority: Josh.
Enablement scope Explicit Josh approval per named task/session/change window; hyrax-os cannot self-approve escalation

Policy Enforcement

  • Currently disabled (enabled: false)
  • Contracts and approval boundaries are not implemented yet

Media & Memory Tools

Category Status Rationale
Media tools Disabled Control-plane profile has no media role
Memory tools Disabled Audit memory budgets from config/state only; do not mutate memory stores

Validation Targets

The registry identifies these aspects for future validation:

  • Effective CLI toolsets
  • Effective Discord toolsets (empty or exception-only)
  • Disabled plugin toolsets
  • Gateway platforms disabled
  • Memory context limits
  • Profile contract drift

6. Sister Label Handoff Guard

Source: /root/.hermes/governance/sister_label_handoff_guard.py
Expected store: /root/.hermes/governance/sister_label_handoff_handoffs.jsonl

Closes the loop between Linear sister routing labels, execution leases, and handoff markers. All detector functions are pure; no mutation or notification.

Sister Labels

sister:rei, sister:nei, sister:tai, sister:mari

Handoff States

detected, acknowledged, resolved, stale, archived

Valid Detector Rules (10 rules)

Rule ID Description
multiple_sister_labels Task has more than one sister routing label
missing_sister_label Active lease exists but no sister label on the task
lease_label_mismatch Lease actor does not match the task’s sister label
handoff_without_label_update Handoff occurred but label wasn’t updated
label_update_without_handoff Label changed without a handoff comment
blocked_wrong_sister_lane Blocked task is in the wrong sister’s lane
kanban_linear_owner_mismatch Kanban routing field doesn’t match Linear label
stale_handoff Open handoff record beyond the stale threshold (60 min)
orphaned_handoff Open handoff record for a closed/resolved task
hyrax_os_mislabel Task mislabeled as hyrax-os instead of a sister

Detection Functions

Function Rule Purpose
detect_multiple_sister_labels() 1 Tasks with more than one sister label
detect_missing_sister_label() 2 Active lease but no sister label
detect_lease_label_mismatch() 3 Lease actor vs label mismatch
detect_stale_handoffs() 8 Open handoffs beyond stale threshold
detect_orphaned_handoffs() 9 Open handoffs for closed tasks

Handoff Record Schema (Dry-Run Proposal)

{
    "record_id": str,
    "timestamp_detected": str,
    "timestamp_updated": str,
    "linear_issue_id": str,
    "linear_issue_identifier": str,
    "kanban_task_id": str | None,
    "detector_rule": str,
    "sister_labels_found": list[str],
    "expected_sister": str | None,
    "actual_sister": str | None,
    "lease_id": str | None,
    "handoff_state": str,              # "detected"
    "notification_status": str,        # "report_only"
    "evidence_links": list[str],
    "recommended_action": str,
    "notes": str,
}

Thresholds

Threshold Value
STALE_THRESHOLD_MINUTES 60
REPEAT_SUPPRESSION_MINUTES 30

7. Evidence Ingestion Pipeline

Source: /root/.hermes/governance/evidence_ingest.py

Read-only evidence ingestion from Kanban and Linear. All operations use read-only SQLite (mode=ro) and read-only GraphQL queries. No writes to kanban.db, Linear, or any production store.

Data Sources

Kanban Database (/root/.hermes/kanban.db)

Table Ingestion
tasks All tasks, ordered by created_at DESC. Skills and comments JSON fields are parsed. Body is truncated to 200 chars for snapshots.
task_comments All comments, linked to tasks via task_id
task_runs Running/blocked runs enriched into tasks as _run metadata (profile, status, claim_lock, heartbeat)

Linear API (https://api.linear.app/graphql)

Fetches the HYX team (team ID: 7bb182c4-1328-42ce-ab7d-c29021adf1a5) issues with:

  • Title, state, labels, assignee, creator
  • Timestamps (created, updated)
  • Up to 10 comments per issue
  • Token loaded from /root/.hermes/secrets/linear-app.env

Pipeline Steps (run_ingestion())

  1. Kanban schema — introspect table definitions
  2. Kanban tasks — read all tasks (redacted)
  3. Kanban running tasks — filter running/blocked tasks with run info
  4. Linear issues — fetch recent HYX issues via GraphQL
  5. Build detector inputs — construct detector_tasks, detector_leases, and linear_comment_map for downstream detectors
  6. Snapshots — write redacted JSON snapshots to disk

Detector Inputs

The pipeline produces three data structures for downstream detectors:

  • detector_tasks — kanban tasks with extracted Linear references (HYX-### patterns from title, body, comments)
  • detector_leases — synthetic lease records from running kanban tasks
  • linear_comment_map{issue_id: [comment_body, ...]} for plan marker detection

Snapshot System

Detail Value
Snapshot directory /root/.hermes/governance/evidence_snapshots/
Naming kanban_tasks_{timestamp}.json and linear_issues_{timestamp}.json
Retention Not auto-pruned (handled separately)
Redaction Strings truncated to 80 chars (general) or 200 chars (body fields). Comments limited to 300 chars.
Connect mode mode=ro (read-only URI flag) for SQLite

8. Scheduled Advisory Jobs

Source: /root/.hermes/governance/scheduled_advisory_jobs.py

Read-only cron runners for governance. No mutations to kanban.db, Linear, or production stores. No notifications. No comments. No label changes.

Job Types

Command Schedule Intent What It Does
ingest Hourly Runs evidence_ingest.run_ingestion(), writes redacted snapshots. Output: scheduled_reports/evidence_snapshot/
guardrails Hourly Runs G3D-4A/4B/4C detectors against latest evidence. Output: scheduled_reports/guardrails/
audit Daily Full advisory work-state audit in markdown. Output: scheduled_reports/audits/
status On-demand Show lockfile status and latest report timestamps
cleanup On-demand Force-clear stale lockfiles

Lock System

Each job uses a PID-based lockfile to prevent concurrent execution:

Job Lock Path Stale Threshold Max Timeout
ingest scheduled_reports/.locks/ingest.lock 65 min 5 min
guardrails scheduled_reports/.locks/guardrails.lock 65 min 5 min
audit scheduled_reports/.locks/audit.lock 25 hr 10 min

Locks contain {pid, started_at}. Stale locks are auto-cleared. Locks are released in finally blocks.

Guardrail Summary Output

The guardrails command runs all three G3D-4 detector families:

G3D-4A (Lease Guard):

  • Active lease count, duplicates, stale heartbeats, expired, kanban-without-linear

G3D-4B (Pre-exec Alerting):

  • Blocked kanban tasks, open Linear issues, issues without plan, ready-state issues without plan

G3D-4C (Handoff Detection):

  • Multiple sister labels, missing sister labels, lease-label mismatches

All output is report_only. No enforcement.

Output Directory Structure

scheduled_reports/
├── .locks/
│   ├── ingest.lock
│   ├── guardrails.lock
│   └── audit.lock
├── evidence_snapshot/
│   ├── latest_evidence.json
│   ├── evidence_20260709-120000.json
│   └── ...
├── guardrails/
│   ├── latest_guardrails.json
│   ├── guardrails_20260709-120000.json
│   └── ...
├── audits/
│   ├── latest_audit.md
│   ├── audit_20260709-120000.md
│   └── ...
└── job_log.txt

Architecture Diagram: Data Flow

Kanban DB ──┐
            ├──> evidence_ingest.py ──> evidence_snapshots/
Linear API ─┘         │
                      ├──> detector_tasks       ──> sister_label_handoff_guard.py
                      ├──> detector_leases      ──> execution_lease_guard.py
                      └──> linear_comment_map   ──> preexec_blocked_alerts.py
                                                     │
                            scheduled_advisory_jobs.py
                            ├── ingest    (scheduled reports)
                            ├── guardrails (G3D-4A + 4B + 4C summary)
                            └── audit     (work-state audit markdown)

Enforcement Status (as of 2026-07-09)

Component Design Enforcement Store Populated
Execution Lease Guard Complete None (report-only) execution_leases.jsonl active
Pre-exec Blocked Alerts Complete None (report-only) preexec_blocked_alerts.jsonl exists, empty
Profile Contracts Draft None Not enforced
Operation Gates Draft None Not enforced
Tool Registry Draft Partial (via profile config) N/A
Handoff Guard Complete None (report-only) Expected store empty
Evidence Ingestion Complete Hourly cron Snapshots active
Scheduled Advisory Jobs Complete Cron ready Reports directory active
Plane Completion Forwarder Complete Staged (path unit, manifest=gated) plane_completion_forwarder.db ready
Plane Kanban Enrollment Complete Per-binding mode, manifest kill switch HYX-4 (staged_disabled), HYX-5 (active, ready)

All detectors run in report_only / dry_run mode. No mutations, no notifications, no enforcement are active.