feat(python): durable transaction subsystem and Kiro IDE 1.0 adapter

Land the crash-recoverable managed-file transaction engine (journal,
staging, commit, restart recovery, opaque snapshots, private artifacts,
Windows DACL hardening) and the Kiro IDE 1.0 workspace hook plugin with
legacy migration.

Repair five defects that kept the suite red (426 failures to zero):

- Anchor the transaction state directory at ~/.elydora/transactions
  instead of the shared system temp directory. The recovery journal now
  shares fate with the runtimes it repairs, survives temp cleaners, and
  isolates naturally under per-test fake homes.
- Skip pending-transaction recovery when no state directory exists so
  rejected installs never create runtime directories.
- Create the runtime root's parent before hardening it, matching the Go
  SDK's expand behavior for fresh homes.
- Stop using file timestamps as identity evidence: Windows exposes
  mtime/ctime with delayed cross-handle visibility. Reads now prove
  stability through a verification re-read; snapshots compare device,
  inode, size, and SHA-256.
- Stop comparing synthetic Windows permission bits across stat
  boundaries: path stats derive execute bits from file extensions while
  handle stats do not, which falsely flagged every managed .cmd wrapper.
  Snapshots store raw modes for restoration and compare portable bits.

Reservation cleanup now verifies content before discarding so a
concurrent writer's file at a rollback path is preserved, and the
install-failure tests assert the precise contract: no agent runtime
residue, with the durable state directory allowed to remain.

Gates: pytest 807 passed, mypy clean, pip check clean, wheel builds.
This commit is contained in:
ZacharyZhang-NY committed 2026-07-21 18:53:06 -04:00
1 parent dce7d1288a
commit e37418c489
53 files changed
+11855 -531

No files matched your search

