Skip to content

feat(cogbattlespace): add Cognitive Battlespace UI components and page#22202

Closed
BrianCLong wants to merge 1 commit intomainfrom
feat/cogbattlespace-ui-14338170707411774589
Closed

feat(cogbattlespace): add Cognitive Battlespace UI components and page#22202
BrianCLong wants to merge 1 commit intomainfrom
feat/cogbattlespace-ui-14338170707411774589

Conversation

@BrianCLong
Copy link
Copy Markdown
Owner

@BrianCLong BrianCLong commented Mar 29, 2026

Implements the React UI skeleton for the Cognitive Battlespace subsystem, including layer toggles, explanation drawers, metrics panels, and a unified rejection report panel. Fixes TypeScript build issues in summit-cogbattlespace by excluding test files and adds missing methods to InMemoryCogBattleStorage for the firewall reconciler.


PR created automatically by Jules for task 14338170707411774589 started by @BrianCLong

Summary by CodeRabbit

  • New Features
    • Launched Cognitive Battlespace interface with layer-based analysis (reality, narrative, and belief layers).
    • Display narrative metrics including velocity calculations and divergence signal analysis.
    • View rejection reports with detailed error tracking and status indicators.
    • Access narrative explanations through an interactive explanation drawer with disclaimers.

Adds the requested Tri-Graph Model UI components to `packages/summit-ui/src/components/cogbattlespace/` including `LayerToggle`, `ExplainDrawer`, `MetricsPanel`, and `RejectionReportPanel`.
Also adds the main Cognitive Battlespace page stub to `packages/summit-ui/src/pages/cogbattlespace/index.tsx`.
Updates `packages/summit-cogbattlespace/src/storage.ts` to include required methods `getCurrentEntity` and `putLaneSnapshot`.
Updates `packages/summit-cogbattlespace/tsconfig.json` to exclude test files from compilation.

Co-authored-by: BrianCLong <6404035+BrianCLong@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements the Cognitive Battlespace feature, adding storage methods for lane snapshots and a suite of UI components for visualizing narratives, divergence signals, and layer toggles. The feedback primarily addresses TypeScript best practices, specifically recommending the replacement of 'any' types with generics or defined interfaces to enhance type safety. Additionally, several suggestions were made to improve React component structure by extracting prop types and using more stable keys for list rendering to prevent potential UI bugs.

Comment on lines +30 to +31
getCurrentEntity(entityType: string, fingerprint: string): Promise<any>;
putLaneSnapshot(entityType: string, fingerprint: string, data: any): Promise<void>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The use of any for getCurrentEntity's return type and putLaneSnapshot's data parameter circumvents TypeScript's type safety. This can lead to runtime errors and makes the code harder to maintain and understand. Consider using a generic type parameter for the entity data, for example <T>, to provide type safety for consumers of this storage interface.

For example:

export interface CogBattleStorage {
  // ... other methods
  getCurrentEntity<T>(entityType: string, fingerprint: string): Promise<T | null>;
  putLaneSnapshot<T>(entityType: string, fingerprint: string, data: T): Promise<void>;
}

This would require updating InMemoryCogBattleStorage as well, but would significantly improve type safety.

Comment on lines +6 to +22
// Replace these with your real API client
async function fetchTopNarratives() {
return [];
}
async function fetchDivergence() {
return [];
}

