Resume Session
Loads projected state from the session registry for a task and binds the current session to it. Combines data from all three stores: Postgres (session state), Memgraph (file conflicts), and Qdrant (semantic decision recall).
Behavior
- Re-verify live state from the latest handoff before trusting any prior-session state claim — see Live-State Re-Verification on Resume. A resumed session never acts on prose state copied from a previous session without re-running that section's probe.
- Accept a ticket ID argument (e.g.,
/onex:resume_session TASK-1234) - Query the
session_registryPostgres table for the task - If Found:
- Gather data from all available stores:
a. Session state from Postgres (task progress, files, phase, decisions)
b. File conflicts from Memgraph via
should_emit_conflict_signal()c. Related decisions from Qdrant viaDecisionSearchClient.search_related()d. Recent coordination signals (what happened while session was inactive) - Build full resume context via
format_full_resume_context() - Bind the session via
TaskBinding(delegates to set-session behavior)
- Gather data from all available stores:
a. Session state from Postgres (task progress, files, phase, decisions)
b. File conflicts from Memgraph via
- If Not Found:
- "No session history for TASK-1234. Starting fresh."
- Still bind
task_idfor future correlation
- If Unavailable (DB down):
- "Session registry unavailable: {reason}. Binding task_id locally only."
- Still bind
task_idlocally (degraded mode per Doctrine D4)
Live-State Re-Verification on Resume
A resumed session MUST NOT trust prose state copied from a prior session. A handoff is a snapshot written at the moment the previous session ended; the live system may have moved on. Before acting on any claim about current state (bus health, lane health, PR verdict, container state, queue depth), re-verify it against the live surface.
1. Read the latest handoff (LATEST.md, fallback newest-mtime)
On resume, locate the candidate current-state document in this order:
LATEST.md— the canonical pointer to the most recent handoff. Read it first (in the repo'sdocs/handoffs/directory when present, otherwise the working-directory root).- Fallback: if
LATEST.mdis absent, select the handoff file with the newest modification time (mtime) underdocs/handoffs/.
Only LATEST.md (or, on fallback, the newest-mtime handoff) is treated as the
candidate current-state document. Every older handoff under docs/handoffs/ is
history, not current state.
2. Re-run verify: blocks in live-state sections
Within the chosen handoff, a live-state section is one that carries a verify:
block — a line of the form verify: <date> via <command>, or a fenced block of
probe commands. The verify: block is the probe; the result written next to it is
stale the instant the previous session ended. For every live-state section:
- Re-execute each
verify:probe command exactly as written. - Act only on the freshly re-observed result, never on the value recorded in the handoff.
- If the probe output differs from the recorded value, the recorded value is wrong — the live probe wins.
Example live-state section in a handoff:
### Bus health
status: BUS-IS-DOWN
verify: 2026-06-28 via rpk cluster health
On resume, re-run rpk cluster health and use that output; do not propagate the
recorded BUS-IS-DOWN.
3. Sections without a verify: block are historical
Any section that does not carry a verify: block is historical — narrative,
rationale, decisions made, links, design notes. It is useful context, but it is
not authoritative for current state and must not be used to gate an action.
Historical sections are read for understanding, never re-probed, and never trusted
as live truth.
4. Stale live-state alarms do not gate action
A live-state alarm carried in a handoff — for example BUS-IS-DOWN,
LANE-UNHEALTHY, or QUEUE-WEDGED — that has no re-runnable verify: block, or
whose verify: probe cannot be re-executed on resume, is treated as stale /
historical. A stale alarm is recorded for context only and does not block or
gate any action: re-probe the live surface and act on what the probe returns. The
alarm is current only if a freshly re-run verify: probe reproduces the alarm
condition; otherwise it is discarded as a snapshot artifact.
Implementation
Use the SessionRegistryClient from omniclaude.services.session_registry_client:
from omniclaude.services.session_registry_client import (
SessionRegistryClient,
ModelSessionFound,
ModelSessionNotFound,
ModelRegistryUnavailable,
)
from omniclaude.services.task_binding import TaskBinding
from omniclaude.hooks.coordination import should_emit_conflict_signal
client = SessionRegistryClient() # reads OMNIBASE_INFRA_DB_URL from env
binding = TaskBinding()
if args == "--list":
result = client.list_active_sessions()
if isinstance(result, ModelRegistryUnavailable):
print(f"Session registry unavailable: {result.reason}")
else:
for entry in result:
print(f" {entry.task_id} | {entry.current_phase} | last: {entry.last_activity}")
return
result = client.get_session(task_id)
if isinstance(result, ModelSessionFound):
entry = result.entry
binding.bind(task_id)
# -- Gather enrichment data from all stores --
# 1. File conflicts from Memgraph
conflicts = []
active_sessions = client.list_active_sessions()
if not isinstance(active_sessions, ModelRegistryUnavailable):
current_task = {
"task_id": task_id,
"files_touched": entry.files_touched,
}
other_tasks = [
{"task_id": s.task_id, "files_touched": s.files_touched}
for s in active_sessions
if s.task_id != task_id
]
detected = should_emit_conflict_signal(current_task, other_tasks)
conflicts = [
{"other_task_id": c.other_task_id, "shared_files": c.shared_files}
for c in detected
]
# 2. Semantic decision recall from Qdrant (Doctrine D7: enrichment only)
related_decisions = []
try:
from omnibase_infra.services.session_registry.decision_search import (
DecisionSearchClient,
)
search_client = DecisionSearchClient()
search_results = search_client.search_related(task_id=task_id, limit=5)
related_decisions = [
{
"task_id": r.task_id,
"decision_text": r.decision_text,
"score": r.score,
}
for r in search_results
]
except Exception:
# D7: Qdrant is enrichment only -- failures are non-fatal
pass
# 3. Coordination signals (placeholder -- consumed from Kafka topic)
coordination_signals = []
# -- Build and display full context --
context = client.format_full_resume_context(
entry=entry,
related_decisions=related_decisions,
conflicts=conflicts,
coordination_signals=coordination_signals,
)
print(context)
elif isinstance(result, ModelSessionNotFound):
print(f"No session history for {task_id}. Starting fresh.")
binding.bind(task_id)
elif isinstance(result, ModelRegistryUnavailable):
print(f"Session registry unavailable: {result.reason}. Binding task_id locally only.")
binding.bind(task_id)
List Mode
/onex:resume_session --list queries all active sessions:
- Shows task_id, phase, last activity, files being touched
- Highlights conflicts: two tasks touching the same file
Doctrine Compliance
- D1 (Binding Authority): Delegates binding to
TaskBindingwhich writes.onex_state/active_session.yaml - D4 (Degradation Contracts): Returns Found/NotFound/Unavailable -- never collapses both failure modes into None
- D6 (Projection/Control Separation): Conflict display is advisory -- shown as warnings, not blocking gates
- D7 (Semantic Recall is Enrichment): Qdrant decision recall is optional -- failures are caught and silently skipped; resume works without Qdrant
- D8 (Integration Proof): Phase 3 proof: decision recorded -> embedded -> semantically recalled on resume
Note: This skill executes directly (not via general-purpose) because it is a synchronous, user-invoked operation with no need for agent routing.