Skip to content
This repository was archived by the owner on May 26, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions BUILD_DEVIATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,31 @@ Format:

## Open

### D-kr3-st1-capability-check-deferred

- **Bucket**: KR-3 ST1 (`iso_node_*` tool family)
- **Why**: Each `iso_node_*` tool handler is supposed to gate its
invocation through a Python mirror of the TS-side
`assertKoraCanPerform(actor_kind, capability)` (Plan 04 helper at
`packages/sea-mcp-server/src/capability-matrix.ts:657`). That
Python mirror ships in KR-6 as part of the Constitution pre-screen
middleware. Spec § ST1 § "Capability check" explicitly pre-authorizes
this deferral: "if the Python helper isn't ready, BUILD_DEVIATIONS
+ use a stub that always allows (with verbatim Rule-6 log
'BUILD_DEVIATIONS D-kr3-st1-capability-check-deferred — wires in KR-6')".
- **Closes when**: KR-6 ships the Python mirror — at that point
`tools/iso_node.py:assert_kora_can_perform` body switches from
"no-op + log" to the real check, and the per-tool capability map
`_TOOL_CAPABILITIES` becomes the gating source of truth.
- **Guarded by**:
- `plugins/memory/isokron/tools/iso_node.py` —
`assert_kora_can_perform` logs a WARNING tagged with the
deviation ID on every call so operators can grep how often
the stub is being relied on.
- `tests/plugins/memory/test_iso_node_tools.py` —
`test_assert_kora_can_perform_stub_logs_deviation_id` asserts
the log line carries the deviation ID + the capability name.

### D-kr2-st4-no-chain-emit-mcp-tool

- **Bucket**: KR-2 ST4 (chain event emission + recent events read + finalize)
Expand Down
27 changes: 16 additions & 11 deletions plugins/memory/isokron/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,14 @@ def shutdown(self) -> None:
# -- Static metadata --------------------------------------------------

def get_tool_schemas(self) -> List[Dict[str, Any]]:
"""Return tool schemas (ABC-required).
"""Return the model-facing tool schemas this provider exposes.

ST1: return empty list — no tools surfaced until KR-3 wires the
``iso_node_*`` / ``iso_link_*`` family on top of this provider.
KR-3 ST1 ships the ``iso_node_*`` family (4 tools). ST2 adds
``iso_link_*`` (3 tools). ST3 wires registration polish.
"""
return []
from .tools import ISO_NODE_TOOL_SCHEMAS

return list(ISO_NODE_TOOL_SCHEMAS)

def get_config_schema(self) -> List[Dict[str, Any]]:
return ISOKRON_CONFIG_SCHEMA
Expand Down Expand Up @@ -513,15 +515,18 @@ def handle_tool_call(
args: Dict[str, Any],
**kwargs: Any,
) -> str:
"""Handle a tool call routed by name.
"""Route a tool call to the right typed-graph handler.

The provider returns no tools from ``get_tool_schemas`` (the
``iso_node_*`` / ``iso_link_*`` family lands in KR-3), so this
hook should never be invoked in normal operation. Inherit the
ABC's "provider X does not handle tool Y" error so a routing
bug surfaces with a clear actionable message.
KR-3 ST1 dispatches ``iso_node_*``. ST2 adds ``iso_link_*``.
Unknown tool names fall through to the ABC default which
raises a clear "Provider isokron does not handle tool X" error.
"""
return super().handle_tool_call(tool_name, args, **kwargs)
del kwargs
if tool_name.startswith("iso_node_"):
from .tools import handle_iso_node_tool_call

return handle_iso_node_tool_call(self, tool_name, args)
return super().handle_tool_call(tool_name, args)

# -- Scratchpad reads (sync wrappers around the async reads) -----------

Expand Down
52 changes: 52 additions & 0 deletions plugins/memory/isokron/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Beads-pattern typed-graph tools for Kora's memory surface.

KR-3 ships model-facing MCP tools that replace Hermes' flat ``memory``
tool surface with typed-node + typed-edge operations against the
IsoKron substrate.

- **ST1 (this PR)** — ``iso_node_*`` family (4 tools) against
``kronicle.agent_scratchpad_entries`` (Plan 02 typed scratchpad).
- **ST2** — ``iso_link_*`` family (3 tools) against the RelationLink
substrate (ADR-0033/0034).
- **ST3** — registration polish + Hermes flat memory deprecation +
system prompt updates.

Spec § 32 calls out a v0.1 simplification: ``iso_node_*`` reads + writes
operate on the scratchpad surface only. Full read-across the 17 entity
tables lands in KR-3a or later when read patterns + permissions firm up.

Tools that need writes go through the Sea MCP tool surface (cap_-gated
+ chain-audited). As of substrate main, two write tools are missing:

- ``kora__write_agent_scratchpad`` — tracked as
D-kr2-st3-no-scratchpad-write-mcp-tool. Affects ``iso_node_create``
and ``iso_node_supersede``.
- ``kora__append_event`` — tracked as
D-kr2-st4-no-chain-emit-mcp-tool. Affects ``iso_node_supersede``'s
``kora.node.superseded`` event.

The handlers attempt the writes through the deferred surfaces and
surface a structured ``{"deferred": true, ...}`` payload back to the
model so it can adapt (e.g. skip a follow-up write that would have
keyed off the new entry_id). When the substrate tools ship, the
handlers' bodies stay the same — only ``scratchpad.write_scratchpad_entry``
and ``events.emit_kora_event`` change.

Capability gating (Plan 04 ``actorHasCapability`` Python mirror) ships
in KR-6 — until then ``assert_kora_can_perform`` is a stub that always
allows and logs ``D-kr3-st1-capability-check-deferred``.
"""

from .iso_node import (
ISO_NODE_TOOL_SCHEMAS,
NODE_KINDS,
assert_kora_can_perform,
handle_iso_node_tool_call,
)

__all__ = [
"ISO_NODE_TOOL_SCHEMAS",
"NODE_KINDS",
"assert_kora_can_perform",
"handle_iso_node_tool_call",
]
Loading