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

feat(KR-2 ST3): scratchpad reads + deferred-write surface#7

Merged
rafe-walker merged 1 commit into
mainfrom
feat/kora-KR2-scratchpad
May 20, 2026
Merged

feat(KR-2 ST3): scratchpad reads + deferred-write surface#7
rafe-walker merged 1 commit into
mainfrom
feat/kora-KR2-scratchpad

Conversation

@rafe-walker

Copy link
Copy Markdown
Owner

Summary

Ships KR-2 ST3: scratchpad reads against kronicle.agent_scratchpad_entries (foundation/0135) + deferred-write surface. Read paths match the ST2 RLS-GUC-in-transaction pattern; the write path is deferred because the Sea MCP server (substrate main a3e77f67) does not yet expose kora__write_agent_scratchpad — spec § ST3 explicitly authorizes BUILD_DEVIATIONS for this case and forbids direct INSERT (would skip cap_write_agent_scratchpad authorization + approved_event_id chain event + visibility_scope validation).

Rule-3 STOP-gate findings during recon

  • Schema reality: kronicle.agent_scratchpad_entries uses workspace_id TEXT REFERENCES public.workspaces(id) — same shape as the ST2 reads. The PM dispatch's "tenant_id UUID keying / JOIN hivex_foundation.tenant" was a memory-only assertion that doesn't match foundation/0135 on current main. Verified by reading the migration verbatim. Standing memory note ("Never ask PM for substrate state — grep yourself") applied; spec SQL was correct.
  • Missing MCP tool: grepped packages/sea-mcp-server/src/tools/ on substrate main — only kora__propose_convention, kora__read_escalation_queue, kora__propose_policy_change exist. Spec § ST3 § 3 pre-authorizes this exact case: "If the tool doesn't exist substrate-side yet, BUILD_DEVIATIONS + queue for substrate-team via PM coordination. Do NOT bypass with direct INSERT." → implemented as deferred-write surface.

What landed

