Skip to content
This repository was archived by the owner on May 26, 2026. It is now read-only.

feat(KR-3 ST1): iso_node_* typed-graph tool family#9

Merged
rafe-walker merged 1 commit into
mainfrom
feat/kora-KR3-iso-node-tools
May 20, 2026
Merged

feat(KR-3 ST1): iso_node_* typed-graph tool family#9
rafe-walker merged 1 commit into
mainfrom
feat/kora-KR3-iso-node-tools

Conversation

@rafe-walker

Copy link
Copy Markdown
Owner

Summary

KR-3 ST1 ships the iso_node_* typed-graph tool family — 4 model-facing MCP tools that replace Hermes' flat memory.set / memory.append / memory.get surface with a typed-node API backed by Plan 02's kronicle.agent_scratchpad_entries.

Tool Purpose Write/Read
iso_node_create Append a typed node (18 canonical IsoKron entity kinds) write (deferred)
iso_node_read Point read by entry_id read
iso_node_search Filter by node_kind + free-text substring + cross_agent_only read
iso_node_supersede Append-only revision (inherits node_kind); emits kora.node.superseded write (deferred)

v0.1 simplification per spec § 32: iso_node_* operates only against the scratchpad surface. Full read-across the 17 entity tables lands in KR-3a once read patterns + permissions firm up. The model still gets a richer memory surface than flat MEMORY.md, just scoped to Kora's own scratchpad.

Deferred-write envelopes

Write paths route through scratchpad.write_scratchpad_entry which still raises ScratchpadWriteNotAvailableError (KR-2 ST3 deferral). The tool handlers catch the defer and return a structured envelope:

{
  "ok": false,
  "deferred": true,
  "deviation_id": "D-kr2-st3-no-scratchpad-write-mcp-tool",
  "message": "[kora.isokron.todo] ..."
}

In-band signal to the model rather than an exception escape. When K-8 ships the Sea MCP write tool, write_scratchpad_entry body swaps to the MCP call and these envelopes naturally turn into {"ok": true, "entry_id": "..."} — handler code stays unchanged.

Capability gating

Each handler asserts the relevant cap_* via assert_kora_can_perform (e.g. iso_node_createcap_write_agent_scratchpad). The Python mirror of TS-side assertKoraCanPerform ships in KR-6 (Constitution pre-screen middleware); until then assert_kora_can_perform is a no-op stub that logs every call tagged with new BUILD_DEVIATIONS entry D-kr3-st1-capability-check-deferred so operators can grep how often the stub is being relied on. Spec § ST1 explicitly pre-authorized this deferral.

v0.1 content encoding

The substrate's kronicle.agent_scratchpad_entries.scratchpad_kind is a closed 7-value enum, separate from the broader 18 IsoKron entity kinds. v0.1 packs node_kind + title into the content_inline header so reads can recover it without schema changes:

iso_node v0.1
node_kind: Decision
title: Pick IsoKron
---
We picked it because ...

The body delimiter (\n---\n) lets the model include arbitrary markdown without breaking the parse. Legacy / non-iso_node entries surface as body-only (no node_kind / title).

What landed

File Change
plugins/memory/isokron/tools/__init__.py new — surface for the iso_node tools (re-exports ISO_NODE_TOOL_SCHEMAS, NODE_KINDS, assert_kora_can_perform, handle_iso_node_tool_call).
plugins/memory/isokron/tools/iso_node.py new — 4 tool schemas (OpenAI function-call format), content packing/unpacking, capability stub, 4 handlers, dispatcher. ~470 LOC.
plugins/memory/isokron/provider.py get_tool_schemas now returns the 4 iso_node schemas; handle_tool_call dispatches iso_node_* by prefix; falls through to ABC default for unknown tool names.
BUILD_DEVIATIONS.md new entry D-kr3-st1-capability-check-deferred under Open — closes when KR-6 ships the Python assertKoraCanPerform mirror.
tests/plugins/memory/test_iso_node_tools.py new — 22 tests (see test plan).
tests/plugins/memory/test_isokron_provider_skeleton.py test_tool_schemas_empty_at_st1test_tool_schemas_exposes_iso_node_family (asserts the 4 expected names + OpenAI shape).

Test plan

22 new tests, all passing:

Schemas + registration (3):

  • test_iso_node_schemas_are_well_formed — 4 schemas, OpenAI function-call shape
  • test_node_kinds_canonical_list_has_18_entries — 17 IsoKron + KronicleBlock
  • test_iso_node_create_schema_enum_matches_node_kinds — JSON Schema enum in sync with Python NODE_KINDS tuple

Content packing (3):

  • test_pack_then_unpack_round_trips_fields
  • test_unpack_handles_legacy_unpacked_content
  • test_unpack_handles_null_content_inline

Capability stub (1):

  • test_assert_kora_can_perform_stub_logs_deviation_id — log line carries both the deviation ID + the capability name

iso_node_create (4):

  • Returns deferred envelope with the correct deviation_id
  • Rejects invalid node_kind
  • Rejects invalid scratchpad_kind
  • Errors on missing required fields

iso_node_read (3):

  • Finds entry in own cache
  • Finds entry in cross-agent cache
  • Returns error on missing entry

iso_node_search (4):

  • Filters by node_kind
  • Free-text substring case-insensitive
  • Respects server-side limit cap (max 50)
  • cross_agent_only includes other actor_kinds (critic, oracle, etc.)

iso_node_supersede (2):

  • Inherits node_kind from the original entry — verified by checking the deferred submission actually ran (means lookup succeeded + packing happened before the write defer)
  • Errors when original entry can't be resolved

Dispatch (2):

  • Unknown tool name in iso_node dispatcher raises
  • provider.handle_tool_call routes iso_node_* by prefix

