Skip to content

Only credit the learnings the assistant says it used#141

Merged
yyiilluu merged 1 commit into
ReflexioAI:mainfrom
wenchanghan:codex/citation-labeling-precision
Jul 21, 2026
Merged

Only credit the learnings the assistant says it used#141
yyiilluu merged 1 commit into
ReflexioAI:mainfrom
wenchanghan:codex/citation-labeling-precision

Conversation

@wenchanghan

@wenchanghan wenchanghan commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Customer impact

When several learnings are available, claude-smart no longer gives the assistant an Applied footer prefilled with all of them. The assistant now sees a one-learning format example and is told to include only the learnings that materially changed its response.

This removes prompt pressure to credit unrelated learnings while keeping the existing Learning applied wording, clickable links, and dashboard behavior.

Before

If learnings A, B, and C were retrieved and only A helped, the prompt still told the assistant to copy a footer containing A, B, and C.

After

The prompt shows A as the format example, keeps B and C available to link, and tells the assistant to add only the learnings that materially changed the response. Legitimate multi-learning attribution remains supported.

Scope

  • No dashboard or UI changes
  • No terminology, parser, resolver, schema, or counting changes
  • No screenshots because no visual surface changed

Validation

  • Full Python suite: 694 passed, 1 skipped
  • Focused renderer and Codex integration tests: 104 passed
  • Diff check passed
  • Independent standards and customer-value reviews found no issues

Supersedes #140.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR changes citation terminology and metrics across the citation pipeline and dashboard, preserves unresolved citation IDs, adds injected-learning counts, and updates session, dashboard, skills, and preferences views to display cited-learning statistics.

Changes

Citation pipeline

Layer / File(s) Summary
Citation resolution and marker instructions
plugin/src/claude_smart/events/stop.py, plugin/src/claude_smart/cs_cite.py, plugin/src/claude_smart/context_format.py, tests/*
Citation markers use “learning cited” wording, qualifying-item guidance is updated, resolved citations are deduplicated, and unresolved IDs are retained in assistant records.
Session contracts and citation statistics
plugin/dashboard/lib/types.ts, plugin/dashboard/lib/session-reader.ts, plugin/src/claude_smart/state.py
Session records expose unresolved citations, citation counts, cited-learning counts, and unique injected-learning counts. Rule statistics use citation counters and timestamps.
Cited statistics and badges
plugin/dashboard/app/api/rules/cited/route.ts, plugin/dashboard/app/dashboard/page.tsx, plugin/dashboard/app/skills/page.tsx, plugin/dashboard/app/preferences/page.tsx, plugin/dashboard/components/common/*
Dashboard data loading, sorting, labels, and badges switch from applied metrics to cited metrics and add injected-learning indicators.
Session citation diagnostics
plugin/dashboard/app/sessions/page.tsx, plugin/dashboard/app/sessions/[sessionId]/page.tsx
Session views show injected and cited badges and render warnings for unresolved citations.
Citation behavior documentation
README.md
The citation marker example and attribution explanation are updated.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Assistant
  participant StopHook
  participant SessionReader
  participant Dashboard
  Assistant->>StopHook: emit citation marker
  StopHook->>SessionReader: store cited_items and unresolved_citations
  SessionReader->>Dashboard: provide citation and injection metrics
  Dashboard-->>Dashboard: render cited and unresolved-citation status
Loading

Possibly related PRs

Suggested reviewers: yyiilluu, yilu331

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the core change: only giving credit for learnings the assistant explicitly cites or uses.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugin/src/claude_smart/events/stop.py (1)

299-321: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Deduplicate the resolved registry entry, not only the raw citation token.

Different aliases such as an exact ID and a stale fingerprint can resolve to the same entry, producing duplicate cited_items and inflated applied counts. Normalize raw tokens and track the resolved entry’s canonical ID.

Proposed fix
-    seen: set[str] = set()
+    seen_citations: set[str] = set()
+    seen_entries: set[str] = set()
     resolved: list[dict[str, Any]] = []
     unresolved: list[str] = []
     for cid in cited_ids:
-        if cid in seen:
+        normalized_cid = cid.casefold()
+        if normalized_cid in seen_citations:
             continue
-        seen.add(cid)
+        seen_citations.add(normalized_cid)
         entry = _registry_entry_for_citation(registry, cid)
         if not entry:
             unresolved.append(cid)
             continue
+        canonical_id = str(entry.get("id") or cid).casefold()
+        if canonical_id in seen_entries:
+            continue
+        seen_entries.add(canonical_id)
         item: dict[str, Any] = {
🤖 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 `@plugin/src/claude_smart/events/stop.py` around lines 299 - 321, Update the
citation loop in the stop-event resolution logic to normalize each raw token
before lookup and deduplicate using the resolved registry entry’s canonical ID,
rather than only the raw citation token. Ensure aliases such as an exact ID and
stale fingerprint produce one resolved item and do not inflate applied counts,
while preserving unresolved-token handling.
🧹 Nitpick comments (1)
plugin/dashboard/lib/session-reader.ts (1)

438-449: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Parallelize I/O for reading session and injected data.

The addition of await countUniqueInjected(sessionId) inside this for loop introduces a second sequential file read per session. To improve dashboard load times without risking file-descriptor limits (which could happen if we parallelized all sessions at once), you can read the two files for each session concurrently.

⚡ Proposed refactor
-    const records = await readJsonl(fullPath).catch(() => []);
+    const [records, injectedLearningCount] = await Promise.all([
+      readJsonl(fullPath).catch(() => []),
+      countUniqueInjected(sessionId),
+    ]);
     const {
       turns,
       publishedUpTo,
       learningInteractionCount,
       lastTs,
       firstTs,
       preview,
       host,
       requestIds,
     } = foldTurns(records);
-    const injectedLearningCount = await countUniqueInjected(sessionId);
🤖 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 `@plugin/dashboard/lib/session-reader.ts` around lines 438 - 449, Within the
session-processing loop, start the session JSONL read and
countUniqueInjected(sessionId) concurrently, then await both results together
before calling foldTurns. Preserve the existing fallback of [] for readJsonl
failures and keep concurrency scoped to each session rather than parallelizing
across sessions.
🤖 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 `@plugin/src/claude_smart/context_format.py`:
- Around line 353-357: Update the available-memory label in the context
formatting flow around marker_parts so it does not call every linked entry
“Qualifying.” Use neutral wording that reflects these are linked titles pending
the model’s relevance decision, while preserving the existing joined list and
punctuation.

---

Outside diff comments:
In `@plugin/src/claude_smart/events/stop.py`:
- Around line 299-321: Update the citation loop in the stop-event resolution
logic to normalize each raw token before lookup and deduplicate using the
resolved registry entry’s canonical ID, rather than only the raw citation token.
Ensure aliases such as an exact ID and stale fingerprint produce one resolved
item and do not inflate applied counts, while preserving unresolved-token
handling.

---

Nitpick comments:
In `@plugin/dashboard/lib/session-reader.ts`:
- Around line 438-449: Within the session-processing loop, start the session
JSONL read and countUniqueInjected(sessionId) concurrently, then await both
results together before calling foldTurns. Preserve the existing fallback of []
for readJsonl failures and keep concurrency scoped to each session rather than
parallelizing across sessions.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 96617689-7578-47ae-9a25-835615163504

📥 Commits

Reviewing files that changed from the base of the PR and between 045d8b3 and ac57191.

📒 Files selected for processing (11)
  • plugin/dashboard/app/dashboard/page.tsx
  • plugin/dashboard/app/sessions/[sessionId]/page.tsx
  • plugin/dashboard/app/sessions/page.tsx
  • plugin/dashboard/components/common/injected-badge.tsx
  • plugin/dashboard/lib/session-reader.ts
  • plugin/dashboard/lib/types.ts
  • plugin/src/claude_smart/context_format.py
  • plugin/src/claude_smart/events/stop.py
  • plugin/src/claude_smart/state.py
  • tests/test_context_format.py
  • tests/test_events.py

Comment thread plugin/src/claude_smart/context_format.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugin/dashboard/lib/session-reader.ts (1)

450-485: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Parallelize session file reads.

Currently, listSessions() iterates over the directory entries sequentially, waiting for each session's file reads to complete before starting the next. As noted in the PR objectives, mapping the entries to promises and awaiting them concurrently via Promise.all() will significantly improve the dashboard's load performance.

⚡ Proposed fix
-  const summaries: SessionSummary[] = [];
-  for (const entry of entries) {
-    if (!entry.endsWith(".jsonl")) continue;
-    if (entry.endsWith(".injected.jsonl")) continue;
-    const fullPath = path.join(dir, entry);
-    const sessionId = entry.replace(/\.jsonl$/, "");
-    const [records, injectedLearningCount] = await Promise.all([
-      readJsonl(fullPath).catch(() => []),
-      countUniqueInjected(sessionId),
-    ]);
-    const {
-      turns,
-      publishedUpTo,
-      learningInteractionCount,
-      appliedLearningCount,
-      lastTs,
-      firstTs,
-      preview,
-      host,
-      requestIds,
-    } = foldTurns(records);
-    summaries.push({
-      session_id: sessionId,
-      turn_count: turns.length,
-      learning_interaction_count: learningInteractionCount,
-      applied_learning_count: appliedLearningCount,
-      injected_learning_count: injectedLearningCount,
-      last_activity: lastTs,
-      first_activity: firstTs,
-      published_up_to: publishedUpTo,
-      preview,
-      source: "local",
-      host,
-      request_ids: requestIds,
-    });
-  }
+  const sessionPromises = entries
+    .filter((entry) => entry.endsWith(".jsonl") && !entry.endsWith(".injected.jsonl"))
+    .map(async (entry): Promise<SessionSummary> => {
+      const fullPath = path.join(dir, entry);
+      const sessionId = entry.replace(/\.jsonl$/, "");
+      const [records, injectedLearningCount] = await Promise.all([
+        readJsonl(fullPath).catch(() => []),
+        countUniqueInjected(sessionId),
+      ]);
+      const {
+        turns,
+        publishedUpTo,
+        learningInteractionCount,
+        appliedLearningCount,
+        lastTs,
+        firstTs,
+        preview,
+        host,
+        requestIds,
+      } = foldTurns(records);
+      return {
+        session_id: sessionId,
+        turn_count: turns.length,
+        learning_interaction_count: learningInteractionCount,
+        applied_learning_count: appliedLearningCount,
+        injected_learning_count: injectedLearningCount,
+        last_activity: lastTs,
+        first_activity: firstTs,
+        published_up_to: publishedUpTo,
+        preview,
+        source: "local",
+        host,
+        request_ids: requestIds,
+      };
+    });
+
+  const summaries = await Promise.all(sessionPromises);
🤖 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 `@plugin/dashboard/lib/session-reader.ts` around lines 450 - 485, Update
listSessions() to map eligible session entries to promises and await them with
Promise.all(), so readJsonl() and countUniqueInjected() for all sessions run
concurrently. Preserve the existing filtering, foldTurns() processing, and
SessionSummary fields, excluding invalid or injected entries as before.
🤖 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.

Outside diff comments:
In `@plugin/dashboard/lib/session-reader.ts`:
- Around line 450-485: Update listSessions() to map eligible session entries to
promises and await them with Promise.all(), so readJsonl() and
countUniqueInjected() for all sessions run concurrently. Preserve the existing
filtering, foldTurns() processing, and SessionSummary fields, excluding invalid
or injected entries as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7cc57564-3bb0-4b10-9e71-a9c75635a292

📥 Commits

Reviewing files that changed from the base of the PR and between ac57191 and b28d9e1.

📒 Files selected for processing (10)
  • plugin/dashboard/app/dashboard/page.tsx
  • plugin/dashboard/app/sessions/[sessionId]/page.tsx
  • plugin/dashboard/app/sessions/page.tsx
  • plugin/dashboard/components/common/learnings-badge.tsx
  • plugin/dashboard/lib/session-reader.ts
  • plugin/dashboard/lib/types.ts
  • plugin/src/claude_smart/context_format.py
  • plugin/src/claude_smart/events/stop.py
  • tests/test_context_format.py
  • tests/test_events.py
🚧 Files skipped from review as they are similar to previous changes (6)
  • plugin/dashboard/app/sessions/page.tsx
  • plugin/dashboard/app/sessions/[sessionId]/page.tsx
  • plugin/dashboard/app/dashboard/page.tsx
  • plugin/dashboard/lib/types.ts
  • plugin/src/claude_smart/context_format.py
  • tests/test_context_format.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
plugin/dashboard/lib/session-reader.ts (1)

450-485: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Read sessions concurrently to avoid N+1 file I/O latency.

Both listSessions and listCitedRules iterate over the session entries sequentially, awaiting each file read before moving to the next. For larger numbers of sessions, this sequential processing significantly increases dashboard load times. You can use Promise.all to read all valid entries concurrently before aggregating them.

  • plugin/dashboard/lib/session-reader.ts#L450-L485: Replace the for loop in listSessions with await Promise.all(entries.filter(...).map(async (entry) => ...)).
  • plugin/dashboard/lib/session-reader.ts#L242-L293: Apply the same parallelization pattern in listCitedRules to concurrently fetch the records arrays, then flat-map or reduce them into the stats map.
🤖 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 `@plugin/dashboard/lib/session-reader.ts` around lines 450 - 485, Parallelize
session processing in listSessions by replacing its sequential loop with
Promise.all over valid entries, preserving each entry’s filtering, record
reading, counting, folding, and summary construction. Apply the same concurrent
record-fetch pattern in listCitedRules, then aggregate the fetched results into
the existing stats map. Affected sites: plugin/dashboard/lib/session-reader.ts
lines 450-485 (update listSessions); plugin/dashboard/lib/session-reader.ts
lines 242-293 (update listCitedRules).
🤖 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 `@plugin/dashboard/lib/session-reader.ts`:
- Around line 450-485: Parallelize session processing in listSessions by
replacing its sequential loop with Promise.all over valid entries, preserving
each entry’s filtering, record reading, counting, folding, and summary
construction. Apply the same concurrent record-fetch pattern in listCitedRules,
then aggregate the fetched results into the existing stats map. Affected sites:
plugin/dashboard/lib/session-reader.ts lines 450-485 (update listSessions);
plugin/dashboard/lib/session-reader.ts lines 242-293 (update listCitedRules).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8c110e82-b6c8-4fb1-84a1-3c3f96111112

📥 Commits

Reviewing files that changed from the base of the PR and between bc5ffec and 69a8420.

📒 Files selected for processing (18)
  • README.md
  • plugin/dashboard/app/api/rules/cited/route.ts
  • plugin/dashboard/app/dashboard/page.tsx
  • plugin/dashboard/app/preferences/page.tsx
  • plugin/dashboard/app/sessions/[sessionId]/page.tsx
  • plugin/dashboard/app/sessions/page.tsx
  • plugin/dashboard/app/skills/page.tsx
  • plugin/dashboard/components/common/cited-learnings-badge.tsx
  • plugin/dashboard/components/common/injected-badge.tsx
  • plugin/dashboard/components/common/learning-citation-badge.tsx
  • plugin/dashboard/lib/session-reader.ts
  • plugin/dashboard/lib/types.ts
  • plugin/src/claude_smart/cs_cite.py
  • tests/host_learning_harness.py
  • tests/test_codex_support.py
  • tests/test_context_format.py
  • tests/test_cs_cite.py
  • tests/test_events.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_context_format.py

@wenchanghan wenchanghan changed the title fix: make citation labels more precise and observable fix: report learning citations precisely and clearly Jul 18, 2026
@wenchanghan wenchanghan changed the title fix: report learning citations precisely and clearly fix: make learning references selective and trustworthy Jul 21, 2026
@wenchanghan wenchanghan changed the title fix: make learning references selective and trustworthy Show claude-smart's value without overstating it Jul 21, 2026
@wenchanghan wenchanghan changed the title Show claude-smart's value without overstating it Make claude-smart's help visible — without guessing Jul 21, 2026
@wenchanghan
wenchanghan force-pushed the codex/citation-labeling-precision branch from 451deae to 1dd3ad9 Compare July 21, 2026 07:51
@wenchanghan wenchanghan changed the title Make claude-smart's help visible — without guessing Make ‘Learning applied’ more trustworthy Jul 21, 2026
@wenchanghan
wenchanghan force-pushed the codex/citation-labeling-precision branch from 1dd3ad9 to 313054e Compare July 21, 2026 08:14
@wenchanghan wenchanghan changed the title Make ‘Learning applied’ more trustworthy Only credit the learnings the assistant says it used Jul 21, 2026
@yyiilluu
yyiilluu merged commit 951e081 into ReflexioAI:main Jul 21, 2026
9 checks passed
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