File Change
plugins/memory/isokron/scratchpad.py newScratchpadEntry, ScratchpadKind enum (7 values), VisibilityScope enum (2 values), ScratchpadWriteNotAvailableError, compute_scratchpad_content_hash (BLAKE3 hex), canonical SQL constants, async read_own_scratchpad, async read_cross_agent_scratchpad, deferred write_scratchpad_entry.
plugins/memory/isokron/provider.py scratchpad TTL caches (own + cross-agent, separate so writes invalidate only Kora's view); _looks_like_kora_action heuristic + _summarize_for_scratchpad truncation; sync_turn real impl (heuristic → attempt write → catch defer → log); on_memory_write real impl (mirrors built-in memory writes); _attempt_scratchpad_write shared try/catch/cache-invalidate; provider-level read_own_scratchpad / read_cross_agent_scratchpad sync wrappers via connection.submit_and_wait.
tests/plugins/memory/test_scratchpad.py new — 26 tests (see test plan below).
tests/plugins/memory/test_isokron_provider_skeleton.py parametrize trimmed: sync_turn and on_memory_write removed (now implemented). Remaining stubs target ST4 / KR-3.
plugins/memory/isokron/README.md "Operator pitfalls" gains two entries — deferred-write notice (grep for D-kr2-st3-no-scratchpad-write-mcp-tool in logs) and scratchpad warn-on-mismatch vs fail-closed Role Charter. Sub-task table updated.
BUILD_DEVIATIONS.md new entry D-kr2-st3-no-scratchpad-write-mcp-tool under Open: closure condition is the Sea MCP write tool shipping; signature is forward-stable so only write_scratchpad_entry's body changes when the tool lands.

Read SQL (verbatim, mirrors spec § ST3 + foundation/0135 verified)

-- read_own_scratchpad
SELECT s.scratchpad_entry_id::text, ar.actor_kind, ar.display_name AS actor_label,
       s.content_inline, s.content_uri, s.content_hash,
       s.visibility_scope::text, s.scratchpad_kind::text, s.created_at
FROM kronicle.agent_scratchpad_entries s
JOIN public.actor_registry ar ON ar.actor_id = s.actor_id
WHERE s.workspace_id = $1 AND ar.actor_kind = 'kora' AND s.status = 'active'
ORDER BY s.created_at DESC LIMIT $2
-- read_cross_agent_scratchpad
WHERE s.workspace_id = $1
  AND ar.actor_kind != 'kora'
  AND s.visibility_scope = 'cross_agent_dereferenceable'
  AND s.status = 'active'

Both bracketed by BEGIN; SELECT set_config('app.current_workspace_id', $1, true); ... COMMIT; to satisfy the RLS policy keyed off the GUC (same pattern as ST2's policy_registry; without it the table silently returns zero rows).

sync_turn heuristic

Per spec § ST3 § 4: "any tool call that's a Kora-action (cap_*) gets a scratchpad entry summarizing what happened". Implemented as a regex \bcap_[a-z0-9_]+\b over assistant_content. Word-boundary anchored (no false positives on substrings like supercap_sea_create). Match → attempt write → on ScratchpadWriteNotAvailableError log a one-line WARNING + continue (turn stays alive).

Test plan

26 new tests, all passing:

Read paths:

  • test_read_own_scratchpad_returns_typed_entries — actor_kind=kora filter, typed entries
  • test_read_own_scratchpad_sets_rls_guc_before_select — txn.enter → execute → fetch → txn.exit
  • test_read_own_scratchpad_binds_workspace_id_and_limit — SQL params + default limit 100
  • test_read_cross_agent_scratchpad_returns_typed_entries — multiple actor_kinds (critic+oracle), scope filtered
  • test_read_cross_agent_scratchpad_sql_excludes_kora_and_filters_scope — SQL WHERE clause inspection

BLAKE3 integrity:

  • test_blake3_hash_mismatch_logs_warning_and_does_not_raise — warn-only, not fail-closed
  • test_compute_scratchpad_content_hash_matches_blake3_hexdigest — bytes-faithful to substrate-side storage
  • test_content_uri_entries_skip_integrity_check — no false-positive drift on object-store rows

Deferred write:

  • test_write_scratchpad_entry_raises_deferred_write_error — exact tag + BUILD_DEVIATIONS ID in message

Heuristic + summarizer:

  • test_looks_like_kora_action_heuristic (parametrized x8 — positive/negative cases incl. digits + word-bounds)
  • test_kora_action_pattern_is_word_bounded — no supercap_* false positive
  • test_summarize_for_scratchpad_returns_short_content_unchanged
  • test_summarize_for_scratchpad_truncates_long_content_with_marker — 2KB cap + clear "[truncated]" suffix

Provider-level write paths:

  • test_sync_turn_no_kora_action_is_noop — no cap_* → zero writes attempted
  • test_sync_turn_kora_action_attempts_write_and_catches_deferred_error — write attempted, error caught, WARNING logged with deviation ID
  • test_sync_turn_invalidates_own_cache_on_attempted_write — pre-invalidates so future MCP-tool swap surfaces fresh data
  • test_on_memory_write_mirrors_to_scratchpad_with_action_and_target[memory.{action} → {target}] summary prefix
  • test_sync_turn_skips_silently_without_workspace_id — debug-log, no write attempt, turn alive

Provider-level reads:

  • test_provider_read_own_scratchpad_caches_results — single pool.fetch across two reads (cache hit)

Plus parametrized expansions on _looks_like_kora_action for a total of 26.

Gates

  • ty check7,337 diagnostics, same as KR-2 ST2 baseline (Δ 0). Zero new diagnostics introduced.
  • pytest tests/plugins/memory/test_{scratchpad,reads,capability_matrix_parity,isokron_provider_skeleton}.py68/68 passing (26 new ST3 + 18 ST2 read tests + 2 parity + 22 ST1 skeleton after parametrize trim).
  • Full suite via xdist (-n auto): 24,551 passed / 143 failed / 129 skipped. Δ vs ST2 merge baseline (24,443 / 227 / 129): +108 passed, −84 failures. Same tests/tools/* + tests/tui_gateway/* xdist isolation noise as ST2; none touch plugins/memory/isokron/.

Rule-6 / BUILD_DEVIATIONS / Open asks

  • D-kr2-st3-no-scratchpad-write-mcp-tool opened in BUILD_DEVIATIONS.md. PM coordinates substrate-team / files the substrate dispatch for a kora__write_agent_scratchpad Sea MCP tool. When it lands, scratchpad.write_scratchpad_entry's body swaps from raising to mcp_client.invoke('kora__write_agent_scratchpad', ...) — signature is forward-stable, no provider refactor needed.
  • Remaining stubs in IsoKronMemoryProvider (handle_tool_call, on_session_end, on_session_switch, on_pre_compress, on_delegation, save_config) still raise NotImplementedError with [kora.isokron.todo] tags pointing to ST4 / KR-3.
  • ST3 spec gotchas confirmed by grepping current substrate main: workspace_id TEXT (not tenant_id UUID); actor_kind not a column on the scratchpad table (JOIN actor_registry); content_hash BLAKE3 hex; visibility_scope enum (not bool); actor_label derived from actor_registry.display_name.

Standing by for ST4 dispatch and the substrate-team coordination on the scratchpad-write MCP tool.

🤖 Generated with Claude Code

Read paths against kronicle.agent_scratchpad_entries (foundation/0135),
mirroring the ST2 RLS-GUC-in-transaction pattern. Write path is deferred
because the substrate-side Sea MCP tool kora__write_agent_scratchpad
doesn't exist yet (substrate main a3e77f6); spec § ST3 explicitly
authorizes the BUILD_DEVIATIONS fallback for this case and forbids
direct INSERT (would skip cap_write_agent_scratchpad authorization,
approved_event_id chain event, and visibility_scope validation).

Read paths (plugins/memory/isokron/scratchpad.py):
* read_own_scratchpad — JOIN actor_registry, filter actor_kind='kora'
  AND status='active'. Default limit 100 per spec § ST3.
* read_cross_agent_scratchpad — JOIN actor_registry, filter
  actor_kind != 'kora' AND visibility_scope='cross_agent_dereferenceable'
  AND status='active'.
* Both: RLS GUC set via set_config(...,true) inside a transaction
  before the SELECT, identical to ST2's policy_registry pattern.
* BLAKE3 content_hash mismatch logs WARNING but does NOT raise
  (per spec § ST3: scratchpad is mutable working memory; refusing
  drifted entries would block sessions on transient state). Unlike
  ST2's Role Charter which is fail-closed.

Write surface (deferred):
* write_scratchpad_entry raises ScratchpadWriteNotAvailableError with
  message tagged [kora.isokron.todo] + D-kr2-st3-no-scratchpad-write-
  mcp-tool. Signature matches the future MCP-backed impl so caller
  code doesn't refactor when the tool lands.
* Schema-faithful enums: ScratchpadKind (7 values), VisibilityScope (2).
* compute_scratchpad_content_hash helper (BLAKE3 hex; matches
  foundation/0135's stored format).

Provider wiring (plugins/memory/isokron/provider.py):
* sync_turn — _looks_like_kora_action(assistant_content) heuristic
  (regex match on \bcap_[a-z0-9_]+\b); on match, _attempt_scratchpad_
  write submits the deferred write via the dedicated IO loop, catches
  ScratchpadWriteNotAvailableError, logs one-line WARNING — session
  stays alive. Cache pre-invalidation runs on attempt (success or
  defer) so a future MCP-tool swap doesn't expose stale data.
* on_memory_write — mirrors built-in memory writes as a reasoning_trail
  scratchpad entry with action+target in the summary; same catch-and-
  continue pattern.
* read_own_scratchpad + read_cross_agent_scratchpad — sync wrappers
  via connection.submit_and_wait; cached 60s per workspace.
* prefetch / queue_prefetch unchanged (no-op since ST2).

Tests (26 new in tests/plugins/memory/test_scratchpad.py):
* Read shapes (own + cross-agent), RLS GUC ordering, limit binding,
  cross-agent SQL filter check.
* BLAKE3 mismatch warns + does NOT raise; content_uri rows skip
  the inline integrity check.
* write_scratchpad_entry raises with the right tag + deviation ID.
* _looks_like_kora_action parametrize across positive/negative cases
  + word-boundary regex anchor.
* sync_turn no-op without kora_action; with kora_action attempts
  write + catches defer + logs; cache invalidates on attempt;
  workspace_id missing → debug-log + no attempt + turn alive.
* on_memory_write embeds action+target in summary; defers gracefully.
* Provider-level read_own_scratchpad cache hit-after-fetch (single
  pool.fetch call across two reads).

ST1 skeleton parametrize trimmed: sync_turn and on_memory_write
removed (now implemented). Remaining stubs target ST4 / KR-3.

Local gates:
* ty check — 7,337 diagnostics (same as KR-2 ST2 baseline; zero-delta).
* pytest tests/plugins/memory/test_{scratchpad,reads,capability_matrix_
  parity,isokron_provider_skeleton}.py — 68/68 passing (26 new ST3 +
  18 ST2 + 2 parity + 22 ST1 skeleton after trim).
* Full suite via xdist (-n auto): 24,551 passed / 143 failed / 129
  skipped vs ST2-merge baseline 24,443/227/129 → +108 passed, −84
  failures. None of the failures touch plugins/memory/isokron/; same
  tests/tools/* + tests/tui_gateway/* xdist isolation noise as ST2.

Rule-6:
* BUILD_DEVIATIONS.md gains D-kr2-st3-no-scratchpad-write-mcp-tool
  under Open with the grep-snapshot of available kora__* tools in
  substrate main and exact closure condition.
* README "Operator pitfalls" adds two entries: deferred-write
  notice (grep for the deviation ID in logs) + scratchpad warn-
  on-mismatch semantics (vs fail-closed Role Charter).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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