export default function CognitiveBattlespacePage() {
const [layers, setLayers] = useState<Record<Layer, boolean>>({
reality: true,
narrative: true,
belief: true
});

const [narratives, setNarratives] = useState<any[]>([]);
const [divergence, setDivergence] = useState<any[]>([]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Using any[] for state and not providing return types for fetch functions undermines TypeScript's type safety. It's best to define strong types for your data structures (Narrative, Divergence) and use them consistently in your state and API functions. This will prevent runtime errors and improve code maintainability. These types could be shared with the MetricsPanel component, for example by moving them to a shared types file.

// It's best to define these types in a shared file and import them.
type Narrative = {
  id: string;
  label: string;
  summary: string;
  metrics: { velocity: number };
};

type Divergence = {
  narrativeId: string;
  claimId: string;
  divergenceScore: number;
};

// Replace these with your real API client
async function fetchTopNarratives(): Promise<Narrative[]> {
  return [];
}
async function fetchDivergence(): Promise<Divergence[]> {
  return [];
}

export default function CognitiveBattlespacePage() {
  const [layers, setLayers] = useState<Record<Layer, boolean>>({
    reality: true,
    narrative: true,
    belief: true,
  });

  const [narratives, setNarratives] = useState<Narrative[]>([]);
  const [divergence, setDivergence] = useState<Divergence[]>([]);

Comment on lines +3 to +9
export function ExplainDrawer(props: {
open: boolean;
onClose: () => void;
title: string;
body: string;
disclaimers: string[];
}) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

For better readability and reusability, it's a good practice to define component props in a separate interface or type alias.

Suggested change
export function ExplainDrawer(props: {
open: boolean;
onClose: () => void;
title: string;
body: string;
disclaimers: string[];
}) {
type ExplainDrawerProps = {
open: boolean;
onClose: () => void;
title: string;
body: string;
disclaimers: string[];
};
export function ExplainDrawer(props: ExplainDrawerProps) {

<h3 className="text-sm font-semibold">Defensive disclaimers</h3>
<ul className="mt-2 list-disc pl-5 text-sm">
{props.disclaimers.map((d, i) => (
<li key={i}>{d}</li>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using an array index as a key is an anti-pattern in React when the list of items can be reordered, added to, or removed from. This can lead to incorrect component state and rendering issues. If the disclaimer strings are not guaranteed to be unique, a more robust key can be generated by combining the string and its index.

Suggested change
<li key={i}>{d}</li>
<li key={`${d}-${i}`}>{d}</li>

@@ -0,0 +1,30 @@
import React from "react";

export type Layer = "reality" | "narrative" | "belief";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To ensure the Layer type and the array of layers used for rendering are always in sync, it's better to define the array as a const and derive the type from it. This avoids potential mismatches if the Layer type is updated in the future but the hardcoded array in the component is not.

Suggested change
export type Layer = "reality" | "narrative" | "belief";
export const LAYERS = ["reality", "narrative", "belief"] as const;
export type Layer = typeof LAYERS[number];


return (
<div className="flex gap-2 items-center">
{(["reality", "narrative", "belief"] as Layer[]).map((k) => (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Following the suggestion to create a LAYERS constant, you should iterate over it here instead of a hardcoded array.

Suggested change
{(["reality", "narrative", "belief"] as Layer[]).map((k) => (
{LAYERS.map((k) => (

Comment on lines +3 to +7
export function MetricsPanel(props: {
narratives: Array<{ id: string; label: string; summary: string; metrics: { velocity: number } }>;
divergence: Array<{ narrativeId: string; claimId: string; divergenceScore: number }>;
onExplain: (narrativeId: string) => void;
}) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

For better readability and reusability, it's a good practice to define component props in a separate interface or type alias. The complex inline types for narratives and divergence would also benefit from being extracted into their own named types.

type Narrative = {
  id: string;
  label: string;
  summary: string;
  metrics: { velocity: number };
};

type Divergence = {
  narrativeId: string;
  claimId: string;
  divergenceScore: number;
};

interface MetricsPanelProps {
  narratives: Narrative[];
  divergence: Divergence[];
  onExplain: (narrativeId: string) => void;
}

export function MetricsPanel(props: MetricsPanelProps) {

<h3 className="text-sm font-semibold">Divergence signals</h3>
<div className="mt-3 grid gap-2">
{props.divergence.map((d, idx) => (
<div key={`${d.narrativeId}-${idx}`} className="p-3 rounded-xl border">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using an array index as part of the key is an anti-pattern if the list can be reordered. Since each divergence item has a narrativeId and a claimId, a composite key from these two properties would be more stable and is preferred over using the index.

Suggested change
<div key={`${d.narrativeId}-${idx}`} className="p-3 rounded-xl border">
<div key={`${d.narrativeId}-${d.claimId}`} className="p-3 rounded-xl border">

items: RejectionItem[];
};

export function RejectionReportPanel({ report }: { report: RejectionReport }) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

For consistency and clarity, it's better to define component props in a named interface.

interface RejectionReportPanelProps {
  report: RejectionReport;
}

export function RejectionReportPanel({ report }: RejectionReportPanelProps) {

{it.errors?.length ? (
<ul className="mt-2 list-disc pl-5 text-xs">
{it.errors.map((e, idx) => (
<li key={idx}>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using an array index as a key is an anti-pattern in React when the list of items can change. This can lead to incorrect component state and rendering issues. A more stable key can be created by combining the index with other properties of the error object, like code and instancePath.

Suggested change
<li key={idx}>
<li key={`${idx}-${e.code}-${e.instancePath ?? ''}`}>

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

Walkthrough

This PR extends the CogBattleStorage interface with snapshot retrieval and storage methods, introduces a new Cognitive Battlespace page with supporting React components for layer toggling, metrics display, divergence tracking, and error reporting, and updates TypeScript configuration to exclude node_modules and test files.

Changes

Cohort / File(s) Summary
Storage Layer
packages/summit-cogbattlespace/src/storage.ts, packages/summit-cogbattlespace/tsconfig.json
Added getCurrentEntity and putLaneSnapshot methods to CogBattleStorage interface and InMemoryCogBattleStorage implementation for snapshot data persistence. Updated TypeScript config to exclude node_modules and test files.
UI Components
packages/summit-ui/src/components/cogbattlespace/ExplainDrawer.tsx, packages/summit-ui/src/components/cogbattlespace/LayerToggle.tsx, packages/summit-ui/src/components/cogbattlespace/MetricsPanel.tsx, packages/summit-ui/src/components/cogbattlespace/RejectionReportPanel.tsx
Created four new React components: ExplainDrawer (overlay panel with title/body/disclaimers), LayerToggle (three-button toggle for reality/narrative/belief layers), MetricsPanel (displays narratives with velocity metrics and divergence signals), and RejectionReportPanel (displays rejection errors with detailed error information).
Page Component
packages/summit-ui/src/pages/cogbattlespace/index.tsx
Introduced CognitiveBattlespacePage that orchestrates layer state management, data fetching, narrative/divergence display, and coordinates child components (MetricsPanel, ExplainDrawer) with explain handlers and dynamic drawer content.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A battlespace of narratives, now in sight,
With layers toggle—reality, belief, recite,
Metrics dance and drawers explain with grace,
Cognitive echoes fill this bounded space!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is significantly incomplete. Multiple required sections from the template are missing or unfilled, including Risk & Surface (required), Assumption Ledger, Execution Governor & Customer Impact, Evidence Bundle, Investigation Trust Doctrine Checklist, Security Impact, and Green CI Contract Checklist. Complete all required template sections: select Risk Level and Surface Area, fill Assumption Ledger, Execution Governor, Evidence Bundle with test/screenshot evidence, Investigation Trust Doctrine items, Security Impact assessment, and Green CI Contract Checklist items before merge.
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main change: adding Cognitive Battlespace UI components and a page, which aligns with the majority of changes in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cogbattlespace-ui-14338170707411774589

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9377367122

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


type RejectionItem = {
opId: string;
status: "ACCEPTED" | "REJECTED";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Align rejection statuses with writeset outcomes

The panel hardcodes item status to "ACCEPTED" | "REJECTED", but the only writeset producer in this repo (applyCogWriteSet in packages/summit-cogbattlespace/src/writeset/firewall.ts) emits statuses like APPLIED, MERGED, PROMOTED, NOOP_ENTITY, etc. When this component is fed real API data, successful operations will not map correctly and will be rendered with the non-accepted styling branch, misreporting valid writes as failures.

Useful? React with 👍 / 👎.

type RejectionReport = {
ok: boolean;
writesetId: string;
summary: { receivedOps: number; acceptedOps: number; rejectedOps: number };
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Align summary counters with writeset report fields

The summary shape expects acceptedOps, but the backend writeset report model provides granular counters (appliedOps, mergedOps, promotedOps, etc.) and no acceptedOps field. If this component is connected to the existing API contract, the accepted count will render as undefined, which breaks the report header and obscures operator outcomes.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
packages/summit-ui/src/pages/cogbattlespace/index.tsx (2)

41-41: Remove unnecessary async keyword.

The explain function is marked async but contains no await expressions. The comment suggests it will be wired to an endpoint later, so this can stay if that's imminent; otherwise remove to avoid confusion.

-  const explain = async (narrativeId: string) => {
+  const explain = (narrativeId: string) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/summit-ui/src/pages/cogbattlespace/index.tsx` at line 41, The
explain function is declared async but contains no await; remove the unnecessary
async keyword from the explain declaration (function explain(narrativeId:
string) { ... }) to avoid misleading callers, or if you intend to call async
operations soon, add the awaited logic inside explain (e.g., await fetch/axios)
instead; locate the explain function in this module to update its signature
accordingly.

29-34: Add error handling and loading state for data fetching.

The current implementation silently fails if fetches error, and provides no loading feedback. Consider adding:

  • A loading state to show a spinner/skeleton
  • Error handling with user feedback
  • Cleanup to prevent state updates on unmounted components
♻️ Proposed improvements
+  const [loading, setLoading] = useState(true);
+  const [error, setError] = useState<string | null>(null);

   React.useEffect(() => {
+    let cancelled = false;
     (async () => {
-      setNarratives(await fetchTopNarratives());
-      setDivergence(await fetchDivergence());
+      try {
+        const [narr, div] = await Promise.all([fetchTopNarratives(), fetchDivergence()]);
+        if (!cancelled) {
+          setNarratives(narr);
+          setDivergence(div);
+        }
+      } catch (e) {
+        if (!cancelled) setError("Failed to load battlespace data");
+      } finally {
+        if (!cancelled) setLoading(false);
+      }
     })();
+    return () => { cancelled = true; };
   }, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/summit-ui/src/pages/cogbattlespace/index.tsx` around lines 29 - 34,
Wrap the data fetching inside useEffect with a loading and error state: add
local state variables like isLoading and fetchError and set isLoading=true
before the async calls; use try/catch around the await fetchTopNarratives() and
fetchDivergence() calls to set fetchError on failure and show user feedback, and
finally set isLoading=false in a finally block. Prevent state updates after
unmount by using an isMounted flag (or AbortController) inside the useEffect and
check it before calling setNarratives and setDivergence. Update the component
render to show a spinner/skeleton when isLoading is true and an error message
when fetchError is set.
packages/summit-ui/src/components/cogbattlespace/ExplainDrawer.tsx (1)

13-34: Consider adding accessibility features for the modal drawer.

The drawer overlay is missing standard accessibility features for modal dialogs:

  • No role="dialog" or aria-modal="true" attributes
  • No focus trap (users can tab outside the drawer)
  • No keyboard listener for Escape to close
  • The backdrop click doesn't close the drawer

These can be addressed iteratively, but consider adding at minimum:

♿ Proposed accessibility improvements
-    <div className="fixed inset-0 bg-black/40 flex justify-end">
-      <div className="w-full max-w-xl h-full bg-white p-6 overflow-auto">
+    <div
+      className="fixed inset-0 bg-black/40 flex justify-end"
+      onClick={props.onClose}
+      onKeyDown={(e) => e.key === "Escape" && props.onClose()}
+    >
+      <div
+        role="dialog"
+        aria-modal="true"
+        aria-labelledby="drawer-title"
+        className="w-full max-w-xl h-full bg-white p-6 overflow-auto"
+        onClick={(e) => e.stopPropagation()}
+      >
         <div className="flex items-center justify-between">
-          <h2 className="text-xl font-semibold">{props.title}</h2>
+          <h2 id="drawer-title" className="text-xl font-semibold">{props.title}</h2>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/summit-ui/src/components/cogbattlespace/ExplainDrawer.tsx` around
lines 13 - 34, The ExplainDrawer component's modal markup lacks accessibility:
add role="dialog" and aria-modal="true" to the container element (the overlay or
inner panel), ensure backdrop clicks call props.onClose (attach onClick to the
overlay but stopPropagation on the panel), add a keyboard listener in the
ExplainDrawer component to call props.onClose on Escape, and implement a focus
trap / initial focus behavior so focus moves into the drawer on open and cannot
tab out (or use a small focus-trap utility or hook inside ExplainDrawer); ensure
the panel stops click propagation so internal clicks don't close the drawer and
return focus to the prior element on close.
packages/summit-ui/src/components/cogbattlespace/RejectionReportPanel.tsx (1)

3-24: Consider exporting types for reuse.

The RejectionError, RejectionItem, and RejectionReport types are useful for API response typing and other components. Exporting them would improve reusability.

♻️ Proposed change
-type RejectionError = {
+export type RejectionError = {
   code: string;
   message: string;
   instancePath?: string;
   schemaPath?: string;
 };

-type RejectionItem = {
+export type RejectionItem = {
   opId: string;
   status: "ACCEPTED" | "REJECTED";
   entityType?: string;
   domain?: string;
   action?: string;
   errors?: RejectionError[];
 };

-type RejectionReport = {
+export type RejectionReport = {
   ok: boolean;
   writesetId: string;
   summary: { receivedOps: number; acceptedOps: number; rejectedOps: number };
   items: RejectionItem[];
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/summit-ui/src/components/cogbattlespace/RejectionReportPanel.tsx`
around lines 3 - 24, Export the three types so they can be reused: add the
export keyword to RejectionError, RejectionItem, and RejectionReport
declarations (e.g., export type RejectionError = {...}) and update any
consumers/import sites to import these named types; also export them from the
module's public API (barrel/index) if one exists so other components can import
them easily.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/summit-cogbattlespace/src/storage.ts`:
- Around line 30-31: Update the storage interface so it uses the concrete
EntitySnapshot type instead of any: change getCurrentEntity to return
Promise<EntitySnapshot | null> and change putLaneSnapshot to accept data:
EntitySnapshot; then update call sites (notably reconcileEntityTrustAware and
the place casting with "as any" in firewall.ts) to stop using casts and work
with the stronger types, adjusting imports to reference the EntitySnapshot type
where needed.

In `@packages/summit-ui/src/components/cogbattlespace/MetricsPanel.tsx`:
- Line 17: Guard against undefined/null before calling .toFixed() on numeric
fields: update the render in MetricsPanel.tsx where n.metrics.velocity and
d.divergenceScore are used (search for the symbols n.metrics.velocity and
d.divergenceScore) to first check for a valid number (e.g., Number.isFinite(...)
or nullish-coalesce to a default like 0) and only call .toFixed(2) when the
value is numeric; otherwise render a safe fallback (such as "-" or "0.00") so no
runtime error occurs.

In `@packages/summit-ui/src/pages/cogbattlespace/index.tsx`:
- Around line 21-22: Replace the loose any[] state with concrete types: define
interfaces (e.g., Narrative and Divergence) that match the API/props shape or
import/reuse the existing types used by MetricsPanel, then update the useState
generics for narratives and divergence to use those types
(useState<Narrative[]>() / useState<Divergence[]>()), adjust
setNarratives/setDivergence usages to satisfy the new types, and add any
necessary imports for the types at the top of the file (or export the types from
MetricsPanel and consume them here).

---

Nitpick comments:
In `@packages/summit-ui/src/components/cogbattlespace/ExplainDrawer.tsx`:
- Around line 13-34: The ExplainDrawer component's modal markup lacks
accessibility: add role="dialog" and aria-modal="true" to the container element
(the overlay or inner panel), ensure backdrop clicks call props.onClose (attach
onClick to the overlay but stopPropagation on the panel), add a keyboard
listener in the ExplainDrawer component to call props.onClose on Escape, and
implement a focus trap / initial focus behavior so focus moves into the drawer
on open and cannot tab out (or use a small focus-trap utility or hook inside
ExplainDrawer); ensure the panel stops click propagation so internal clicks
don't close the drawer and return focus to the prior element on close.

In `@packages/summit-ui/src/components/cogbattlespace/RejectionReportPanel.tsx`:
- Around line 3-24: Export the three types so they can be reused: add the export
keyword to RejectionError, RejectionItem, and RejectionReport declarations
(e.g., export type RejectionError = {...}) and update any consumers/import sites
to import these named types; also export them from the module's public API
(barrel/index) if one exists so other components can import them easily.

In `@packages/summit-ui/src/pages/cogbattlespace/index.tsx`:
- Line 41: The explain function is declared async but contains no await; remove
the unnecessary async keyword from the explain declaration (function
explain(narrativeId: string) { ... }) to avoid misleading callers, or if you
intend to call async operations soon, add the awaited logic inside explain
(e.g., await fetch/axios) instead; locate the explain function in this module to
update its signature accordingly.
- Around line 29-34: Wrap the data fetching inside useEffect with a loading and
error state: add local state variables like isLoading and fetchError and set
isLoading=true before the async calls; use try/catch around the await
fetchTopNarratives() and fetchDivergence() calls to set fetchError on failure
and show user feedback, and finally set isLoading=false in a finally block.
Prevent state updates after unmount by using an isMounted flag (or
AbortController) inside the useEffect and check it before calling setNarratives
and setDivergence. Update the component render to show a spinner/skeleton when
isLoading is true and an error message when fetchError is set.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 00c156c6-e280-42cb-a7de-7eafbaead762

📥 Commits

Reviewing files that changed from the base of the PR and between 12cad4a and 9377367.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • packages/summit-cogbattlespace/src/storage.ts
  • packages/summit-cogbattlespace/tsconfig.json
  • packages/summit-ui/src/components/cogbattlespace/ExplainDrawer.tsx
  • packages/summit-ui/src/components/cogbattlespace/LayerToggle.tsx
  • packages/summit-ui/src/components/cogbattlespace/MetricsPanel.tsx
  • packages/summit-ui/src/components/cogbattlespace/RejectionReportPanel.tsx
  • packages/summit-ui/src/pages/cogbattlespace/index.tsx

Comment on lines +30 to +31
getCurrentEntity(entityType: string, fingerprint: string): Promise<any>;
putLaneSnapshot(entityType: string, fingerprint: string, data: any): Promise<void>;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify call sites and check whether callers currently depend on untyped `any` behavior.
rg -nP --type=ts -C2 '\b(getCurrentEntity|putLaneSnapshot)\s*\('

Repository: BrianCLong/summit

Length of output: 2420


🏁 Script executed:

#!/bin/bash
# Find the reconcileEntityTrustAware function and see what type it expects for 'current'
rg -nP --type=ts 'reconcileEntityTrustAware' -A5 -B2

Repository: BrianCLong/summit

Length of output: 2463


🏁 Script executed:

#!/bin/bash
# Check if there are any other imports or implementations of CogBattleStorage interface
rg -nP --type=ts 'implements CogBattleStorage|CogBattleStorage' -B1 -A3

Repository: BrianCLong/summit

Length of output: 7681


🏁 Script executed:

#!/bin/bash
# Look at the context around the firewall.ts call sites to understand type expectations
sed -n '190,240p' packages/summit-cogbattlespace/src/writeset/firewall.ts

Repository: BrianCLong/summit

Length of output: 2514


🏁 Script executed:

#!/bin/bash
# Find EntitySnapshot type definition
rg -nP --type=ts 'type EntitySnapshot|interface EntitySnapshot' -B2 -A5

Repository: BrianCLong/summit

Length of output: 759


🏁 Script executed:

#!/bin/bash
# Check what is imported in reconciler.ts to understand type context
head -30 packages/summit-cogbattlespace/src/writeset/reconcile/reconciler.ts

Repository: BrianCLong/summit

Length of output: 1272


Replace any with concrete EntitySnapshot type for storage API contract.

The interface returns and accepts any, which bypasses strict type checking. getCurrentEntity() should return Promise<EntitySnapshot | null> (matching what reconcileEntityTrustAware() expects), and putLaneSnapshot() should accept EntitySnapshot. The current workaround as any cast on line 201 of firewall.ts is a symptom of this weak typing.

Recommended fix
+ import type { EntitySnapshot } from "./writeset/reconcile/types";

 export interface CogBattleStorage {
-  getCurrentEntity(entityType: string, fingerprint: string): Promise<any>;
-  putLaneSnapshot(entityType: string, fingerprint: string, data: any): Promise<void>;
+  getCurrentEntity(entityType: string, fingerprint: string): Promise<EntitySnapshot | null>;
+  putLaneSnapshot(entityType: string, fingerprint: string, data: EntitySnapshot): Promise<void>;
 }

 export class InMemoryCogBattleStorage implements CogBattleStorage {
-  private laneSnapshots = new Map<string, Map<string, any>>();
+  private laneSnapshots = new Map<string, Map<string, EntitySnapshot>>();

-  async getCurrentEntity(entityType: string, fingerprint: string): Promise<any> {
+  async getCurrentEntity(entityType: string, fingerprint: string): Promise<EntitySnapshot | null> {
     const typeMap = this.laneSnapshots.get(entityType);
     if (!typeMap) return null;
     return typeMap.get(fingerprint) ?? null;
   }

-  async putLaneSnapshot(entityType: string, fingerprint: string, data: any): Promise<void> {
+  async putLaneSnapshot(entityType: string, fingerprint: string, data: EntitySnapshot): Promise<void> {
     let typeMap = this.laneSnapshots.get(entityType);
     if (!typeMap) {
-      typeMap = new Map();
+      typeMap = new Map<string, EntitySnapshot>();
       this.laneSnapshots.set(entityType, typeMap);
     }
     typeMap.set(fingerprint, data);
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/summit-cogbattlespace/src/storage.ts` around lines 30 - 31, Update
the storage interface so it uses the concrete EntitySnapshot type instead of
any: change getCurrentEntity to return Promise<EntitySnapshot | null> and change
putLaneSnapshot to accept data: EntitySnapshot; then update call sites (notably
reconcileEntityTrustAware and the place casting with "as any" in firewall.ts) to
stop using casts and work with the stronger types, adjusting imports to
reference the EntitySnapshot type where needed.

<div key={n.id} className="p-3 rounded-xl border">
<div className="flex items-center justify-between">
<div className="font-medium">{n.label}</div>
<div className="text-xs opacity-70">v={n.metrics.velocity.toFixed(2)}</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard against undefined values before calling .toFixed().

If n.metrics.velocity is undefined or null, calling .toFixed(2) will throw a runtime error. Same applies to d.divergenceScore on line 39.

🛡️ Proposed defensive fix
-                <div className="text-xs opacity-70">v={n.metrics.velocity.toFixed(2)}</div>
+                <div className="text-xs opacity-70">v={(n.metrics.velocity ?? 0).toFixed(2)}</div>

And for line 39:

-              <div className="text-xs opacity-70">score={d.divergenceScore.toFixed(2)}</div>
+              <div className="text-xs opacity-70">score={(d.divergenceScore ?? 0).toFixed(2)}</div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="text-xs opacity-70">v={n.metrics.velocity.toFixed(2)}</div>
<div className="text-xs opacity-70">v={(n.metrics.velocity ?? 0).toFixed(2)}</div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/summit-ui/src/components/cogbattlespace/MetricsPanel.tsx` at line
17, Guard against undefined/null before calling .toFixed() on numeric fields:
update the render in MetricsPanel.tsx where n.metrics.velocity and
d.divergenceScore are used (search for the symbols n.metrics.velocity and
d.divergenceScore) to first check for a valid number (e.g., Number.isFinite(...)
or nullish-coalesce to a default like 0) and only call .toFixed(2) when the
value is numeric; otherwise render a safe fallback (such as "-" or "0.00") so no
runtime error occurs.

Comment on lines +21 to +22
const [narratives, setNarratives] = useState<any[]>([]);
const [divergence, setDivergence] = useState<any[]>([]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Avoid any[] - define proper types for state.

Using any[] defeats TypeScript's purpose. Define types matching your API response or reuse the types from MetricsPanel props.

♻️ Proposed type definitions
+type Narrative = {
+  id: string;
+  label: string;
+  summary: string;
+  metrics: { velocity: number };
+};
+
+type DivergenceSignal = {
+  narrativeId: string;
+  claimId: string;
+  divergenceScore: number;
+};
+
 export default function CognitiveBattlespacePage() {
   // ...
-  const [narratives, setNarratives] = useState<any[]>([]);
-  const [divergence, setDivergence] = useState<any[]>([]);
+  const [narratives, setNarratives] = useState<Narrative[]>([]);
+  const [divergence, setDivergence] = useState<DivergenceSignal[]>([]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [narratives, setNarratives] = useState<any[]>([]);
const [divergence, setDivergence] = useState<any[]>([]);
type Narrative = {
id: string;
label: string;
summary: string;
metrics: { velocity: number };
};
type DivergenceSignal = {
narrativeId: string;
claimId: string;
divergenceScore: number;
};
export default function CognitiveBattlespacePage() {
const [narratives, setNarratives] = useState<Narrative[]>([]);
const [divergence, setDivergence] = useState<DivergenceSignal[]>([]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/summit-ui/src/pages/cogbattlespace/index.tsx` around lines 21 - 22,
Replace the loose any[] state with concrete types: define interfaces (e.g.,
Narrative and Divergence) that match the API/props shape or import/reuse the
existing types used by MetricsPanel, then update the useState generics for
narratives and divergence to use those types (useState<Narrative[]>() /
useState<Divergence[]>()), adjust setNarratives/setDivergence usages to satisfy
the new types, and add any necessary imports for the types at the top of the
file (or export the types from MetricsPanel and consume them here).

@github-actions
Copy link
Copy Markdown
Contributor

❌ Lint Gate Results

ESLint

  • Errors: 0
  • Warnings: 0
  • Status: ✅ Pass

Code Quality

  • console.log statements: 37054 ❌
  • TODO/FIXME in changes: 0 ✅

❌ Lint gate failed

Please fix the linter errors and warnings before merging.

About Lint Gate

This gate enforces zero linter warnings/errors and production code quality:

  • ESLint must pass with zero errors and warnings
  • No console.log statements in production code
  • Prettier formatting enforced
  • TODO/FIXME comments tracked (warning only)

@github-actions
Copy link
Copy Markdown
Contributor

❌ TypeScript Gate Results

Type Safety

Sample Errors


About TypeScript Gate

This gate enforces type safety:

  • Zero TypeScript compilation errors
  • Strict mode enabled in all tsconfig files
  • noEmitOnError: true (build fails on errors)
  • Limited use of @ts-ignore (<10) and @ts-expect-error (<50)

To fix TypeScript errors:

  1. Run pnpm typecheck locally
  2. Fix type errors (don't use @ts-ignore)
  3. If error is unavoidable, use @ts-expect-error with explanation

@BrianCLong
Copy link
Copy Markdown
Owner Author

Closing in favor of #22241, which carries the Cognitive Battlespace UI slice on top of the converged CI/governance baseline.

@BrianCLong BrianCLong closed this Mar 29, 2026
@google-labs-jules
Copy link
Copy Markdown
Contributor

Closing in favor of #22241, which carries the Cognitive Battlespace UI slice on top of the converged CI/governance baseline.

Understood. Acknowledging that this work is superseded by #22241 and stopping work on this task.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant