Skip to content

feat(ai-rules): AI-assisted rule generation with multi-backend CLI/API support#84

Merged
tstapler merged 31 commits into
mainfrom
feat/ai-rule-generation
May 19, 2026
Merged

feat(ai-rules): AI-assisted rule generation with multi-backend CLI/API support#84
tstapler merged 31 commits into
mainfrom
feat/ai-rule-generation

Conversation

@tstapler
Copy link
Copy Markdown
Owner

Summary

  • AI rule generation: New GenerateSuggestedRule RPC lets an AI agent propose approval rules from analytics gaps, review queue items, or a pasted command sample. Human approval required before any rule is saved (FR-8: no auto-save).
  • Multi-backend AI client: NewBestAvailableAIClient selects the best available backend — claude/gemini/opencode CLI (preferred; they manage their own auth) or Anthropic HTTP API as fallback. Zero config needed if a supported CLI is in PATH.
  • Four suggestion surfaces: "Generate Suggestions" panel on /rules, "Create Rule from This" in the review queue, per-gap "Suggest Rule" buttons in analytics, and a "Generate from command" inline input in the rule creation form.
  • Session hibernation (also in this branch): HibernateSession/ResumeHibernatedSession RPCs with idle sweeper, checkpoint writer, and status badge in the UI.

Architecture

AIClient interface          — Complete(ctx, system, user) (string, error)
  CLIAIClient               — stdin delivery via executor.ShortLivedCmd
  AnthropicAIClient         — net/http, claude-haiku-4-5-20251001

RulePromptBuilder interface — BuildSystemPrompt / BuildUserPrompt
  DefaultRulePromptBuilder  — assembles context; XML-delimiters on command previews

Security

  • auto_allow/allow suggestions with overbroad commandPattern (.*, .+, empty) are rejected server-side
  • CommandPreview values wrapped in <command> XML delimiters before AI prompt to prevent injection
  • Redaction sentinels unified into constants (redactedSecret, redactedPrompt)
  • GenerateSuggestedRule is read-only — never calls rulesStore.Upsert

Test plan

  • make test — all Go packages pass (623 tests)
  • cd web-app && npx jest --no-coverage — all 1435 frontend tests pass
  • make quick-check — build + tests + lint clean
  • Verify "Generate Suggestions" button on /rules shows loading state then cards
  • Verify "Create Rule from This" in review queue opens modal with pre-filled card
  • Verify accepting a suggestion calls UpsertApprovalRule (not auto-saved)
  • Verify discarding a suggestion removes the card without saving
  • Verify generation works with claude CLI in PATH (no API key needed)
  • Verify generation falls back to Anthropic API when no CLI is available and ANTHROPIC_API_KEY is set
  • Verify HibernateSession/ResumeHibernatedSession RPCs work end-to-end

🤖 Generated with Claude Code

tstapler and others added 30 commits May 18, 2026 21:49
State machine redesign + hibernation feature planning:
- requirements.md: 4 epics (state machine, async creation, sub-status visibility, hibernation)
- research/: stack, features, architecture, pitfalls
- implementation/plan.md: 4 epics, 18 stories, 36 tasks
- implementation/validation.md: 78 tests, 100% AC coverage
- decisions/ADR-001: state machine redesign (5-state model)
- decisions/ADR-002: hibernation checkpoint storage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ecture

- Go type system research added (research/go-type-system-state-machine.md)
- plan.md updated: state machine uses []TransitionDef with Guard/After hooks
  instead of flat map[Status][]Status
- Added exhaustive linter task to Epic 1
- Added ent DB migration task with old→new integer remapping
- Clean iota renumbering: Creating=0, Active=1, Paused=2, Stopped=3, Hibernated=4

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Epic 1 Story 1.3: Instance Interface Redesign
- Predicate methods (IsActive, IsHibernated, IsPaused, etc.)
- Rename setStatus → loadStatus, restrict to deserialization only
- Eliminate transitionTo fallback pattern (setStatus bypass)
- Add context to transitionTo for guard cancellation
- Update InstanceReader interface with typed Status and predicates
- Remove NeedsApproval as lifecycle state (demoted to sub-status)
- Replace RecoverFromStopped with normal Resume via state machine

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Creating=0, Active=1, Paused=2, Stopped=3, Hibernated=4. Deprecated
aliases Running/Ready/Loading kept temporarily for compile compatibility.
Adds _statusSentinel guard against iota reordering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… recovery

Deserialization wires tmux object for hibernated sessions without calling
Start(). Health checker early-bails with IsHealthy=true for hibernated
sessions. Prevents accidental wakeup on server restart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds IsCreating/IsActive/IsPaused/IsStopped/IsHibernated predicates and
GetLifecycleStatus(). Renames setStatus→loadStatus with strict scope
restriction. Adds context.Context to transitionTo. Removes
RecoverFromStopped() in favor of normal state machine path.
Updates InstanceReader interface with typed predicates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…uard+after hooks)

Introduces TransitionDef struct with Guard and After hook fields for
pre/post-transition choreography. Builds O(1) transitionIndex at init
time. Provides CanTransition() for reachability checks. Removes old
allowedTransitions map and NeedsApproval/Running/Ready transitions.
Updates state machine tests to cover 5-state model.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ew 5-state constants

Replaces all == Running, == Ready, case Running:, case Ready:,
case NeedsApproval: guards across session/ and server/ with canonical
Active/Creating. Adds || i.IsHibernated() to tmux operation guards.
Removes deprecated alias constants. Updates all tests to use new names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds runStatusRemap() which uses a sentinel-offset technique (+100) to
safely remap old 7-value status integers to the new 5-state model without
value collisions. Wired into NewEntRepository() before session loading.
Migration is a no-op on empty or already-migrated databases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…usages

Replaces session.Running and session.Ready references in server/services,
pkg/events, server/analytics, session/workspace, and tests/demo with
session.Active. Fixes ProtoToStatus to use integer literals for legacy
wire values (READY=2, NEEDS_APPROVAL=5, LOADING=3) to avoid duplicate
case errors from allow_alias. Removes unused _statusSentinel constant.
All lint checks pass (make lint: exit 0).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…atus

The CreateSession RPC now saves the instance with Creating status and
returns immediately, then spawns a goroutine to call instance.Start().
On success the goroutine sets Active status; on failure it sets Stopped
and records the error in creation_progress.

Also adds the creation_progress = 51 proto field to the Session message
and populates it from Instance.CreationProgress in instance_adapter.go.
Adds ForceStatus() public method for error-recovery paths that cannot
use the normal state-machine transition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Generated Go and TypeScript bindings from the proto change in the
previous commit. Session.creation_progress (field 51) is now accessible
as Session.CreationProgress in Go and session.creationProgress in
TypeScript.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ctive sessions

Adds SubStatusChip component that renders inline on Active session rows and cards,
showing fine-grained activity (Thinking…, Needs Approval, Error, Tests Failing,
Rate Limited). Uses vanilla-extract CSS with theme tokens. Returns null for IDLE
and UNSPECIFIED to keep the UI clean when no noteworthy sub-status is present.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SessionCard: render spinner + creationProgress text when status is Creating
- SessionCard: guard terminal snapshot display behind ACTIVE status
- SessionActionsOverflow: disable Pause/Restart actions while Creating
- Fix RUNNING→ACTIVE enum references (same wire value via allow_alias)
- Cast to number to bypass TS duplicate-value narrowing for allow_alias enums

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… hibernate status support

- SessionActionsOverflow: use ACTIVE only (not RUNNING) to avoid TS2367 from allow_alias
- SessionActionsOverflow: add onHibernate/onResumeFromHibernation menu items
- SessionCard: map HIBERNATED status to statusPaused (no distinct style yet)
- SessionCard: add "Hibernated" display text for HIBERNATED status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…st filter

SubStatus enum in proto-es uses NEEDS_APPROVAL, not SUB_STATUS_NEEDS_APPROVAL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wire hibernateSession/resumeHibernatedSession RPCs into the UI:
- SessionActionsOverflow already had the menu items; now wires through
- SessionRow/SessionCard accept onHibernate/onResumeFromHibernation props
- SessionList threads both callbacks down to row and card views
- PaneSplitRenderer pulls hibernate callbacks from SessionServiceContext
- SessionServiceContext exposes hibernateSession/resumeHibernatedSession
- SessionRow status dot gains 'hibernated' data-status value (idle color)
- SessionCard getStatusText returns "Hibernated" for HIBERNATED status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
InstanceToProto was called after goroutine launch, racing with the
goroutine's first write to instance.CreationProgress. Snapshot the
proto before spawning the goroutine so the response is always a clean
Creating-status snapshot.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BottomNav had its own hardcoded lists that didn't reflect NAV_PAGES.
Backlog was missing from primaryItems; Features was missing from
moreItems entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ary flag

BottomNav maintained its own parallel hardcoded item lists that diverged
from NAV_PAGES. Adding or reordering a nav item required updating two
places.

Adds `bottomNavPrimary` to NavPage. Primary bar items are marked in
NAV_PAGES; everything mobile-visible without that flag flows
automatically into the More sheet. Exports BOTTOM_NAV_PRIMARY and
BOTTOM_NAV_MORE for BottomNav to consume.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- requirements.md: 8 FRs across 4 user stories, 4 surfaces
- research/: stack, features, architecture, pitfalls (4 agents)
- implementation/plan.md: 6 epics, 17+ stories, 35 tasks
- implementation/adversarial-review.md: 2 blocking issues resolved
- implementation/validation.md: 35 tests, 8/8 FRs covered
- decisions/ADR-001: RulePromptBuilder + AIClient interface design

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends the AIClient abstraction to support locally installed AI CLI
tools (claude, gemini, opencode) as backends alongside the Anthropic
HTTP API. The executor.ShortLivedCmd primitive provides context
cancellation, timeout, and audit logging for all subprocess calls.

NewBestAvailableAIClient selects automatically by priority:
  1. Anthropic HTTP API (ANTHROPIC_API_KEY)
  2. claude CLI (--print mode, stdin delivery)
  3. gemini CLI
  4. opencode CLI

Adding a new agent CLI requires one CLIAgentSpec entry in
knownCLIAgents — no other changes needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CLIs manage their own authentication (API keys, login state) so they
require no extra configuration in stapler-squad. Anthropic HTTP API
is now a last-resort fallback for environments with no CLI installed.

New priority: claude CLI → gemini → opencode → Anthropic HTTP API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ponent

Backend (Epic 1 completion):
- server/services/ai_interfaces.go: RulePromptBuilder, AIClient, RulePromptContext
- server/services/rule_prompt_builder.go: DefaultRulePromptBuilder with secret redaction
- server/services/anthropic_client.go: AnthropicAIClient (HTTP fallback)
- server/services/rules_service.go: GenerateSuggestedRule handler, parseSuggestions,
  attachConflictInfo, buildPromptContext
- server/services/approval_handler.go: redact secrets before analytics recording
- config/config.go: AnthropicAPIKey field + ANTHROPIC_API_KEY env override

Frontend (Epic 2):
- web-app/src/lib/hooks/useGenerateRule.ts: wraps GenerateSuggestedRule RPC,
  manages suggestions[], loading, error, cancel (AbortController), 60s timeout
- web-app/src/components/sessions/SuggestedRuleCard.tsx: shared card with
  confidence badge, conflict/shadow warnings, editable fields, Accept & Discard
- web-app/src/components/sessions/SuggestedRuleCard.css.ts: vanilla-extract styles

Tests: 13 Go unit + 21 frontend (9 hook + 12 card), all passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Epic 3 (Rules page): "Generate Suggestions" button on ApprovalRulesPanel
triggers analytics-gap suggestions; renders SuggestedRuleCard list with
per-card discard and accept→refresh.

Epic 4 (Review queue): "Create Rule" button on pending approval items
opens a modal with SuggestedRuleCard pre-filled from the item's command.

Epic 5 (Analytics panel): "Suggest Rule" buttons replace "Add rule →"
links on coverage-gap rows; inline SuggestedRuleCard expands below the
active row without a modal.

Epic 6 (Command sample): Collapsible "Generate from command" section in
the rule creation form pre-fills fields from COMMAND_SAMPLE suggestion,
respecting user-touched fields via a Set<keyof RuleFormState> ref.

Tests: 37 ApprovalRulesPanel + 17 ReviewQueuePanel + 10 ApprovalAnalyticsPanel
All passing. Zero new TypeScript errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…, test quality

- Security: reject auto_allow/allow rules with overbroad commandPattern (.*/.+/empty)
- Security: wrap CommandPreview in <command> XML delimiters to prevent prompt injection
- Security: unify redaction sentinel strings into constants (redactedSecret/redactedPrompt)
- Refactor: NewBestAvailableAIClient accepts []CLIAgentSpec param (no global var mutation)
- Refactor: consolidate duplicate decisionString/riskLevelString into single canonical copy
- Refactor: remove unused SeedExamples field from RulePromptContext
- Accessibility: confidence badge adds text label (High/Medium/Low) for WCAG 1.4.1
- Accessibility: aria-live region for generate loading state in ApprovalRulesPanel
- Accessibility: aria-label on filter input in ApprovalAnalyticsPanel
- Accessibility: both modals in ReviewQueuePanel use createPortal to escape transforms
- CSS: replace hardcoded zIndex:1000 with zIndex.modal from theme contract
- CSS: replace inline var(--warning-bg) strings with vanilla-extract vars.*
- React: useEffect cleanup aborts in-flight request on unmount in useGenerateRule
- React: friendly timeout/cancellation error messages distinguish the two cases
- React: void on unhandled generateRule() Promise in ReviewQueuePanel
- React: rulesRef prevents stale closure in SuggestedRuleCard handleAccept
- React: all Suggest Rule buttons disabled while generation is in-flight
- Tests: parseSuggestions tests for markdown-fenced and malformed input
- Tests: fix vacuous >= 0 gap count assertion -> exact count
- Tests: replace time.Sleep with require.Eventually polling
- Tests: data-testid attributes on buttons; getByTestId in tests
- Tests: assert SuggestionSource enum in RPC call arguments
- Registry: add GenerateSuggestedRule entry + update lastModified timestamps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 19, 2026 05:50
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@github-actions
Copy link
Copy Markdown
Contributor

⚠️ Registry Validation

Registry Validation
===================

Building backend scanner...
Scanning backend features...
Wrote 85 feature files to /tmp/tmp.HDSQOENliZ/backend
Wrote 12 feature files to /tmp/tmp.HDSQOENliZ/backend
Wrote 20 feature files to /tmp/tmp.HDSQOENliZ/backend

=== Backend Registry Diff ===
Committed: 118  Generated: 115  Divergence: 2.54%
⚠️  Removed RPCs:
  - browser:cdp-stream
  - browser:proxy
  - rules:generate-suggested
⚠️  78 feature(s) missing // +api: marker (markerFound: false)

❌ Divergence 2.54% > 2%. Run 'make registry-generate' and commit.

Test Coverage: 87/118 features have testIds (73.7%)

Registry validation is in observation mode until 2026-05-02.
After that date, divergence > 2% will block merges.
Coverage reporting is advisory only.

@github-actions
Copy link
Copy Markdown
Contributor

Go Benchmarks (Tier 1)

