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

feat(KR-8): swap scratchpad-write defer for kora__write_agent_scratchpad MCP call — closes D-kr2-st3#16

Merged
rafe-walker merged 1 commit into
mainfrom
feat/kora-KR8-scratchpad-write-mcp-swap
May 20, 2026
Merged

feat(KR-8): swap scratchpad-write defer for kora__write_agent_scratchpad MCP call — closes D-kr2-st3#16
rafe-walker merged 1 commit into
mainfrom
feat/kora-KR8-scratchpad-write-mcp-swap

Conversation

@rafe-walker

Copy link
Copy Markdown
Owner

Summary

KR-8 swaps the deferred scratchpad-write surface for a real kora__write_agent_scratchpad MCP call via the KR-7a-wired IsoKronMCPClient. Closes D-kr2-st3-no-scratchpad-write-mcp-tool.

Mechanical follow-on unlocked by K-8 (bd165eb2). Same pattern as KR-7's chain-emit swap — write_scratchpad_entry body replaces raise ScratchpadWriteNotAvailableError() with await mcp_client.invoke(...) returning the substrate-assigned scratchpad_entry_id. ~40 LOC across implementation + tests + docs.

Production-test posture (same as KR-7 / KR-7b)

K-8's kora__write_agent_scratchpad handler is a notImplementedHandler stub on substrate main; substrate-team's dispatch tier (task NousResearch#395) un-stubs + bridges Layer-A wsk_* auth → Layer-B actor_kind='kora'. KR-8 code shape is sound; mock tests verify correctness; production deploys wait on dispatch tier landing.

K-DG note: substrate-canonical chain literal

K-8's internal flow emits kronicle.agent_scratchpad.created (NOT kora.scratchpad.entry.created). The runtime doesn't pass an event_type — the substrate emits internally as part of the SECDEF flow.

What landed

File Change
plugins/memory/isokron/scratchpad.py write_scratchpad_entry body swap: invokes kora__write_agent_scratchpad with {workspace_id, scratchpad_kind, visibility_scope, content_inline, content_hash}. BLAKE3 hash computed via existing compute_scratchpad_content_hash. Defensive: None client → ValueError; unexpected response shape → RuntimeError. mcp_client param now required. ScratchpadWriteNotAvailableError kept tagged [kora.isokron.deprecated] for one-release runway.
plugins/memory/isokron/provider.py _attempt_scratchpad_write stops catching the defer-error; fetches IsoKronMCPClient via get_mcp_client(); catches IsoKronMCPInvocationError + defensive Exception at lifecycle boundary, logs at ERROR ([kora.scratchpad.write.failed]). Successful writes log INFO [kora.scratchpad.write] with the substrate entry_id + invalidate the own-scratchpad cache.
plugins/memory/isokron/tools/iso_node.py iso_node_create + iso_node_supersede handlers: dropped the deferred-envelope path. Success → {"ok": True, "entry_id": <substrate-uuid>}. Substrate failure → {"ok": False, "substrate_error": True, "tool_name", "message"}. iso_node_supersede passes the new entry_id into the kora.node.superseded chain event payload so audit consumers can trace the supersession chain.
BUILD_DEVIATIONS.md D-kr2-st3 moved Open → Closed with Rule-5 spec-quote, call-site refactor inventory, production-test posture, deprecation-runway note.
plugins/memory/isokron/README.md Deferred-surface table trimmed 2 → 1 row. D-kr2-st3 moves to Recently-closed. Individual pitfall rewritten: success/failure log paths + operator grep pointer.

Test plan

Existing tests updated to post-KR-8 reality (all passing):

test_scratchpad.py — replaced test_write_scratchpad_entry_raises_deferred_write_error with:

  • Happy path: invokes kora__write_agent_scratchpad with spec-pinned arg shape; returns substrate entry_id; BLAKE3 hex hash present.
  • Propagates IsoKronMCPInvocationError from substrate.
  • Rejects None mcp_client (defensive ValueError).
  • Rejects unexpected response shape (RuntimeError).
  • Deprecation runway: ScratchpadWriteNotAvailableError still importable; carries [kora.isokron.deprecated] tag.

test_scratchpad.py (provider integration):

  • sync_turn Kora-action triggers real MCP write; success-INFO log fires.
  • sync_turn invalidates own-scratchpad cache on successful write.
  • sync_turn substrate error → ERROR log; session lifecycle continues.
  • on_memory_write embeds [memory.{action} → {target}] prefix in content_inline; real MCP invoke fires + success-INFO log.

test_iso_node_tools.py:

  • test_iso_node_create_returns_ok_envelope_with_substrate_entry_id (replaces _returns_deferred_payload).
  • test_iso_node_create_substrate_error_surfaces_structured_envelope (new).
  • test_iso_node_supersede_inherits_node_kind_from_original flipped to verify success + inherited node_kind: Pattern in new content_inline + kora.node.superseded emit fires with new entry_id in payload.

test_tool_finalize.py:

  • Round-trip create-then-search asserts success envelope.
  • Round-trip read-then-supersede asserts success envelope.

test_provider_end_to_end.py:

  • _FakeMcpClient extended to handle kora__write_agent_scratchpad (returns spe-mock-N).
  • E2E asserts 3 [kora.scratchpad.write] INFO logs (sync_turn + on_memory_write + on_delegation) + 3 scratchpad_write_calls + 2 append_event_calls. Assertion narrowed to NOT count cap-matrix fetch — KR-7b adds that in its own PR, kept parallel-mergeable.

Gates

  • ty check7,337 diagnostics, zero-delta vs KR-7 baseline.
  • pytest tests/plugins/memory/357/357 passing.
  • Full suite via xdist (-n auto): 24,654 passed / 168 failed / 129 skipped. Δ vs KR-7b (24,652/175/129): +2 passed, -7 failed. Same tests/tools/* + tests/tui_gateway/* xdist isolation noise; none touch plugins/memory/isokron/.

Rule-6 / BUILD_DEVIATIONS / Open asks

Closed: D-kr2-st3-no-scratchpad-write-mcp-tool (KR-8).

1 deferral remains open:

Deviation Closes when
D-kr3-st2-no-relationlink-write-mcp-tool KR-9 (K-10 merged; PM dispatched)

Shipping KR-9 next. After it merges, all 4 BUILD_DEVIATIONS are closed code-side; standing-by state becomes dispatch-tier-gated (substrate task NousResearch#395 + service-token mint for production).

🤖 Generated with Claude Code

@rafe-walker rafe-walker reopened this May 20, 2026
@rafe-walker rafe-walker force-pushed the feat/kora-KR8-scratchpad-write-mcp-swap branch from b37df46 to 0c57dbf Compare May 20, 2026 22:15
…pad MCP call — closes D-kr2-st3

Mechanical follow-on swap unlocked by K-8 (substrate `bd165eb2`) +
KR-7a's MCP client transport. Same pattern as KR-7 chain-emit:
write_scratchpad_entry body replaces `raise
ScratchpadWriteNotAvailableError()` with
`await mcp_client.invoke('kora__write_agent_scratchpad', {...})`,
returning the substrate-assigned scratchpad_entry_id. ~40 LOC across
scratchpad.py + provider.py + tools/iso_node.py + tests + docs.

Production-test posture per IsoKron PM #27:
* K-8's kora__write_agent_scratchpad handler is a
  notImplementedHandler stub on substrate main; dispatch tier (task
  NousResearch#395) un-stubs + bridges Layer-A wsk_* auth → Layer-B actor_kind.
* KR-8 code shape is sound; mock tests verify correctness.
* Production deploys wait on dispatch tier landing — identical
  posture to KR-7 / KR-7b / KR-7a.

K-DG note: K-8's internal flow emits the substrate-canonical chain
literal `kronicle.agent_scratchpad.created` (NOT
`kora.scratchpad.entry.created` as some prior specs may suggest).
The runtime doesn't pass an event_type — the substrate emits
internally as part of the SECDEF flow. Verify-at-first-live-emit:
confirm event_log.actor_id resolves to the 0076-seeded canonical
Kora actor.

scratchpad.py:
* write_scratchpad_entry body: live call to mcp_client.invoke. K-8
  contract output: {'scratchpad_entry_id': '<uuid>',
  'approved_event_id': '<uuid>'} — projected to a plain str
  scratchpad_entry_id return.
* BLAKE3 content_hash computed via existing
  compute_scratchpad_content_hash() helper; passed to MCP tool.
* Defensive: None mcp_client raises ValueError; unexpected response
  shape raises RuntimeError (same pattern as KR-7's emit_kora_event).
* mcp_client param now required (was Optional with deferred-error
  default). Forward signature stays caller-stable from KR-2 ST3.
* ScratchpadWriteNotAvailableError class kept exported tagged
  [kora.isokron.deprecated] for one release. Original deferred-tag
  preserved inside the message for grep stability.

provider.py:_attempt_scratchpad_write:
* Stop catching ScratchpadWriteNotAvailableError (no longer raised).
* Fetch IsoKronMCPClient via self._connection.get_mcp_client();
  failure logs ERROR [kora.scratchpad.write.failed] + returns.
* Catch IsoKronMCPInvocationError + defensive Exception at the
  lifecycle boundary — log at ERROR so failures are operator-visible
  (same posture as KR-7's chain emit). Sessions don't crash; writes
  drop with a loud log line.
* Successful writes log INFO [kora.scratchpad.write] with the
  substrate-assigned entry_id and invalidate the per-workspace
  own-scratchpad cache so the next read re-fetches.

tools/iso_node.py:
* _handle_iso_node_create + _handle_iso_node_supersede: dropped
  the deferred-envelope return path. On success return
  {"ok": True, "entry_id": <substrate-uuid>}. On
  IsoKronMCPInvocationError return
  {"ok": False, "substrate_error": True, "tool_name": ...,
   "message": ...} — structured signal for the model that maps to
  substrate-level failures distinct from validation errors.
* iso_node_supersede also passes the new entry_id into the
  kora.node.superseded chain event payload so audit consumers can
  trace the supersession chain.
* Removed unused ScratchpadWriteNotAvailableError import.

Tests (5 new + 9 updated):
* test_scratchpad.py:
  - Replaced test_write_scratchpad_entry_raises_deferred_write_error
    with 4 MCP-call-path tests (happy/error/None-client/bad-shape)
    + 1 deprecation-runway test.
  - _FakeProviderConnection now exposes get_mcp_client() returning a
    _FakeMcpClient; sync_turn/on_memory_write tests flipped from
    deferred-WARNING assertions to success-INFO + entry_id checks.
  - New test_sync_turn_substrate_error_logs_at_error_session_continues
    verifies the IsoKronMCPInvocationError → ERROR-log path.

* test_iso_node_tools.py:
  - _FakeProviderConnection gains get_mcp_client(); new _FakeMcpClient
    routes by tool_name (kora__write_agent_scratchpad → spe-mock-N;
    kora__append_event → evt-mock-N; kora__read_kora_capability_row
    → minimal matrix).
  - test_iso_node_create_returns_deferred_payload →
    test_iso_node_create_returns_ok_envelope_with_substrate_entry_id;
    new test_iso_node_create_substrate_error_surfaces_structured_envelope.
  - test_iso_node_supersede_inherits_node_kind_from_original flipped
    to assert success + verify the inherited node_kind in the new
    content_inline header + verify the kora.node.superseded emit
    payload carries the new entry_id.

* test_tool_finalize.py: round-trip tests flipped from deferred to
  success-envelope assertions. _FakeProviderConnection gains
  get_mcp_client().

* test_provider_end_to_end.py: _FakeMcpClient extended to handle
  kora__write_agent_scratchpad (returns spe-mock-N). E2E assertions
  flipped from 3 deferred-WARNINGs to 3 [kora.scratchpad.write] INFO
  logs + 3 scratchpad_write_calls on the fake + 2 append_event_calls.
  Assertion narrowed to NOT count the cap-matrix fetch (KR-7b adds
  that; this PR stays narrow per parallel-mergeability).

BUILD_DEVIATIONS:
* D-kr2-st3-no-scratchpad-write-mcp-tool moved Open → Closed with
  Rule-5 spec-quote, KR-8's call-site refactor inventory, production-
  test posture, deprecation-runway note, verify-at-first-live-emit
  step.
* 1 deferral remains open (D-kr3-st2 — KR-9 dispatchable).

README "Operator pitfalls":
* Deferred-surface table trimmed 2 → 1 row (D-kr3-st2 only).
* D-kr2-st3 moves to Recently-closed list.
* Individual "Scratchpad writes are currently deferred" pitfall
  rewritten as "Scratchpad writes route via kora__write_agent_
  scratchpad (KR-8)" with success/failure log paths + operator
  grep pointer.

Local gates:
* ty check — 7,337 diagnostics, zero-delta vs KR-7 baseline.
* pytest tests/plugins/memory/ — 357/357 passing.
* Full suite via xdist (-n auto): 24,654 / 168 failed / 129 skipped.
  Δ vs KR-7b (24,652/175/129): +2 passed, -7 failed. Same
  tests/tools/* + tests/tui_gateway/* xdist isolation noise; none
  touch isokron.

1 deferral still open pending CC#1 substrate:
* D-kr3-st2-no-relationlink-write-mcp-tool ← KR-9 (K-10 merged; PM dispatched)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rafe-walker rafe-walker reopened this May 20, 2026
@rafe-walker rafe-walker merged commit 8a2534d into main May 20, 2026
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