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

feat(kora): KR-FE-PANEL-KIT-AND-MUTATING-ACTIONS-MEGABUCKET — kit + autofix + kora-actions (#180/#183 retrofit)#187

Merged
rafe-walker merged 1 commit into
feature/phase2-upgradesfrom
feat/kora-KR-FE-PANEL-KIT-AND-MUTATING-ACTIONS-MEGABUCKET
May 24, 2026
Merged

feat(kora): KR-FE-PANEL-KIT-AND-MUTATING-ACTIONS-MEGABUCKET — kit + autofix + kora-actions (#180/#183 retrofit)#187
rafe-walker merged 1 commit into
feature/phase2-upgradesfrom
feat/kora-KR-FE-PANEL-KIT-AND-MUTATING-ACTIONS-MEGABUCKET

Conversation

@rafe-walker

Copy link
Copy Markdown
Owner

Summary

Four deliverables in one PR per the new batched-dispatch discipline. CC#2-flagged "3rd-consumer threshold" from PR #183 crossed — kit extraction landed alongside the 3rd consumer (AutofixLogPage) + the 4th consumer (KoraActionsPage, apex operator-trust view).

Deliverable What Status
A Extract AuditPanelKit from #180/#183 duplication ✅ 7 components + types + README + 8 tests
B Retrofit #180 + #183 to use the kit ✅ Visual diff zero; 47/47 existing tests pass
C KR-FE-AUTOFIX-LOG-PANEL (3rd kit consumer) ✅ BE endpoint + page + 21 tests + 3-source drift guard
D KR-FE-KORA-ACTIONS-AGGREGATED-PANEL (apex) ✅ BE endpoint joining 4 seams + page + 22 tests + drift guard

98/98 panel-suite tests pass (51 new + 47 existing). tsc + vite build clean.

Screenshots

A. AuditPanelKit storybook (4 components in isolation)

AuditPanelKit storybook

B. #180 + #183 retrofit — visually unchanged

The kit IS the source-of-truth shape these pages used; retrofitted pages render identically. Existing screenshots in web/docs/email-intent-log-panel/ + web/docs/outbound-email-log-panel/ remain accurate.

C. Probe Autofix Log (3rd kit consumer)

Autofix Log

5 events covering all 3 statuses (3 attempted with state transitions, 1 rejected with envelope-gate detail, 1 execution_failed).

D. Kora Actions — apex "what did Kora do" timeline (4th kit consumer)

Kora Actions

8-event apex timeline with category-color-coded chips, deep-link icons, cross-seam chronological order.

Drift-guard pin summary across all new endpoints

Deliverable Drift guard Sources pinned
C test_status_values_drift_guard (3 values: attempted / rejected / execution_failed) (1) _PROBE_AUTOFIX_STATUS_VALUES in web_server.py (2) STATUS_* constants in tools/probe_autofix.py (3) PROBE_AUTOFIX_STATUS_VALUES in api.ts
D test_action_categories_drift_guard (6 values: email_sent / sea_ticket_created / autofix_attempted / investigation_completed / phrasebook_proposal_approved / other) (1) _KORA_ACTION_CATEGORIES in web_server.py (2) KORA_ACTION_CATEGORIES in api.ts. No emitter constants — these are FE-defined cross-seam categories, not per-tool emit-time values.
D test_seam_literal_includes_all_source_seams Asserts SeamName Literal includes tool.email_to_operator_sent + intent.email_to_sea_ticket + tool.probe_autofix_attempted + phrasebook.updated (the 4 seams the apex panel reads)
Kit test_badge_tone_matches_library BadgeTone.ts pinned to @nous-research/ui Badge's tone union

Plus existing drift guards from #180 + #183 still pass unchanged.

STOP-ASKs (none triggered) — design choices resolved inline

Spec §4 flagged 4 possible STOP-ASKs:

STOP-ASK Deliverable Resolution
Kit extraction surfaces visual deviation A None surfaced — kit copies were verbatim across #180/#183; CategoryDef<K> parameterization handles per-enum differences cleanly.
Aggregated endpoint perf under realistic load D Not currently a concern — each per-seam read_audit_entries is bounded by JSONL file size, merge is in-memory + bounded by limit. Pre-aggregation cache / cursor-pagination deferred until operator reports actual perf issue.
phrasebook.updated actor field shape D Confirmed field exists per PR #177's emitter; filter actor != \"operator\" works cleanly. v1 yields zero (no kora-proposal loop yet) — correct semantic; future KR-PROMOTE-PHRASEBOOK with actor=\"kora_proposal_approved\" auto-populates.
Deep-link routing URL-builder helper C, D Used /sea-tickets?focus=<id> + /probe-autofix-log + /outbound-email-log forward-compat params; no new helper. SeaTicketsPage doesn't currently consume focus; same forward-compat posture as PR #180.

What ships in the kit

Components: Sparkline (plain SVG, parameterizable totalSuffix) · SummaryChips<K> (zero-count categories skipped) · FilterChips<K> (All + per-category buttons) · EmptyFilteredMessage (calm reassurance copy with inline All-reset link)

Types: BadgeTone (pinned to @nous-research/ui) · CategoryDef<K extends string> · DailyCountPoint · FilterValue<K>

Helpers: formatTimestamp / formatRelative / truncate / formatBytes / formatChars / formatDurationMs (new — used by autofix executor-duration rendering)

See web/src/components/AuditPanelKit/README.md for the full props contract + theming notes + "adding a new consumer" runbook.

SECURITY notes

Test plan

  • 98/98 panel-suite tests pass (pytest tests/kora_cli/test_audit_panel_kit.py tests/kora_cli/test_email_intent_panel.py tests/kora_cli/test_outbound_email_panel.py tests/kora_cli/test_autofix_log_panel.py tests/kora_cli/test_kora_actions_panel.py)
  • pnpm tsc -b clean
  • pnpm build clean
  • Manual smoke: open /kora-actions against a daemon with mixed audit-log rows → all categories render with correct chips + summaries + deep-links work → switch filter chips → counts narrow correctly → switch to Investigation completed (zero-count v1) → empty state renders. Then /probe-autofix-log against a daemon with at least one KR-PROBE-AUTOFIX-EXECUTION — Kora attempts the fix (vision completion) #182 audit row → before→after state transition rendered + reason snippet visible.

Follow-on recommendation

When new mutating-action seams ship, add to KoraActionsPage by:

  1. Adding to the BE endpoint's read_audit_entries calls (1 line)
  2. Adding a per-category summary composer
  3. Adding to KORA_ACTION_CATEGORIES enum + drift-guard
  4. Adding CategoryDef + visual to KORA_ACTION_CATEGORIES_DEFS

Highest-value next additions:

Refs

🤖 Generated with Claude Code

…utofix + kora-actions (#180/#183 retrofit)

Four deliverables in one PR per the new batched-dispatch
discipline. CC#2-flagged "3rd-consumer threshold" from PR #183
crossed — kit extraction landed alongside the 3rd consumer
(AutofixLogPage) + the 4th consumer (KoraActionsPage, apex
operator-trust view).

================================================================
Deliverable A — AuditPanelKit extracted
================================================================

New module: web/src/components/AuditPanelKit/

  Sparkline.tsx              — 14-day daily-count bars (plain SVG)
  SummaryChips.tsx           — 24h count band (skip zero-counts)
  FilterChips.tsx            — All + per-category buttons
  EmptyFilteredMessage.tsx   — calm green empty-state w/ All-reset
  BadgeTone.ts               — type pin to @nous-research/ui Badge
  types.ts                   — CategoryDef<K> + DailyCountPoint
  formatters.ts              — formatTimestamp / formatRelative /
                               truncate / formatBytes / formatChars
                               / formatDurationMs (new)
  index.ts                   — public API exports
  README.md                  — props contract + theming notes +
                               "adding a new consumer" runbook

Generic over per-panel enum (CategoryDef<K extends string>) so the
kit's FilterChips type-checks onChange exhaustively per consumer.

================================================================
Deliverable B — #180 + #183 retrofitted to use kit
================================================================

EmailIntentLogPage.tsx + OutboundEmailLogPage.tsx now import
Sparkline / SummaryChips / FilterChips / formatters /
EmptyFilteredMessage / BadgeTone / CategoryDef / FilterValue from
the kit. Local copies removed.

Each panel still defines its own CategoryDef[] array (with
{key, label, tone, Icon} per enum value) — that's panel-specific.

Tests updated:
  * test_filter_chips_iterate_*: now verify CategoryDef[] keys
    include every canonical action/status value + the kit-
    sourced FE constant is still imported (drift-guard greps it)
  * test_sparkline_uses_plain_svg: belt+suspenders — pin both
    page imports from kit AND kit's Sparkline.tsx uses plain SVG

Both pages' existing test suites pass unchanged (24 #180 + 23 #183
= 47/47) after retrofit. Visual diff is zero (kit IS the
source-of-truth shape).

================================================================
Deliverable C — KR-FE-AUTOFIX-LOG-PANEL (3rd kit consumer)
================================================================

Surfaces tool.probe_autofix_attempted audit seam (PR #182).

Backend GET /api/probe-autofix/recent:
  * _PROBE_AUTOFIX_STATUS_VALUES drift-guard allow-list
  * _project_probe_autofix_audit per-status field whitelist
  * SECURITY: before_state / after_state dicts NOT propagated whole
    — only `state` field exposed as before_state_label /
    after_state_label. Test injects hostile fields (region,
    instance_id, fly_api_key) and asserts none leak.
  * reason_from_reasoning truncated to 300 chars (longer than
    other truncations because operator uses this for "why?" triage)
  * rejection_detail JSON-serialized + 200-char truncate
  * Daily-attempted 14d sparkline (status=attempted only)
  * by_status_24h aggregation across 3 known statuses + unknown

Frontend web/src/pages/AutofixLogPage.tsx:
  * 3rd consumer of AuditPanelKit (validates the kit's API
    across 3 different enums)
  * Per-row: probe + action + target + status badge + before→after
    state transition + executor duration + reason_from_reasoning snippet
  * Wrench icon for the page (mirrored in card row + sidebar nav)
  * /probe-autofix-log route + sidebar nav entry

================================================================
Deliverable D — KR-FE-KORA-ACTIONS-AGGREGATED-PANEL (apex)
================================================================

Apex "what did Kora do today" timeline. Joins 4 mutating-action
audit seams into one chronological view. Operator-trust surface
that answers "did Kora do anything worth my attention today?"

Backend GET /api/kora-actions/recent:
  * Joins 4 seams via read_audit_entries — orchestration only,
    no new audit/JSONL plumbing:
      - tool.email_to_operator_sent           → email_sent
      - intent.email_to_sea_ticket (action=created only)
                                              → sea_ticket_created
      - tool.probe_autofix_attempted (status=attempted only)
                                              → autofix_attempted
      - phrasebook.updated (actor != "operator")
                                              → phrasebook_proposal_approved
                                                (v1: yields zero — see below)
      - probe.investigation_completed (NousResearch#406 pending; forward-
        compat: read attempt silently returns [] until seam lands)
                                              → investigation_completed
  * Merged + sorted by emitted_at desc
  * Per-row summary composed per category — privacy-preserved
    whitelisted fields only (no operator PII / SMTP headers etc;
    pinned by test_per_category_summaries_dont_leak_arbitrary_fields)
  * Deep-link target per category (sea-ticket → /sea-tickets?focus=X,
    autofix → /probe-autofix-log, email → /outbound-email-log,
    phrasebook → /phrasebook)
  * _KORA_ACTION_CATEGORIES drift-guarded canonical list (6 values
    including "other" forward-compat catch-all)
  * Daily-actions 14d sparkline (all categories combined)
  * by_category_24h aggregation

Frontend web/src/pages/KoraActionsPage.tsx:
  * 4th consumer of AuditPanelKit
  * Activity-icon header "What Kora Did" (apex framing)
  * Per-row card: timestamp + category chip + status chip + composed
    summary + deep-link "detail" link
  * Calm empty state: "Kora has been quiet today"
  * /kora-actions route + prominent sidebar nav entry

v1 behavior:
  * email_sent + sea_ticket_created + autofix_attempted populate
    normally
  * phrasebook_proposal_approved yields zero rows (ALL existing
    phrasebook.updated entries have actor="operator"; KR-PROMOTE-
    PHRASEBOOK bucket will emit actor="kora_proposal_approved"
    and this category begins to populate)
  * investigation_completed yields zero rows pending NousResearch#406

================================================================
Drift-guard pin summary (all new endpoints)
================================================================

C — Autofix status (test_status_values_drift_guard):
  3 sources × 3 values {attempted, rejected, execution_failed}
    1. BE  _PROBE_AUTOFIX_STATUS_VALUES in kora_cli/web_server.py
    2. BE  STATUS_* constants in kora_cli/tools/probe_autofix.py
    3. FE  PROBE_AUTOFIX_STATUS_VALUES in web/src/lib/api.ts

D — Kora-action categories (test_action_categories_drift_guard):
  2 sources × 6 values {email_sent, sea_ticket_created,
  autofix_attempted, investigation_completed,
  phrasebook_proposal_approved, other}
    1. BE  _KORA_ACTION_CATEGORIES in kora_cli/web_server.py
    2. FE  KORA_ACTION_CATEGORIES in web/src/lib/api.ts
    (No emitter constants — these are FE-defined cross-seam
    categories, not per-tool emit-time values.)

Plus the existing drift guards from #180 + #183 still pass
unchanged (action_values for intent, status_values for outbound).

================================================================
STOP-ASKs (none triggered) — design choices noted inline
================================================================

Spec §4 flagged 4 possible STOP-ASKs. All resolved inline:

  [A] AuditPanelKit visual deviation → none surfaced (kit copies
      were verbatim across #180 + #183; CategoryDef parameterization
      handles per-enum differences cleanly)

  [D] Aggregated endpoint perf → not currently a concern; each
      per-seam read_audit_entries is bounded by JSONL file size,
      merge is in-memory + bounded by limit. Pre-aggregation cache
      / cursor-pagination deferred until operator reports actual
      perf issue under realistic load.

  [D] phrasebook actor field filter → confirmed field exists per
      PR #177's emitter; filter `actor != "operator"` works
      cleanly. v1 yields zero (no kora-proposal loop yet) which
      is the correct semantic.

  [C,D] Deep-link routing — used /sea-tickets?focus=<id> +
      /probe-autofix-log + /outbound-email-log forward-compat
      params; no URL-builder helper needed for v1. SeaTicketsPage
      doesn't currently consume focus; same forward-compat
      posture as PR #180.

================================================================
Tests (98 panel-suite tests pass; 51 new)
================================================================

  test_audit_panel_kit.py            (8 NEW) — kit file shape,
    exports, plain-SVG, BadgeTone matches lib, README docs every
    export, all 4 consumers import from kit, no residual local
    Sparkline/SummaryChips/FilterChips in retrofitted pages
  test_autofix_log_panel.py         (21 NEW) — backend (12) +
    drift guard (2) + FE source-pins (8) including SECURITY pin
    that before/after state dicts don't leak
  test_kora_actions_panel.py        (22 NEW) — backend (14) +
    drift guard (2) + FE source-pins (8) including
    cross-seam category filters + forward-compat investigation
    seam + privacy whitelist
  test_email_intent_panel.py        (24 existing, 2 updated
    after retrofit) — still pass
  test_outbound_email_panel.py      (23 existing, 2 updated
    after retrofit) — still pass

  98/98 panel tests pass. tsc -b clean. vite build clean.

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

  web/docs/panel-kit-megabucket/kit_storybook.png — AuditPanelKit
    components rendered in isolation (Sparkline + SummaryChips +
    FilterChips + EmptyFilteredMessage)
  web/docs/panel-kit-megabucket/autofix_log.png — 5 events covering
    all 3 statuses (3 attempted with state transitions, 1 rejected
    with envelope-gate detail, 1 execution_failed with error)
  web/docs/panel-kit-megabucket/kora_actions.png — 8-event apex
    timeline with category-color-coded chips, deep-link icons,
    cross-seam chronological order

  #180 + #183 visually unchanged (the kit IS their components —
    retrofitted pages render identically; existing screenshots in
    web/docs/email-intent-log-panel/ + web/docs/outbound-email-
    log-panel/ remain accurate).

================================================================
Recommendation for follow-on additions to KoraActionsPage
================================================================

When new mutating-action seams ship, add them by:
  1. Adding the seam to the BE endpoint's read_audit_entries
     calls (1 line each)
  2. Adding a per-category summary composer
  3. Adding to KORA_ACTION_CATEGORIES enum + drift-guard pin
  4. Adding CategoryDef + visual to KORA_ACTION_CATEGORIES_DEFS

Highest-value next additions:
  * probe.investigation_completed once NousResearch#406 lands (already wired
    forward-compat — just need the seam in SeamName Literal)
  * Future KR-PROMOTE-PHRASEBOOK actor="kora_proposal_approved"
    rows will auto-populate phrasebook_proposal_approved
    (already wired; zero changes needed when the loop ships)
  * mcp.tool_called rows where caller_actor_kind != "operator"
    — i.e., agent-driven mutating calls (would need a new
    category "agent_mutation")

Refs:
  * rafe-walker/kora-docs
    17_cc_bucket_prompts/KR-FE-PANEL-KIT-AND-MUTATING-ACTIONS-MEGABUCKET.md
  * PR #155 — KR-AUDIT-PANEL-ENDPOINTS (per-seam endpoint pattern)
  * PR #164 — CostTelemetryPage (plain-SVG charts discipline)
  * PR #180 — KR-FE-EMAIL-INTENT-LOG-PANEL (retrofitted in this PR)
  * PR #182 — tool.probe_autofix_attempted (audit source for Deliverable C)
  * PR #183 — KR-FE-OUTBOUND-EMAIL-LOG-PANEL (retrofitted in this PR)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rafe-walker rafe-walker merged commit 27f3d4a into feature/phase2-upgrades May 24, 2026
@rafe-walker rafe-walker deleted the feat/kora-KR-FE-PANEL-KIT-AND-MUTATING-ACTIONS-MEGABUCKET branch May 24, 2026 05:59
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