benchmarks/go/tier1-baseline.txt:6: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:2010: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:3963: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:5977: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:7995: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:9974: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:11956: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:13963: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:15966: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:22713: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:29153: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:36635: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:43339: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:49988: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:56807: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:63623: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:70281: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:75582: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:81213: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:86505: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:92385: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:98321: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:103519: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:108992: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:114268: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:121056: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:128125: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:134755: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:142068: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:148949: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:155721: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:162608: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:169453: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:176776: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:183851: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:191095: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:197706: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:205205: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:213317: parsing iteration count: invalid syntax
benchmarks/go/tier1-baseline.txt:220466: parsing iteration count: invalid syntax
tier1-bench.txt:6: parsing iteration count: invalid syntax
tier1-bench.txt:1893: parsing iteration count: invalid syntax
tier1-bench.txt:3824: parsing iteration count: invalid syntax
tier1-bench.txt:5763: parsing iteration count: invalid syntax
tier1-bench.txt:7704: parsing iteration count: invalid syntax
tier1-bench.txt:9678: parsing iteration count: invalid syntax
tier1-bench.txt:11528: parsing iteration count: invalid syntax
tier1-bench.txt:13519: parsing iteration count: invalid syntax
tier1-bench.txt:15438: parsing iteration count: invalid syntax
tier1-bench.txt:21410: parsing iteration count: invalid syntax
tier1-bench.txt:27456: parsing iteration count: invalid syntax
tier1-bench.txt:33757: parsing iteration count: invalid syntax
tier1-bench.txt:39851: parsing iteration count: invalid syntax
tier1-bench.txt:45877: parsing iteration count: invalid syntax
tier1-bench.txt:52206: parsing iteration count: invalid syntax
tier1-bench.txt:58240: parsing iteration count: invalid syntax
tier1-bench.txt:65037: parsing iteration count: invalid syntax
tier1-bench.txt:70156: parsing iteration count: invalid syntax
tier1-bench.txt:75296: parsing iteration count: invalid syntax
tier1-bench.txt:80298: parsing iteration count: invalid syntax
tier1-bench.txt:85548: parsing iteration count: invalid syntax
tier1-bench.txt:90675: parsing iteration count: invalid syntax
tier1-bench.txt:95765: parsing iteration count: invalid syntax
tier1-bench.txt:101128: parsing iteration count: invalid syntax
tier1-bench.txt:106321: parsing iteration count: invalid syntax
tier1-bench.txt:112802: parsing iteration count: invalid syntax
tier1-bench.txt:119087: parsing iteration count: invalid syntax
tier1-bench.txt:126185: parsing iteration count: invalid syntax
tier1-bench.txt:132425: parsing iteration count: invalid syntax
tier1-bench.txt:139068: parsing iteration count: invalid syntax
tier1-bench.txt:145047: parsing iteration count: invalid syntax
tier1-bench.txt:151350: parsing iteration count: invalid syntax
tier1-bench.txt:157599: parsing iteration count: invalid syntax
tier1-bench.txt:164455: parsing iteration count: invalid syntax
tier1-bench.txt:171248: parsing iteration count: invalid syntax
tier1-bench.txt:177470: parsing iteration count: invalid syntax
tier1-bench.txt:184061: parsing iteration count: invalid syntax
tier1-bench.txt:191025: parsing iteration count: invalid syntax
tier1-bench.txt:197947: parsing iteration count: invalid syntax
tier1-bench.txt:204870: parsing iteration count: invalid syntax
goos: linux
goarch: amd64
pkg: github.com/tstapler/stapler-squad/session/scrollback
cpu: AMD EPYC 7763 64-Core Processor                
                                      │ tier1-bench.txt │
                                      │     sec/op      │
CircularBuffer_ConcurrentReadWrite-4        3.534µ ± 2%
CircularBuffer_BurstAppend-4                102.7µ ± 2%
CircularBuffer_GetLastN_LargeBuffer-4       17.94µ ± 4%
CircularBuffer_GetRange_Sequential-4        10.09µ ± 2%
CircularBufferAppend-4                      100.2n ± 0%
CircularBufferGetLastN-4                    1.915µ ± 2%
CircularBufferConcurrentAppend-4            129.2n ± 1%
geomean                                     2.876µ

                                      │ tier1-bench.txt │
                                      │      B/op       │
CircularBuffer_ConcurrentReadWrite-4       6.062Ki ± 0%
CircularBuffer_BurstAppend-4               62.50Ki ± 0%
CircularBuffer_GetLastN_LargeBuffer-4      56.00Ki ± 0%
CircularBuffer_GetRange_Sequential-4       28.00Ki ± 0%
CircularBufferAppend-4                       24.00 ± 0%
CircularBufferGetLastN-4                   6.000Ki ± 0%
CircularBufferConcurrentAppend-4             32.00 ± 0%
geomean                                    3.077Ki

                                      │ tier1-bench.txt │
                                      │    allocs/op    │