Gates

  • ty check7,337 diagnostics, zero-delta vs KR-2 ST4 baseline.
  • pytest tests/plugins/memory/270/270 passing (22 new ST1 + 248 pre-ST1).
  • Full suite via xdist (-n auto): 24,535 / 157 failed / 43 errors / 129 skipped. Same tests/tools/* + tests/tui_gateway/* xdist isolation noise as KR-2 ST2-ST4; the failing kanban tests pass in isolation. None of the failures touch plugins/memory/isokron/.

Rule-6 / BUILD_DEVIATIONS / Open asks

  • New deviation D-kr3-st1-capability-check-deferred — closes when KR-6 ships the Python actorHasCapability mirror.
  • Existing deviations carry forward: D-kr2-st2-capability-matrix-mirror ← K-7, D-kr2-st3-no-scratchpad-write-mcp-tool ← K-8 (affects iso_node_create + iso_node_supersede writes), D-kr2-st4-no-chain-emit-mcp-tool ← K-9 (affects iso_node_supersede's kora.node.superseded event).
  • All four deviations follow the same shape: signature is forward-stable, body swaps from raise <DeferredError> to mcp_client.invoke(...). Tool handlers stay unchanged.

Standing by for KR-3 ST2 dispatch (iso_link_* family — typed edges; ST2 recon on RelationLink table name + schema).

🤖 Generated with Claude Code

Ships 4 model-facing MCP tools that replace Hermes' flat memory.set /
memory.append / memory.get with a typed-node API backed by Plan 02's
kronicle.agent_scratchpad_entries:

* iso_node_create — append a typed node (18 canonical IsoKron entity
  kinds: Decision / Gotcha / Pattern / Convention / Concept / Ticket /
  FailedAttempt / AcceptanceTest / GamingPattern / Project / Component
  / Milestone / CrossCut / ExternalDependency / Resource / Tool /
  Schema / KronicleBlock). v0.1 packs node_kind into content_inline
  header so iso_node_read / search recover it without schema changes
  (dedicated column lands in KR-3a if read patterns firm up).
* iso_node_read — point read by entry_id; searches own + cross-agent
  caches (KR-3a will add a dedicated single-row substrate fetch).
* iso_node_search — node_kind filter + free-text substring (FTS in
  KR-3a) + optional cross_agent_only inclusion + server-side limit
  cap (max 50 per spec).
* iso_node_supersede — append-only revision; inherits node_kind from
  the original entry; attempts the supersession write + the
  kora.node.superseded chain event (both deferred behind their
  existing BUILD_DEVIATIONS entries).

Write paths route through scratchpad.write_scratchpad_entry which
still defers behind ScratchpadWriteNotAvailableError. The tool
handlers catch the defer + return a structured envelope
{"ok": False, "deferred": True, "deviation_id":
"D-kr2-st3-no-scratchpad-write-mcp-tool", "message": ...} so the
model gets an in-band signal rather than an exception. Read paths
are fully functional (work today against the existing read surface).

Provider wiring:
* get_tool_schemas returns the 4 schemas in OpenAI function-call
  format (matches mem0/honcho/etc. existing memory providers).
* handle_tool_call dispatches "iso_node_*" by prefix to the new
  handler; unknown tool names fall through to the ABC default's
  clear "Provider isokron does not handle tool X" error.

Capability gating (Plan 04 actorHasCapability Python mirror) ships
in KR-6 — until then the in-module assert_kora_can_perform helper
is a stub that always allows and logs every invocation tagged with
the new BUILD_DEVIATIONS entry D-kr3-st1-capability-check-deferred
so operators can grep how often the stub is being relied on. Spec
§ ST1 explicitly pre-authorized this deferral path.

Tests (22 new):
* Schema validity (well-formed shape, 18-node-kind enum sync,
  OpenAI function-call format).
* Content packing/unpacking round-trip + legacy / null fallback.
* Capability check stub logs the deviation ID.
* iso_node_create: deferred-write envelope shape; invalid node_kind
  rejection; invalid scratchpad_kind rejection; missing required
  fields.
* iso_node_read: hits own cache; hits cross-agent cache; returns
  error on missing entry.
* iso_node_search: node_kind filter; case-insensitive text query;
  server-side limit cap; cross_agent_only inclusion.
* iso_node_supersede: inherits original's node_kind; errors when
  original is missing.
* Dispatch: unknown tool raises; provider.handle_tool_call routes
  iso_node_* by prefix.

ST1 skeleton test updated: test_tool_schemas_empty_at_st1 replaced
with test_tool_schemas_exposes_iso_node_family (the family is the
new tool surface).

Local gates:
* ty check — 7,337 diagnostics, zero-delta vs KR-2 ST4 baseline.
* pytest tests/plugins/memory/ — 270/270 passing (22 new ST1 + 248
  pre-ST1).
* Full suite via xdist (-n auto): 24,535 / 157 fail / 43 err / 129
  skip. xdist isolation noise in tests/tools/* (kanban + others
  pass in isolation — same family as ST2-ST4 documented noise);
  none touch plugins/memory/isokron/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rafe-walker rafe-walker merged commit 41ddc20 into main May 20, 2026
rafe-walker added a commit that referenced this pull request May 22, 2026
Phase 2 Feature 5 frontend. Pairs with CC#3 KR-FEAT-SLACK-DM (#119).

- Backend /api/slack-dm/recent stub (4 messages spanning happy path + filtered_non_joshua).
- SlackDMPanel.tsx (stats + filter pills + newest-first timeline + expandable rows).
- Dashboard card #9 (MessageCircle icon) + nav + 15 backend tests.

4-layer security contract (extending standard 3-layer with a 4th walk-payload guard for Slack-specific tokens): no raw U... Slack user IDs + channel_id stub shape + plain-text rendering (with dangerouslySetInnerHTML ban) + Slack token/signing-secret walk-payload sweep.

Layout: 9 cards = clean 3+3+3 with lg:grid-cols-3 — landed the symmetry sweet spot, no orphans.

219/219 admin-panel tests pass across 20 suites; tsc + vite clean.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant