Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.oktolabs.ai/llms.txt

Use this file to discover all available pages before exploring further.

Archive & retention

Pulse never hard-deletes graph data. Two layers cooperate:
  • Entity archivearchive_tree / restore_tree mark a Pulse entity (ideation, refinement, spec, sprint, card) and its children as archived. Their KG nodes stay queryable.
  • KG retentionDecision, Constraint, and other graph nodes are retired through supersedes edges, not by deletion. A daily decay tick reduces stale-node relevance scores. Dead-letter entries linger until an operator inspects them.
The result is a graph that is durable across team changes, model upgrades, and entity-level cleanups. For the schema and tool index, see overview. For the consolidation flow that writes nodes, see consolidation.

Entity archive — the 2 tools

Source: okto-pulse-core/src/okto_pulse/core/mcp/server.py. Citations: 80-pulse-feature-inventory.md:392–393, :566–567, :814–848.

okto_pulse_archive_tree

Archive an entity and all of its children (cascading soft-delete). The entity is hidden from default views but every row remains in the database. server.py:6733. Permission: the entity-scoped <entity>.entity.archive flag in the granular registry — e.g. spec.entity.archive for archiving a spec, card.entity.archive for archiving a card.
Input:
  board_id:    "brd_abc123"
  entity_type: "spec"        # ideation | refinement | spec | sprint | card
  entity_id:   "spec_007"
  reason:      "Replaced by spec_011 — webhook delivery v3"   (optional)
Output:
{
  "board_id":     "brd_abc123",
  "entity_type":  "spec",
  "entity_id":    "spec_007",
  "archived_at":  "2026-05-07T14:00:00Z",
  "archived_by":  "agent_cognitive_01",
  "cascade": {
    "ideations":    [],
    "refinements":  [],
    "specs":        ["spec_007"],
    "sprints":      ["sprint_007a"],
    "cards":        ["card_91", "card_92", "card_93"]
  },
  "kg_impact": {
    "nodes_kept":         42,
    "nodes_marked_stale": 0
  }
}
The cascade follows the standard parent→children rules:
RootChildren archived along with it
IdeationLinked refinements (only if not also linked elsewhere)
RefinementSpecs derived from this refinement (only if not multi-parent)
SpecSprints scoped to this spec, plus their cards
SprintCards in this sprint
Card(leaf — no cascade)
kg_impact.nodes_kept reports how many graph nodes reference the archived tree by belongs_to. Those nodes are not removed. Their decisions, constraints, and learnings remain queryable by every read tool — including across boards via kg_query_global.

okto_pulse_restore_tree

Reverse of archive_tree. Restores the entity and every cascade entry that was archived in the same operation. server.py:6777. Permission: the entity-scoped <entity>.entity.restore flag — e.g. spec.entity.restore.
Input:
  board_id:    "brd_abc123"
  entity_type: "spec"
  entity_id:   "spec_007"
Output:
{
  "board_id":    "brd_abc123",
  "entity_type": "spec",
  "entity_id":   "spec_007",
  "restored_at": "2026-05-07T14:30:00Z",
  "restored_by": "agent_cognitive_01",
  "cascade": {
    "specs":   ["spec_007"],
    "sprints": ["sprint_007a"],
    "cards":   ["card_91", "card_92", "card_93"]
  }
}
Restore only un-archives entries that were archived together with the root. Children archived independently (a card archived on its own before the spec was archived) stay archived.

Permissions per entity type

80-pulse-feature-inventory.md:814–848. Every entity type that supports archival exposes archive and restore flags under its <entity>.entity.* namespace in core/infra/permissions.py:PERMISSION_REGISTRY:
EntityArchive flagRestore flag
Ideationideation.entity.archiveideation.entity.restore
Refinementrefinement.entity.archiverefinement.entity.restore
Specspec.entity.archivespec.entity.restore
Sprintsprint.entity.archivesprint.entity.restore
Cardcard.entity.archivecard.entity.restore
Cognitive agents typically have all archive/restore flags on the entity types they own. User-facing dashboards typically have them only for cards.

Underlying REST endpoints

MethodPath
POST/boards/{board_id}/archive/{entity_type}/{entity_id}
POST/boards/{board_id}/restore/{entity_type}/{entity_id}
80-pulse-feature-inventory.md:566–567. The MCP tools wrap these directly — most callers should prefer the MCP tools.

How archive interacts with the Knowledge Graph

