UI ops slice: retry & guardrail depth, audit rejections, July event vocabulary in the feed#217
Conversation
…s signals Checkpoints (verified flag), context-pressure warnings (detect-only wording per the #136 rule), approval audit rejections, episode-memory write/skip decisions, metrics-write drift, retry decisions and evaluator-shaped goal_evaluated events now get first-class feed summaries and levels. Each of these types also carries a compact structured 'data' object (only fields the event actually had — nothing fabricated) so the client panels can render depth without a second data path. Unknown event types keep degrading exactly as before: no summary, no feed line, never an invented one. Part of #156 / #215. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…abulary Checkpoint (CP), context pressure (CX), approval audit (AU), memory write (MB), metrics drift (MX), retry (RT), guardrail (GR) and goal-loop (GL) events now read at a glance in the Live Feed, each with one meta chip from the event's structured data (limit vs value, size vs threshold, attempt counts, audit issue count, verified flag). Types outside the map fall back to the shared level-based feed row, so unknown events keep rendering as today. Ships the append-only [wave:ops] styles block the ops panels share. Part of #156 / #215. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ssure panels Three self-mounting panels on Alerts & Guardrails, derived from the recent server-composed event feed plus task/approval state already in the snapshot: - Retry State: per-task retry_state (scheduled / budget exhausted / human input required) with attempt counts, the retry_decision verdict and reason; BLOCKED tasks whose retry events scrolled out of the window are still listed and say so instead of pretending to know why. - Guardrail Triggers: limit name, value vs limit, the blocked task, and an honest override status line (none on file / requested / approved by whom / consumed and re-armed). - Context Pressure Warnings: source, size vs threshold, and the detect-only note — nothing was pruned or truncated. Every panel states its empty case plainly. Part of #156 / #215. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…re visible, never silent approval_audit_failed events (#133's trust boundary) now render on the Approvals page with the artifact, the audit's reason, the failed check list, the record id and the status the record claimed. The panel copy says exactly what happened: the record failed the consistency audit and was treated as absent — the gate stayed closed and the gated work did not proceed. This is the tampering-visibility surface for forged or corrupted approvals. Part of #215. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… states Executes the real page renderers (template-literal client code) against a minimal DOM stub instead of only string-matching the source: feed lines + levels + structured data for every July event type from fixture-shaped events, unknown-type graceful degradation, the retry/guardrail/pressure panels, override status variants (none / approved / consumed), the audit rejections panel, honest empty states, and single-mount across re-renders. Part of #156 / #215. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
📝 WalkthroughWalkthroughAdds ops-specific feed summaries, severity mapping, and structured data, then renders new dashboard panels for retries, guardrails, context pressure, and audit rejections with updated live-feed rows, styling, and tests. ChangesOps signals feed, panels, and styling
Estimated code review effort: 3 (Moderate) | ~30 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/observability/dashboard/ui/pages/alerts-guardrails.js (1)
24-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
.grid-2gives Guardrail Triggers / Context Pressure an uneven 1.55fr/.85fr split.
.grid-2(styles.js line 830) was built for asymmetric main-content/sidebar layouts elsewhere. Reusing it here for two co-equal panels will visually skew Guardrail Triggers wider than Context Pressure Warnings, which doesn't look like the intended peer-panel layout.💡 Suggested fix
- '<div class="grid-2" style="margin-top:16px">' + + '<div class="grid-2-even" style="margin-top:16px">' +.grid-2-even { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; align-items: start; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/observability/dashboard/ui/pages/alerts-guardrails.js` around lines 24 - 40, The two side-by-side panels in OPS_ALERTS_PANELS_HTML are using the shared .grid-2 layout, which creates an uneven main/sidebar split instead of equal peer columns. Update this markup to use a dedicated equal-width grid class for the Guardrail Triggers and Context Pressure Warnings section, and keep the existing panel structure unchanged so both panels render with the same width. Use the OPS_ALERTS_PANELS_HTML constant as the anchor when making the layout swap.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/observability/dashboard/ui/pages/alerts-guardrails.js`:
- Around line 24-40: The two side-by-side panels in OPS_ALERTS_PANELS_HTML are
using the shared .grid-2 layout, which creates an uneven main/sidebar split
instead of equal peer columns. Update this markup to use a dedicated equal-width
grid class for the Guardrail Triggers and Context Pressure Warnings section, and
keep the existing panel structure unchanged so both panels render with the same
width. Use the OPS_ALERTS_PANELS_HTML constant as the anchor when making the
layout swap.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 13750464-b66e-4f49-9212-094008a586d9
📒 Files selected for processing (6)
src/observability/dashboard/state/feed.jssrc/observability/dashboard/ui/pages/alerts-guardrails.jssrc/observability/dashboard/ui/pages/approvals.jssrc/observability/dashboard/ui/pages/live-feed.jssrc/observability/dashboard/ui/styles.jstests/dashboard-ops-signals.test.js
…ontract (#217 review) The retry-decision surfaces read an `ev.decision` field the harness never emits. The pinned #123 contract is { action, next_status, retry_recommendation, attempt, max_attempts, reason, issues } — so on every real run the Live Feed showed "Retry decision: ?" and the Retry State panel could label any BLOCKED task with a fabricated cause. - feed.js / live-feed.js / alerts-guardrails.js: rekey the retry summary, level, structured data and panel state onto action/next_status. Distinguish action:exhausted ("budget exhausted") from action:block ("validator blocked") — both carry next_status BLOCKED, so keying on status alone mislabels a validator block as budget exhaustion. - goal_evaluated: drop the fictional recommendation/criteria_met shape; the real pipeline-loop.js emitter carries iteration/max_iterations/goal_id/ status/score/failing_stages. Defer the summary to the shared plain-language path and read pass/fail from `status`. - approvals / live-feed: count only array-shaped `issues` on forged approval_audit_failed records — hostile input never throws into the page. - opsEnsureSection moves to the earliest-concatenated wave:ops module. - tests: fixtures + assertions now use the real emitted shapes; add a regression proving a validator block is never labelled budget exhausted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/observability/dashboard/state/feed.js (1)
159-159: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueFallback
/^retry_/branch likely unreachable given current vocabulary.
retry_decisionis already handled and returned at Lines 151-158;task_retry_scheduled/task_retry_exhausted/task_human_context_requiredare handled above too and don't match^retry_. This line only fires for some hypothetical future event type prefixedretry_that isn't in the pinned contract — currently dead code, but harmless as a defensive catch-all.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/observability/dashboard/state/feed.js` at line 159, The /^retry_/ fallback in the event-to-severity mapping is effectively unreachable because retry_decision and the known retry-related event types are already handled earlier in the same logic. Remove this dead branch from the relevant classifier function (the one processing ev.type and returning 'warn') unless you intentionally want a future-proof catch-all, in which case keep it only with an explicit comment explaining that it is defensive and not part of the current contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/observability/dashboard/state/feed.js`:
- Around line 86-89: The retry feed data is stripping the issues field from
retry decision payloads before they reach the UI. Update the field lists used by
OPS_EVENT_DATA_FIELDS for retry_decision and the task_retry_* entries so they
include issues, and make sure the feed mapping in feed.js preserves that
property alongside the existing task_id, stage_id, attempt, action, next_status,
and reason fields.
---
Nitpick comments:
In `@src/observability/dashboard/state/feed.js`:
- Line 159: The /^retry_/ fallback in the event-to-severity mapping is
effectively unreachable because retry_decision and the known retry-related event
types are already handled earlier in the same logic. Remove this dead branch
from the relevant classifier function (the one processing ev.type and returning
'warn') unless you intentionally want a future-proof catch-all, in which case
keep it only with an explicit comment explaining that it is defensive and not
part of the current contract.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a53141e9-5ffa-4720-a3d1-0c75012fb542
📒 Files selected for processing (5)
src/observability/dashboard/state/feed.jssrc/observability/dashboard/ui/pages/alerts-guardrails.jssrc/observability/dashboard/ui/pages/approvals.jssrc/observability/dashboard/ui/pages/live-feed.jstests/dashboard-ops-signals.test.js
🚧 Files skipped from review as they are similar to previous changes (4)
- src/observability/dashboard/ui/pages/live-feed.js
- src/observability/dashboard/ui/pages/approvals.js
- tests/dashboard-ops-signals.test.js
- src/observability/dashboard/ui/pages/alerts-guardrails.js
| // Pinned #123 emitter contract: { task_id, stage_id, attempt, | ||
| // max_attempts, retry_recommendation, action, next_status, reason, | ||
| // issues[] } with action ∈ complete|retry|exhausted|human_context|block. | ||
| return `Retry decision for ${ev.task_id ?? 'task'}: ${ev.action ?? '?'}${ev.next_status ? ` → ${ev.next_status}` : ''}${ev.reason ? ` — ${ev.reason}` : ''}`; |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map the relevant file and search the retry/issue paths.
git ls-files src/observability/dashboard/state/feed.js src/observability/dashboard/state/live-feed.js src/observability/dashboard/alerts-guardrails.js
printf '\n--- feed.js outline ---\n'
ast-grep outline src/observability/dashboard/state/feed.js --view expanded || true
printf '\n--- live-feed.js outline ---\n'
ast-grep outline src/observability/dashboard/state/live-feed.js --view expanded || true
printf '\n--- alerts-guardrails.js outline ---\n'
ast-grep outline src/observability/dashboard/alerts-guardrails.js --view expanded || true
printf '\n--- feed.js relevant lines ---\n'
nl -ba src/observability/dashboard/state/feed.js | sed -n '100,180p'
printf '\n--- live-feed.js issue references ---\n'
rg -n "issues|retry_decision|task_retry_|approval_audit_failed" src/observability/dashboard/state/live-feed.js src/observability/dashboard/alerts-guardrails.js src/observability/dashboard/state/feed.jsRepository: richard-devbot/SDLC-rstack
Length of output: 887
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- files matching observability/dashboard ---\n'
git ls-files | rg '^src/observability/dashboard/'
printf '\n--- retry / issues references under observability ---\n'
rg -n "retry_decision|task_retry_|approval_audit_failed|issues" src/observability/dashboard
printf '\n--- feed.js whitelist section ---\n'
sed -n '110,150p' src/observability/dashboard/state/feed.jsRepository: richard-devbot/SDLC-rstack
Length of output: 255
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- files matching src/observability/dashboard ---'
git ls-files | rg '^src/observability/dashboard/' || true
echo
echo '--- retry / issues references under src/observability/dashboard ---'
rg -n "retry_decision|task_retry_|approval_audit_failed|issues" src/observability/dashboard || true
echo
echo '--- feed.js whitelist section ---'
python3 - <<'PY'
from pathlib import Path
p = Path('src/observability/dashboard/state/feed.js')
for i, line in enumerate(p.read_text().splitlines(), 1):
if 108 <= i <= 150:
print(f"{i:4d}: {line}")
PYRepository: richard-devbot/SDLC-rstack
Length of output: 10084
issues is missing from retry feed data. OPS_EVENT_DATA_FIELDS drops issues for retry_decision and the task_retry_* entries, so any retry payload carrying validator-block detail loses it before the feed reaches the UI. Add issues to those field lists if the retry panels are meant to surface that contract.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/observability/dashboard/state/feed.js` around lines 86 - 89, The retry
feed data is stripping the issues field from retry decision payloads before they
reach the UI. Update the field lists used by OPS_EVENT_DATA_FIELDS for
retry_decision and the task_retry_* entries so they include issues, and make
sure the feed mapping in feed.js preserves that property alongside the existing
task_id, stage_id, attempt, action, next_status, and reason fields.
Part of #156 (the ops remainder: retry traces + guardrail depth on Alerts & Guardrails, feed rendering slot filled). Part of #215 (approval audit visibility + checkpoint / context-pressure / memory / metrics / retry-decision / goal events in the feed).
Not closed by this PR: #156's Command Center items (pipeline next-action, BLOCKED-task treatment on the stage strip) and schema-version visibility ship in the sibling command-center PR of this wave — coordinate there before closing #156.
What ships
state/feed.js — July event vocabulary (sole owner this wave)
stage_checkpoint_before/after_saved(verified flag, warn when NOT verified),context_pressure_warning(detect-only wording per the [BLE-6.2] Add context pressure warnings #136 rule — never claims pruning),approval_audit_failed,episode_memory_written/episode_memory_skipped_untrusted,metrics_write_failed,retry_decision(BLOCKED/NEEDS_CONTEXT → blocked level), evaluator-shapedgoal_evaluated(iteration + criteria counts; legacy status/score shape keeps its existing summary).dataobject — only fields the event actually had — so the panels below render depth without a second data path.pages/live-feed.js — distinct glyphs + detail chips
CP / CX / AU / MB / MX / RT / GR / GL glyphs with one meta chip per line (limit vs value, size vs threshold, attempt n/m, audit issue count, verified flag). Everything else falls back to the shared feed row.
pages/alerts-guardrails.js — retry & guardrail depth (#156)
pages/approvals.js — Audit Rejections (#215, tampering visibility)
approval_audit_failedevents render with artifact, reason, failed-check list, record id and claimed status, under plain copy: the record failed the consistency audit and was treated as absent — the gate stayed closed and the gated work did not proceed.All panels self-mount once, state their empty case honestly, and fabricate nothing.
Ownership discipline
Only
state/feed.js,pages/live-feed.js,pages/alerts-guardrails.js,pages/approvals.js, one appended/* [wave:ops] */block instyles.js, and the new test file. No changes to lib.js, drawer.js, client.js core, other pages, or the state payload.Verification
tests/dashboard-ops-signals.test.jsexecutes the real page renderers against a DOM stub (13 tests); full suite 668/668, lint,rstack-agents validate, security audit,git diff --checkall green; the bundle-compile test stays green.…-ui-fixture-july-signals) from this worktree: all eight glyph types in the Live Feed, the BLOCKED005-testingtask in Retry State (attempt 1, decision BLOCKED, budget exhausted),maxTaskAttempts 2/2with "No override on file",memory_summary 61000 vs 40000 chars, and the forgedguardrail-override:005-testingrecord in Audit Rejections.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes