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

feat(kora): KR-CHEAP-TRIVIAL-DM-SHORTCIRCUIT — regex + snapshot-interp phrasebook (R3-4 #2)#160

Merged
rafe-walker merged 1 commit into
feature/phase2-upgradesfrom
feat/kora-KR-CHEAP-TRIVIAL-DM-SHORTCIRCUIT
May 24, 2026
Merged

feat(kora): KR-CHEAP-TRIVIAL-DM-SHORTCIRCUIT — regex + snapshot-interp phrasebook (R3-4 #2)#160
rafe-walker merged 1 commit into
feature/phase2-upgradesfrom
feat/kora-KR-CHEAP-TRIVIAL-DM-SHORTCIRCUIT

Conversation

@rafe-walker

Copy link
Copy Markdown
Owner

Summary

Per Council R3 Lock R3-4 build-list #2. Pre-filter on Slack DM handler catches routine status queries ("what's my burn?", "any alerts?") and answers from the pre-warmed snapshot (PR #157) at zero LLM cost. Pairs with prompt caching (#158) — caching halves LLM-bound work; short-circuit eliminates the LLM call entirely for matched patterns.

Operator-review gate: bundled default phrasebook below for Joshua's approval before merge.

Bundled default phrasebook (operator-review focus)

# Default Kora DM phrasebook (KR-CHEAP-TRIVIAL-DM-SHORTCIRCUIT).
# Patterns are case-insensitive Python regex (re.IGNORECASE).
# Reply templates support {snapshot.path.to.field} interpolation.
# Missing fields OR a literal "unknown" value triggers fall-through
# to the reasoning engine (we never send a half-filled response).
# Tone: Kora — direct, brief, conversational.

entries:
  - pattern: "^(hey|hi|hello|yo|sup)[\\s!.]*$"
    category: greeting
    description: Bare greeting with no other content
    reply_template: "Hey. What's up?"

  - pattern: "^(thanks|thx|ty|tysm|thank you|appreciate it|appreciate that)[\\s.!]*$"
    category: thanks
    description: Operator acknowledgment, no follow-up needed
    reply_template: "Anytime."

  - pattern: "^(ack|acknowledged|got it|k|ok|okay|kk|sounds good|cool)[\\s.!]*$"
    category: ack
    description: Operator ack, no reply substance needed
    reply_template: "👍"

  - pattern: "^(burn|cost|spend|how ?much|what'?s? my burn|what'?s? the burn|whats the spend)[\\s.?!]*$"
    category: burn_query
    description: Single-clause cost / burn inquiry
    reply_template: "{snapshot.cost_ladder.monthly_budget_pct_used}% of monthly budget used. Tier: {snapshot.cost_ladder.current_tier}."

  - pattern: "^(status|state|how ?are (you|things)|how'?s? it going|whats up|what'?s? up)[\\s.?!]*$"
    category: status_query
    description: General "are you alive / how are things"
    reply_template: "Operational state: {snapshot.operational_state.primary}. {snapshot.alerts.active_count} active alert(s)."

  - pattern: "^(any alerts|alerts|what'?s? alerting|what'?s? firing|whats firing)[\\s.?!]*$"
    category: alert_query
    description: Direct alert-count inquiry
    reply_template: "{snapshot.alerts.active_count} active: {snapshot.alerts.by_severity.critical} crit / {snapshot.alerts.by_severity.warning} warn / {snapshot.alerts.by_severity.info} info."

  - pattern: "^(health|service health|services?|service status)[\\s.?!]*$"
    category: health_query
    description: External-service health rollup
    reply_template: "Supabase: {snapshot.service_health.supabase} · Fly: {snapshot.service_health.fly} · Vercel: {snapshot.service_health.vercel} · Sentry: {snapshot.service_health.sentry} · Doppler: {snapshot.service_health.doppler}."

  - pattern: "^(are you paused|paused\\?|you paused\\??|are you stopped|stopped\\??)[\\s.?!]*$"
    category: pause_check
    description: Quick "are you running" probe
    reply_template: "Primary state: {snapshot.operational_state.primary}. Paused: {snapshot.operational_state.paused}."

Operator-editable post-merge: drop a replacement YAML at `${KORA_HOME}/phrasebook/slack_dm.yml`. Override REPLACES bundled (not merged) — copy this doc as a starting point. Daemon restart picks up changes; broken override falls back to bundled with a WARN log.

Coverage estimate: 30-40% of Joshua's daily DMs. Highest-volume categories I expect to catch: `ack` (most replies), `thanks`, `status_query`, `alert_query`, `burn_query`. Free-form questions ("walk me through X", "why is Y broken") still hit the engine as intended.

Telemetry hook for CC#1's KR-CHEAP-COST-TELEMETRY

Every short-circuit hit writes `model_used="short_circuit"` into the outbound JSONL plus two new keys: `short_circuit_category` + `short_circuit_pattern`. CC#1's cost-telemetry bucket will:

  • GROUP BY `model_used` to separate zero-cost-route hits from LLM-billed reasoning
  • BREAK DOWN by `short_circuit_category` so we can see "burn_query: 12 hits, status_query: 8, ack: 24" / 24h
  • Literal `"short_circuit"` string is the contract — `test_short_circuit_model_used_is_join_key` locks it

Implementation

Site Change
`kora_cli/short_circuit/init.py` New package; re-exports public surface
`kora_cli/short_circuit/dm_phrasebook.py` Phrasebook load + match + render + try_short_circuit
`kora_cli/short_circuit/default_slack_dm_phrasebook.yml` Bundled 8-entry default (see above)
`kora_cli/handlers/slack_dm_handler.py` `init` caches phrasebook; `handle_event` calls `_try_short_circuit` BEFORE engine resolution; engine path extracted into `_resolve_via_engine` helper (no behavior change); `_short_circuit_reasoning_meta` builds sentinel meta; `_append_outbound_log_entry` accepts the two new kwargs with None-omit semantic
`pyproject.toml` `tool.setuptools.package-data` for kora_cli gains `short_circuit/*.yml` — bundled YAML ships with the wheel; `importlib.resources` loads it

Fall-through semantics (the safety contract)

A phrasebook match returns a real reply ONLY when:

  1. The regex matches the inbound text
  2. `read_snapshot()` returns a non-None dict (snapshot file exists + fresh per PR feat(kora): KR-CHEAP-PRE-WARMED-SNAPSHOT — daemon state every 5 min at zero LLM cost #157's 10-min threshold)
  3. Every `{snapshot.X.Y}` placeholder resolves to a non-None, non-"unknown" value

Any failure on (2) or (3) → `_try_short_circuit` returns None → handler falls through to engine resolution unchanged. We never send "your burn is unknown%" to Joshua.

The short-circuit also catches every exception internally → handler stays alive even if the phrasebook YAML is destroyed mid-flight (engine path drives the reply).

Test plan

  • 25 phrasebook unit tests pass (load / match / render / try_short_circuit)
  • 15 handler integration tests pass (engine bypass on match; fall-through on no-match / no-snapshot / degraded; broken-phrasebook safety; telemetry shape; cost-ladder no-op for SC hits)
  • Full handlers + listeners + reasoning + short_circuit regression: 532/532 serial green
  • Full repo xdist: 9319 passed; 43 failed identical to baseline (test_anthropic_adapter / test_backup / test_config / test_gateway_* / test_web_server* / test_kanban_db / test_list_picker_providers / test_model_switch_* / test_startup_plugin_gating). Zero failures in short_circuit, handlers, or reasoning.
  • Bundled YAML loads via `importlib.resources` from both source tree + installed wheel
  • Every bundled-default entry renders cleanly against the canonical fresh snapshot
  • Every bundled-default entry falls through cleanly against a fully-degraded snapshot
  • `model_used="short_circuit"` join-key contract locked in test

🤖 Generated with Claude Code

…p phrasebook (R3-4 #2)

Per Council R3 Lock R3-4 build-list #2. Pre-filter on Slack DM
handler that catches routine status queries ("what's my burn?",
"any alerts?") and answers from the pre-warmed snapshot
(PR #157) at zero LLM cost. Pairs with prompt caching (#158) —
caching halves LLM-bound work; short-circuit eliminates the
LLM call entirely for matched patterns. Estimated coverage:
30-40% of operator traffic.

# New package

  kora_cli/short_circuit/
    __init__.py
    dm_phrasebook.py
    default_slack_dm_phrasebook.yml

Public surface (re-exported from __init__):
  - PhrasebookEntry / ShortCircuitMatch (frozen dataclasses)
  - load_phrasebook(path=None) — bundled default unless override
    at ${KORA_HOME}/phrasebook/slack_dm.yml exists; malformed
    override → WARN + fall back to bundled
  - match_message(text, phrasebook) — first-match-wins, case-
    insensitive, strips whitespace
  - render_reply(entry, snapshot) — walks {snapshot.dotted.path}
    placeholders; returns None on snapshot None / missing path /
    None value / literal "unknown" sentinel (PR #157 degraded
    marker) so we never ship a half-filled response
  - try_short_circuit(text, phrasebook, snapshot) — convenience
    wrapper: match → render → ShortCircuitMatch or None

# Bundled default phrasebook

8 entries: greeting, thanks, ack, burn_query, status_query,
alert_query, health_query, pause_check. Tone target: Kora
(direct, brief, conversational) not chatbot phrasing.
Operator-editable post-merge via
${KORA_HOME}/phrasebook/slack_dm.yml override (REPLACES
bundled — copy the doc as starting point).

# Handler wiring (kora_cli/handlers/slack_dm_handler.py)

handle_event refactored: short-circuit attempt runs BEFORE
engine resolution; on hit, sentinel reasoning_meta is built
(model_used="short_circuit", input_tokens=0, output_tokens=0,
cache_*_tokens=0, short_circuit_category, short_circuit_
pattern). On miss → falls through to the original engine
resolution path (extracted into _resolve_via_engine helper for
clarity; existing engine behavior unchanged byte-for-byte).

_append_outbound_log_entry extended with two new optional
kwargs (short_circuit_category, short_circuit_pattern) — same
None-omit semantic as the existing cache_* fields. Flowing
through naturally via **reasoning_meta splat.

Phrasebook is cached on the handler instance after first call.
Any exception during _try_short_circuit (broken phrasebook,
import failure, etc.) is logged + swallowed; handler falls
through to engine as if no phrasebook existed. DM handling
NEVER breaks because of a bad phrasebook.

# Cost-ladder no-op for short-circuit hits

Short-circuit's sentinel reasoning_meta sets all four token
buckets to 0. The existing _record_inference_to_cost_ladder
guard (added in #158) bails when every bucket is 0 → no
spurious cost-ladder writes. Verified by
test_short_circuit_does_not_call_record_inference.

# Telemetry contract for CC#1's KR-CHEAP-COST-TELEMETRY

Literal "short_circuit" string in model_used is the join key.
Don't rename without coordinating. category field lets the
reasoning panel break down which query shapes are getting
short-circuited (e.g. "burn_query: 12 hits / 24h").

# Package data wiring

tool.setuptools.package-data → kora_cli gained
"short_circuit/*.yml" so the bundled YAML ships with the
wheel. importlib.resources loads it from both source tree +
installed wheel; source-tree Path fallback for very-early-boot
edge cases.

# Tests

tests/kora_cli/short_circuit/test_dm_phrasebook.py — 25 tests:
  - load_phrasebook: bundled, explicit path, operator override,
    malformed override fallback, invalid regex fallback
  - match_message: case-insensitive, whitespace strip, first-
    match-wins, empty/whitespace/non-string returns None,
    no-match returns None
  - render_reply: all-fields-present, snapshot None, missing
    path, "unknown" sentinel, None value, nested dict path,
    literal punctuation preservation
  - try_short_circuit: match+render integration, no match,
    match-but-fall-through (snapshot None / degraded field)
  - Bundled default sanity: all patterns compile; every entry
    with placeholders renders against canonical fresh snapshot;
    fully-degraded snapshot falls through for every placeholder
    entry (bare-text entries still render)

tests/kora_cli/handlers/test_slack_dm_short_circuit.py — 15
tests:
  - Match bypasses engine; post_dm receives rendered template
  - Outbound JSONL carries telemetry keys (model_used=
    short_circuit, short_circuit_category, short_circuit_
    pattern, zero token buckets, tools_used=[])
  - All 7 categories route correctly (parametrized)
  - No match → engine called as before; no JSONL contamination
  - Match + no snapshot → fall through to engine
  - Match + degraded field → fall through to engine
  - Broken phrasebook → fall through, handler stays alive
  - model_used="short_circuit" is the CC#1 join key (literal
    contract test)
  - Short-circuit hits don't burn $200/mo pool (record_inference
    never called)

# Regression

532/532 short_circuit + handlers + listeners + reasoning green
serially. Full repo xdist: 9319 passed, 43 failed identical to
baseline (test_anthropic_adapter / test_backup / test_config /
test_gateway_* / test_web_server* / test_kanban_db /
test_list_picker_providers / test_model_switch_* /
test_startup_plugin_gating / test_web_server_cron_profiles).
Zero failures in short_circuit, handlers, or reasoning.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rafe-walker rafe-walker merged commit d389a30 into feature/phase2-upgrades May 24, 2026
@rafe-walker rafe-walker deleted the feat/kora-KR-CHEAP-TRIVIAL-DM-SHORTCIRCUIT branch May 24, 2026 01:51
rafe-walker pushed a commit that referenced this pull request May 24, 2026
…tion + revert

Build-list follow-on to PR #167 (phrasebook read-only viewer).
Adds the operator-edit story so the phrasebook can be modified
from the cockpit instead of YAML by hand. Also sets the UX
foundation that the eventual promotion-review panel will reuse
(KR-PROMOTE-PHRASEBOOK proposals are pending edits to this same
surface).

Backend (kora_cli/short_circuit/phrasebook_editor.py — new)
==========================================================

Module hosts everything the read-only viewer didn't need:

  * validate_entries(entries) → [EntryValidationError, ...]
    8 checks (in order; later skipped for an entry that failed
    earlier to avoid noise): payload is list, length cap (200
    entries), required-fields-non-empty, length caps on each
    field, regex compiles with re.IGNORECASE, catastrophic-
    backtracking guard (catches (a+)+, (.*)*, etc), snapshot
    placeholder paths resolve to known scalars, no duplicate
    (pattern, category) tuples.

  * SNAPSHOT_SCALAR_PATHS — static frozen-set of known snapshot
    v4 scalar paths the operator can reference in
    {snapshot.X.Y} placeholders. Drift-guarded by
    test_static_schema_matches_snapshot_collectors which greps
    each leaf key against the snapshot collector source. Only
    scalars (no dynamic-key dicts like listeners.X or
    cost_telemetry.X — those str() to Python repr and would
    render garbage in operator-facing DMs).

  * write_backup_for / rotate_backups / list_backups —
    timestamped backups under ${KORA_HOME}/phrasebook/backups/.
    KORA_PHRASEBOOK_BACKUP_COUNT env var tunes rotation
    (default 10; clamped to [1, 1000]). Filenames sort
    chronologically as plain strings (ISO-Z format).

  * write_phrasebook(entries) — atomic write via
    utils.atomic_replace (same pattern as snapshot writer).
    Deterministic field order for readable YAML diffs.

  * revert_phrasebook(filename=...) — revert to a specific
    backup OR (no filename) the most-recent OR (no backups)
    remove the override entirely. Path-traversal defense
    rejects any filename containing /, \, .., or not matching
    the slack_dm.*.yml shape.

Backend (kora_cli/web_server.py — 3 new endpoints)
==================================================

  PUT  /api/phrasebook/slack_dm           — validate + back up +
                                            atomic-write + audit
  POST /api/phrasebook/slack_dm/revert    — revert to backup
  GET  /api/phrasebook/slack_dm/backups   — list backups

PUT contract:
  * Validation fails → 422 with structured per-entry errors;
    NO write; previous override preserved.
  * Validation passes → backup current (if exists) → atomic
    write → rotate backups → audit row → 200 with echoed
    entries + backup_filename + rotated_count.
  * Write itself fails → 500; backup preserved so operator
    can recover.

Audit seam (new): phrasebook.updated
====================================

Extended SeamName Literal in kora_cli/audit/jsonl_sink.py.
Each successful PUT (or revert) emits one entry with:
  * actor          — "operator" (cockpit-driven; future
                      "kora_proposal_approved" from the
                      promotion-loop bucket reuses this shape)
  * action         — "put" | "revert"
  * entry_count_before / entry_count_after
  * backup_filename (when applicable)
  * rotated_backup_count (when applicable)
  * reverted_to (when action=revert)

Drives the future KR-PROMOTION-REVIEW-PANEL via the existing
audit-panel infrastructure (KR-AUDIT-PANEL-ENDPOINTS PR #155).

Frontend
========

  * web/src/pages/PhrasebookEditor.tsx (new) — hosts the
    editor sub-components so PhrasebookPage stays readable:
    EntryEditorRow (4 inline editable fields + per-field
    validation errors), EditModeControls (Save / Cancel / Add),
    BackupsDialog (modal with newest-first list + per-row
    revert + confirm), ClientSidePreview (mirrors
    dm_phrasebook.match + render_reply in TS so operator can
    preview in-progress edits without saving).

  * api wrappers + types: putSlackDmPhrasebook /
    revertSlackDmPhrasebook / getSlackDmPhrasebookBackups +
    PhrasebookEntryWrite / PhrasebookPutResponse /
    PhrasebookValidationErrorEntry/Body /
    PhrasebookRevertResponse / PhrasebookBackupItem /
    PhrasebookBackupsResponse.

  * PhrasebookPage extended with edit-mode toggle. View-mode
    surface (read-only table + live tester) is unchanged for
    operators who just want to inspect. Edit-mode swaps to
    editor rows + client-side preview + Save/Cancel/Add.
    Backups button in view-mode opens the revert dialog.

  * 422 validation-error parsing: Save handler unmarshals
    fetchJSON's "STATUS: BODY" Error message; on 422 with
    error="validation_failed" the body's per-entry errors are
    routed to the editor for inline rendering.

Tests
=====

  Backend (tests/kora_cli/test_phrasebook_editor.py — 44 tests):

    Validation (14): valid entries, non-list, missing field,
      empty whitespace, all 4 length caps, entries count cap,
      invalid regex, catastrophic-backtracking parametrized
      across 4 pathological patterns, unknown vs known snapshot
      path, duplicate (pattern, category), schema drift guard
      against state_snapshot.py.
    Backups (5): no-override returns None, copies content +
      timestamps filename, rotation keeps N most recent, env
      override + clamping parametrized, list returns newest-
      first with entry_count.
    Write+revert (5): round-trip load, revert by name, revert
      most-recent, revert with no backups removes override,
      path-traversal rejection parametrized across 5 attacks.
    Endpoints (7): PUT valid + audit, PUT invalid + preserve,
      PUT no-existing-override, POST revert + audit, POST
      revert invalid filename → 400, POST revert missing →
      404, GET backups newest-first.
    Audit (1): SeamName Literal includes phrasebook.updated.
    Integration (1): round-trip PUT then revert restores seed.

  FE source-pins (tests/kora_cli/test_phrasebook_editor_fe_pins.py
  — 17 tests):

    api wrappers (3), TS types declared + shape pinned (2),
    editor file exists + exports (1), page wiring (3), edit
    button hidden in edit-mode (1), 422 body parsing branch
    present (1), ClientSidePreview mirrors backend regex flag
    + "unknown" sentinel + null-snapshot fall-through (3),
    SnapshotResponse type covers daemon_health (1).

  All 76 phrasebook tests pass (44 BE editor + 17 FE pins +
  15 existing PR #167 read-side tests).

  tsc -b clean. vite build clean.

Screenshots
===========

  web/docs/phrasebook-editor/edit_mode.png — edit-mode UI with
    client-side preview rendering a $0 reply
  web/docs/phrasebook-editor/validation.png — 422 response with
    4 inline per-field errors (bad regex, unknown snapshot path,
    catastrophic backtracking, missing description) + top-level
    duplicate error
  web/docs/phrasebook-editor/revert.png — backups modal with
    newest-first list, per-row Revert confirm flow, greyed-out
    corrupt backup

Design choices noted (no STOP-ASKs triggered)
=============================================

  * Snapshot field-path validation uses a STATIC allow-list
    (SNAPSHOT_SCALAR_PATHS), NOT a live snapshot walk. Spec §4
    flagged this as a possible STOP-ASK; the static-list
    approach is what the spec offered as the alternative
    ("use SnapshotResponse static schema") and avoids the
    dynamic-snapshot-during-warm-up problem where freshly-
    booted daemons would mark canonical paths as invalid.
    Drift guard test pins each scalar leaf against the
    snapshot collector source.

  * Audit seam SeamName extension follows the
    probe.wake_requested precedent — extend Literal with a
    new value + add the comment explaining the future
    actor extension. No consumer drift.

  * Operator-defined category values: free-form for v1
    (the runtime doesn't reserve any category names yet;
    promotion-loop bucket can add reserved-prefix
    discipline when its UX lands).

Refs
====

  * rafe-walker/kora-docs
    17_cc_bucket_prompts/KR-FE-PHRASEBOOK-EDITOR-AND-CRUD_write_path_with_validation.md
  * PR #160 — phrasebook + dm_phrasebook module (read side)
  * PR #167 — KR-FE-PHRASEBOOK-VIEWER (read-only viewer this extends)
  * PR #170 — snapshot v4 daemon_health (referenced in
    SNAPSHOT_SCALAR_PATHS allow-list)

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