When a Pulse entity is archived, three things happen on the KG side:
  1. Graph nodes are not deleted. Every Decision, Constraint, Requirement, etc. that was consolidated from the archived entity stays in graph.lbug. Queries continue to return them.
  2. belongs_to edges remain. The structural link from the graph node to its source artifact is preserved. Queries can still walk back to the (now-archived) entity row.
  3. Audit rows are immutable. The consolidation audit table is append-only. An archive operation never edits or deletes audit rows.
The only KG-visible effect of archiving an entity is the count returned in kg_impact.nodes_kept — a count, not a delete. This is by design: a decision that turned out to be wrong is a decision worth keeping in the graph, marked as superseded, so future agents can see why the team moved away from it. If the goal is to retire a decision (not the entity that produced it), the right move is a new consolidation session that adds a supersedes edge from the new node to the old one. See consolidation — add_edge_candidate.

KG retention model

The graph is durable, but it is not append-only forever. Three mechanisms keep it useful at scale:

Supersedence — the structural soft-archive

When a Decision is replaced, the new node is added with a supersedes edge to the old one. The old node is preserved. Queries that walk the chain (kg_get_supersedence_chain) return both, in order. This is the canonical way to “retire” a decision in the KG. There is no delete_decision MCP tool — and there will not be one. The supersedence chain is the audit trail.
[dec_001 "No retry"]  ←supersedes—  [dec_4c20 "Fixed 3s × 5"]  ←supersedes—  [dec_8f1a "Exponential backoff with jitter"]
Queries default to the head of the chain, but the full history is always reachable.

Decay tick — relevance, not deletion

The decay tick worker (kg/workers/kg_decay_tick.py) runs daily by default (kg_decay_tick_interval_minutes = 1440). For each node whose last_recomputed_at is older than kg_decay_tick_staleness_days (default 7), it recomputes relevance_score based on age, citation count, and edge centrality. Stale nodes don’t disappear — they sink in default rankings. kg_find_similar_decisions and kg_query_natural return higher-relevance nodes first. Direct queries (kg_get_decision_history, kg_query_cypher) still return everything. To force a recompute without waiting for the schedule, use kg_tick_run_now.

Dead-letter retention

Failed consolidation entries land in a dead-letter table after kg_queue_max_attempts retries. They are kept indefinitely until an operator either:
  • Calls kg_dead_letter_reprocess to retry them, or
  • Decides the underlying source no longer exists and removes the row directly from the database.
There is no automatic dead-letter purge. This is a deliberate choice — silent loss of consolidation work is worse than a slowly-growing inspection queue.

Audit-row immutability

The consolidation audit table is append-only. Every kg_commit_consolidation call writes one row containing the session id, content hash, every candidate, every reconciliation hint, every override, and the agent identity. Archiving a Pulse entity does not edit or delete audit rows. Migrating the schema does not edit audit rows. The graph state can be reconstructed from the audit table at any point in history.

Retention vs. archive — pick the right tool

You want to…Tool
Hide a spec from the default board view, but keep its decisions queryableokto_pulse_archive_tree
Bring back a spec that was archivedokto_pulse_restore_tree
Retire a decision while preserving the rationale chainNew consolidation session with a supersedes edge — see consolidation
Reduce ranking weight of stale decisionsokto_pulse_kg_tick_run_now (or wait for the daily schedule)
Remove a dead-letter entry that will never succeedDB-level delete (no MCP tool — by design)

Common scenarios

”We retired a feature — archive its specs but keep the decisions”

  1. archive_tree on each retired spec. Cascade carries sprints and cards.
  2. KG nodes for those specs (Decision, Constraint, Requirement) stay. They are returned by every read tool.
  3. To make the supersedence explicit, open a consolidation session and add supersedes edges from the replacement specs’ decisions to the retired ones.

”We archived the wrong spec”

  1. restore_tree on the same (entity_type, entity_id). The full cascade restores.
  2. Any KG nodes consolidated since the archive remain — restore is a belongs_to flip, not a graph rewrite.

”Old decisions are dominating new search results”

  1. Force a decay tick: kg_tick_run_now.
  2. If still ranking high, the issue is recency weighting in find_similar_decisions (0.2 × recency in the formula at kg_service.py:244). Use kg_get_decision_history with since filter for time-bounded results.

Next steps

Overview

Schema, storage layout, and the full 26-tool index.

Health & migration

Operate the consolidation pipeline, dead-letter, and decay tick.
Last modified on May 8, 2026