+395
View File
@@ -0,0 +1,395 @@
"""Kiro IDE v1 hook schema, ownership, and rendering."""
from __future__ import annotations
from dataclasses import dataclass
import json
import os
from typing import Any, Callable, Dict, List, Optional, Tuple
from ._managed_files import FileSnapshot
from ._strict_json import JsonObject, parse_json_object
from .kiroide_command import (
KiroIdeRuntimeReference,
build_kiroide_command,
kiroide_runtime_reference,
legacy_audit_reference,
legacy_guard_reference,
owned_kiroide_runtime_reference,
same_kiroide_agent_id,
same_kiroide_path,
)
AGENT_KEY = "kiroide"
AUDIT_SCRIPT = "hook.py"
CONFIG_FILE = "elydora-audit.json"
GUARD_SCRIPT = "guard.py"
LEGACY_CONFIG_FILE = "elydora-audit.kiro.hook"
HOOK_TIMEOUT_SECONDS = 10
GUARD_NAME = "elydora-guard"
AUDIT_NAME = "elydora-audit"
GUARD_DESCRIPTION = "Block tool use when the Elydora agent is frozen"
AUDIT_DESCRIPTION = "Record tool use in the Elydora audit trail"
KIRO_TRIGGERS = frozenset(
{
"SessionStart",
"Stop",
"PreToolUse",
"PostToolUse",
"PreTaskExec",
"PostTaskExec",
"UserPromptSubmit",
"PostFileCreate",
"PostFileSave",
"PostFileDelete",
}
)
@dataclass(frozen=True)
class KiroIdeDocument:
exists: bool
file_path: str
root: JsonObject
hooks: List[JsonObject]
raw: Optional[str] = None
snapshot: Optional[FileSnapshot] = None
@dataclass(frozen=True)
class RenderedKiroIdeDocument:
document: KiroIdeDocument
changed: bool
next_source: Optional[str] = None
@dataclass(frozen=True)
class KiroIdeRuntimeContract:
agent_id: str
guard_path: str
audit_path: str
def _validate_action(value: Any, label: str) -> JsonObject:
if not isinstance(value, dict):
raise ValueError(f"{label} action must be an object")
action = dict(value)
action_type = action.get("type")
if action_type == "command":
if not isinstance(action.get("command"), str) or not action["command"]:
raise ValueError(f"{label} command action requires a non-empty command")
return action
if action_type == "agent":
if not isinstance(action.get("prompt"), str) or not action["prompt"]:
raise ValueError(f"{label} agent action requires a non-empty prompt")
return action
raise ValueError(f'{label} action has unsupported type "{action_type}"')
def _validate_hook(value: Any, index: int, document_label: str) -> JsonObject:
label = f"{document_label} hooks[{index}]"
if not isinstance(value, dict):
raise ValueError(f"{label} must be an object")
hook = dict(value)
if not isinstance(hook.get("name"), str) or not hook["name"]:
raise ValueError(f"{label} requires a name")
trigger = hook.get("trigger")
if not isinstance(trigger, str) or trigger not in KIRO_TRIGGERS:
raise ValueError(f'{label} has unsupported trigger "{trigger}"')
if "description" in hook and not isinstance(hook["description"], str):
raise ValueError(f"{label} description must be a string")
if "matcher" in hook and not isinstance(hook["matcher"], str):
raise ValueError(f"{label} matcher must be a string")
if "timeout" in hook and (
type(hook["timeout"]) is not int or hook["timeout"] < 0
):
raise ValueError(f"{label} timeout must be a non-negative integer")
if "enabled" in hook and not isinstance(hook["enabled"], bool):
raise ValueError(f"{label} enabled must be a boolean")
hook["action"] = _validate_action(hook.get("action"), label)
return hook
def parse_kiroide_document(
file_path: str,
raw: str,
snapshot: Optional[FileSnapshot] = None,
document_label: str = "Kiro IDE hooks",
) -> KiroIdeDocument:
root = parse_json_object(raw, f"{document_label} at {file_path}")
if root.get("version") != "v1":
raise ValueError(f'{document_label} version must be "v1": {file_path}')
hooks = root.get("hooks")
if not isinstance(hooks, list):
raise ValueError(
f'{document_label} field "hooks" must be an array: {file_path}'
)
return KiroIdeDocument(
True,
file_path,
root,
[_validate_hook(hook, index, document_label) for index, hook in enumerate(hooks)],
raw,
snapshot,
)
def create_kiroide_document(file_path: str) -> KiroIdeDocument:
return KiroIdeDocument(False, file_path, {}, [])
def _managed_specification(name: object) -> Optional[Tuple[str, str, str]]:
if name == GUARD_NAME:
return GUARD_DESCRIPTION, "PreToolUse", GUARD_SCRIPT
if name == AUDIT_NAME:
return AUDIT_DESCRIPTION, "PostToolUse", AUDIT_SCRIPT
return None
def _command_reference(
hook: JsonObject, current_executable: bool
) -> Optional[KiroIdeRuntimeReference]:
specification = _managed_specification(hook.get("name"))
action = hook.get("action")
if (
specification is None
or not isinstance(action, dict)
or action.get("type") != "command"
or not isinstance(action.get("command"), str)
):
return None
reader = (
kiroide_runtime_reference
if current_executable
else owned_kiroide_runtime_reference
)
return reader(action["command"], specification[2])
def _owned_reference(hook: JsonObject) -> Optional[KiroIdeRuntimeReference]:
return _command_reference(hook, False)
def _exact_reference(
hook: JsonObject, current_executable: bool
) -> Optional[KiroIdeRuntimeReference]:
specification = _managed_specification(hook.get("name"))
reference = _command_reference(hook, current_executable)
action = hook.get("action")
if (
specification is None
or reference is None
or set(hook)
!= {
"name",
"description",
"trigger",
"matcher",
"action",
"timeout",
"enabled",
}
or hook.get("description") != specification[0]
or hook.get("trigger") != specification[1]
or hook.get("matcher") != ".*"
or hook.get("timeout") != HOOK_TIMEOUT_SECONDS
or hook.get("enabled") is not True
or not isinstance(action, dict)
or set(action) != {"type", "command"}
):
return None
return reference
def _managed_reference(hook: JsonObject) -> Optional[KiroIdeRuntimeReference]:
return _exact_reference(hook, True)
def _portable_managed_reference(
hook: JsonObject,
) -> Optional[KiroIdeRuntimeReference]:
return _exact_reference(hook, False)
_ReferenceReader = Callable[
[JsonObject], Optional[KiroIdeRuntimeReference]
]
def _managed_pair(
hooks: List[JsonObject], reader: _ReferenceReader
) -> Optional[
Tuple[
Tuple[int, KiroIdeRuntimeReference],
Tuple[int, KiroIdeRuntimeReference],
]
]:
managed = [
(index, hook)
for index, hook in enumerate(hooks)
if _managed_specification(hook.get("name")) is not None
]
guards = [item for item in managed if item[1].get("name") == GUARD_NAME]
audits = [item for item in managed if item[1].get("name") == AUDIT_NAME]
if len(managed) != 2 or len(guards) != 1 or len(audits) != 1:
return None
guard = reader(guards[0][1])
audit = reader(audits[0][1])
if (
guard is None
or audit is None
or not same_kiroide_agent_id(guard.agent_id, audit.agent_id)
):
return None
return (guards[0][0], guard), (audits[0][0], audit)
def _owned_references(
hooks: List[JsonObject],
) -> Dict[int, KiroIdeRuntimeReference]:
return {
index: reference
for index, hook in enumerate(hooks)
if (reference := _owned_reference(hook)) is not None
}
def require_available_kiroide_hooks(hooks: List[JsonObject]) -> None:
owned = _owned_references(hooks)
for index, hook in enumerate(hooks):
if _managed_specification(hook.get("name")) and index not in owned:
raise ValueError(
f'Kiro IDE hook name "{hook.get("name")}" conflicts with '
"the Elydora contract"
)
def build_kiroide_hook(name: str, script_path: str) -> JsonObject:
specification = _managed_specification(name)
if specification is None:
raise ValueError(f"Unsupported managed Kiro IDE hook name: {name}")
return {
"name": name,
"description": specification[0],
"trigger": specification[1],
"matcher": ".*",
"action": {"type": "command", "command": build_kiroide_command(script_path)},
"timeout": HOOK_TIMEOUT_SECONDS,
"enabled": True,
}
def without_managed_kiroide_hooks(
hooks: List[JsonObject], agent_id: str = ""
) -> List[JsonObject]:
owned = _owned_references(hooks)
result = []
for index, hook in enumerate(hooks):
reference = owned.get(index)
remove = reference is not None and (
not agent_id or same_kiroide_agent_id(reference.agent_id, agent_id)
)
if not remove:
result.append(hook)
return result
def _entirely_managed(document: KiroIdeDocument) -> bool:
return (
document.exists
and set(document.root) == {"version", "hooks"}
and len(document.hooks) == 2
and _managed_pair(document.hooks, _portable_managed_reference)
is not None
)
def render_kiroide_document(
document: KiroIdeDocument, hooks: List[JsonObject]
) -> RenderedKiroIdeDocument:
if not document.exists and not hooks:
return RenderedKiroIdeDocument(document, False)
if len(hooks) == len(document.hooks) and all(
hook is document.hooks[index] for index, hook in enumerate(hooks)
):
return RenderedKiroIdeDocument(document, False)
if not hooks and _entirely_managed(document):
return RenderedKiroIdeDocument(document, True)
root = {**document.root, "version": "v1", "hooks": hooks}
next_source = json.dumps(root, indent=2, ensure_ascii=False) + "\n"
parse_kiroide_document(document.file_path, next_source)
return RenderedKiroIdeDocument(
document,
next_source != document.raw,
next_source,
)
def kiroide_runtime_contracts(
hooks: List[JsonObject],
) -> List[KiroIdeRuntimeContract]:
pair = _managed_pair(hooks, _managed_reference)
if pair is None:
return []
guard = pair[0][1]
audit = pair[1][1]
return [
KiroIdeRuntimeContract(
guard.agent_id,
guard.script_path,
audit.script_path,
)
]
def managed_kiroide_hooks_present(hooks: List[JsonObject]) -> bool:
return any(
_managed_specification(hook.get("name")) is not None for hook in hooks
)
def legacy_kiroide_runtime_contract(
raw: str, file_path: str
) -> Optional[KiroIdeRuntimeContract]:
root = parse_json_object(raw, f"legacy Kiro IDE hook at {file_path}")
if (
set(root) != {"name", "description", "version", "hooks"}
or root.get("name") != "Elydora Audit"
or root.get("description")
!= "Sends tool-use events to the Elydora tamper-evident audit platform"
or root.get("version") != "1.0.0"
or not isinstance(root.get("hooks"), dict)
):
return None
hooks = root["hooks"]
if set(hooks) != {"pre_tool_use", "post_tool_use"}:
return None
guard_hook = hooks["pre_tool_use"]
audit_hook = hooks["post_tool_use"]
if (
not isinstance(guard_hook, dict)
or not isinstance(audit_hook, dict)
or set(guard_hook) != {"command", "timeout_ms"}
or set(audit_hook) != {"command", "timeout_ms"}
or guard_hook.get("timeout_ms") != 5000
or audit_hook.get("timeout_ms") != 5000
):
return None
guard = legacy_guard_reference(guard_hook.get("command"), GUARD_SCRIPT)
audit = legacy_audit_reference(audit_hook.get("command"), AUDIT_SCRIPT)
if (
guard is None
or audit is None
or not same_kiroide_agent_id(guard.agent_id, audit.agent_id)
or not same_kiroide_path(
os.path.dirname(guard.script_path), os.path.dirname(audit.script_path)
)
):
return None
return KiroIdeRuntimeContract(
guard.agent_id,
guard.script_path,
audit.script_path,
)