CircularBuffer_ConcurrentReadWrite-4         2.000 ± 0%
CircularBuffer_BurstAppend-4                1.000k ± 0%
CircularBuffer_GetLastN_LargeBuffer-4        1.000 ± 0%
CircularBuffer_GetRange_Sequential-4         1.000 ± 0%
CircularBufferAppend-4                       1.000 ± 0%
CircularBufferGetLastN-4                     1.000 ± 0%
CircularBufferConcurrentAppend-4             1.000 ± 0%
geomean                                      2.962

                             │ tier1-bench.txt │
                             │       B/s       │
CircularBuffer_BurstAppend-4      594.3Mi ± 3%

cpu: AMD EPYC 9V74 80-Core Processor                
                                      │ benchmarks/go/tier1-baseline.txt │
                                      │              sec/op              │
CircularBuffer_ConcurrentReadWrite-4                         3.156µ ± 1%
CircularBuffer_BurstAppend-4                                 105.2µ ± 0%
CircularBuffer_GetLastN_LargeBuffer-4                        16.10µ ± 1%
CircularBuffer_GetRange_Sequential-4                         9.103µ ± 2%
CircularBufferAppend-4                                       103.1n ± 1%
CircularBufferGetLastN-4                                     1.733µ ± 1%
CircularBufferConcurrentAppend-4                             134.2n ± 1%
geomean                                                      2.742µ

                                      │ benchmarks/go/tier1-baseline.txt │
                                      │               B/op               │
CircularBuffer_ConcurrentReadWrite-4                        6.062Ki ± 0%
CircularBuffer_BurstAppend-4                                62.50Ki ± 0%
CircularBuffer_GetLastN_LargeBuffer-4                       56.00Ki ± 0%
CircularBuffer_GetRange_Sequential-4                        28.00Ki ± 0%
CircularBufferAppend-4                                        24.00 ± 0%
CircularBufferGetLastN-4                                    6.000Ki ± 0%
CircularBufferConcurrentAppend-4                              32.00 ± 0%
geomean                                                     3.077Ki

                                      │ benchmarks/go/tier1-baseline.txt │
                                      │            allocs/op             │
CircularBuffer_ConcurrentReadWrite-4                          2.000 ± 0%
CircularBuffer_BurstAppend-4                                 1.000k ± 0%
CircularBuffer_GetLastN_LargeBuffer-4                         1.000 ± 0%
CircularBuffer_GetRange_Sequential-4                          1.000 ± 0%
CircularBufferAppend-4                                        1.000 ± 0%
CircularBufferGetLastN-4                                      1.000 ± 0%
CircularBufferConcurrentAppend-4                              1.000 ± 0%
geomean                                                       2.962

                             │ benchmarks/go/tier1-baseline.txt │
                             │               B/s                │
CircularBuffer_BurstAppend-4                       580.1Mi ± 4%

@github-actions
Copy link
Copy Markdown
Contributor

E2E RPC Latency

list-sessions-ttfb-mean: 3ms (▼ faster -26.8%; baseline: 4ms)
list-sessions-total-mean: 4ms (▼ faster -24.2%; baseline: 5ms)

@github-actions
Copy link
Copy Markdown
Contributor

UX Analysis

Check Status Details
✅ Axe Core (WCAG 2.1 AA) success Critical/serious violations block merge
⚠️ Lighthouse Performance Score: unknown Warning if < 70 (non-blocking)
🤖 Claude UX Analysis Advisory See docs/qa/ for findings

Axe Core excludes terminal rendering areas (intentional design).
Lighthouse runs in desktop preset for this developer tool.

@github-actions
Copy link
Copy Markdown
Contributor

Frontend Terminal Throughput

terminal-throughput-mean: 16 KB/s ▲ +13.0% (baseline: 14 KB/s)
terminal-throughput-p50: 16 KB/s ▲ +0.5% (baseline: 16 KB/s)

@github-actions
Copy link
Copy Markdown
Contributor

🎬 E2E Feature Demos

2 shard(s) recorded feature flows for this PR.

recordings shard 1
recordings shard 2

Demo preview opens directly in browser (single-file HTML). Raw WebM recordings in ZIP. Expires after 30 days.

@tstapler tstapler merged commit b7dc968 into main May 19, 2026
22 checks passed
@tstapler tstapler deleted the feat/ai-rule-generation branch May 19, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants