feat: publish citations + dashboard status fix + reflexio 0.2.17#10
feat: publish citations + dashboard status fix + reflexio 0.2.17#10
Conversation
…to 0.2.17 - Map local cited_items onto InteractionData.citations on the publish wire so reflexio can drive playbook/profile reflection from real Assistant citations. - Align dashboard with reflexio's lowercase-StrEnum + null-for-current wire shape: narrow PlaybookStatus/ProfileStatus types, treat null as CURRENT, consolidate the three duplicated statusLabel helpers into lib/status.ts. - Lower per-session interaction-cleanup thresholds (1000/500 -> 500/200) for shorter-lived claude-smart users. - Bump reflexio submodule to v0.2.17.
📝 WalkthroughWalkthroughConsolidates status handling into a shared dashboard status utility, narrows wire-format status types to lowercase, adds citation mapping into assistant turn payloads, adjusts backend cleanup defaults, extends citation tests, and updates the reflexio submodule pointer. Changes
Sequence Diagram(s)sequenceDiagram
participant Unpub as Unpublished Slice
participant Mapper as _to_wire_citations
participant Turn as Turn Object
participant Payload as Published Payload
Unpub->>Unpub: Gather cited_items (local)
Unpub->>Mapper: _to_wire_citations(cited_items)
Mapper->>Mapper: Validate each element (dict, kind allowed, non-empty real_id)
Mapper->>Mapper: Transform {id -> tag, real_id, kind, title}
Mapper-->>Unpub: Return mapped citations[]
alt citations non-empty
Unpub->>Turn: Attach citations field to assistant turn
else citations empty
Unpub->>Turn: Do not attach citations field
end
Turn->>Payload: Include turn in published payload
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
plugin/src/claude_smart/state.py (1)
37-37: Consider centralizing citation-kind constants.
_VALID_CITATION_KINDSduplicates the same domain contract enforced inplugin/src/claude_smart/cs_cite.py:rank_id. A shared constant/module would prevent future drift when new kinds are added.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/src/claude_smart/state.py` at line 37, The file defines _VALID_CITATION_KINDS which duplicates the contract enforced by cs_cite.rank_id; centralize this by moving the set of valid citation kinds into a single shared symbol (e.g., export VALID_CITATION_KINDS from a new or existing module such as claude_smart.constants or from plugin/src/claude_smart/cs_cite.py) and update state.py to import and use that shared VALID_CITATION_KINDS instead of its own _VALID_CITATION_KINDS to avoid drift when kinds change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@plugin/src/claude_smart/state.py`:
- Around line 196-206: The current block allows non-string truthy values for
real_id/tag/title to be emitted; ensure the emitted payload only contains
strings by validating and coercing values before appending: check real_id is a
non-empty string (reject or continue if not a str), coerce tag and title to
strings using safe conversion (defaulting to "" when None or non-primitive), and
keep the existing kind validation against _VALID_CITATION_KINDS; update the
logic around the real_id, tag, title variables used in the out.append call so
only validated/converted strings are written to the payload.
- Around line 264-266: The code computes citations from rec.get("cited_items")
for every turn, which can attach citations to user turns; restrict this to
Assistant turns only by checking the role before calling _to_wire_citations.
Update the logic around rec / turn (the block that sets citations and uses
_to_wire_citations) to first verify rec.get("role") == "assistant" (or
turn.get("role") == "assistant") and only then compute citations and set
turn["citations"]; leave other behavior unchanged.
In `@reflexio`:
- Line 1: The reflexio submodule is currently pinned to the unreleased main
commit 9143d1bf74584d0ecced69216c5b50773363c8e0 instead of the v0.2.17 release
commit a99ff1df33ed586125b277010aef1ab282fb1421; either update the submodule
pointer to the v0.2.17 tag/commit (reset the reflexio submodule to
a99ff1df33ed586125b277010aef1ab282fb1421, commit the change) so the repo
consumes the released wire-model, or if using main is intentional, add a clear
PR note and repository CHANGELOG entry stating that reflexio is intentionally
pinned to main (9143d1bf...) and accept the risk of unreleased changes.
---
Nitpick comments:
In `@plugin/src/claude_smart/state.py`:
- Line 37: The file defines _VALID_CITATION_KINDS which duplicates the contract
enforced by cs_cite.rank_id; centralize this by moving the set of valid citation
kinds into a single shared symbol (e.g., export VALID_CITATION_KINDS from a new
or existing module such as claude_smart.constants or from
plugin/src/claude_smart/cs_cite.py) and update state.py to import and use that
shared VALID_CITATION_KINDS instead of its own _VALID_CITATION_KINDS to avoid
drift when kinds change.
🪄 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: 1b432268-9a21-44cc-bcb1-54d47e069219
📒 Files selected for processing (10)
plugin/dashboard/app/dashboard/page.tsxplugin/dashboard/app/playbooks/[id]/page.tsxplugin/dashboard/app/playbooks/page.tsxplugin/dashboard/app/profiles/[id]/page.tsxplugin/dashboard/lib/status.tsplugin/dashboard/lib/types.tsplugin/scripts/backend-service.shplugin/src/claude_smart/state.pyreflexiotests/test_state.py
| @@ -1 +1 @@ | |||
| Subproject commit d9e000100157bcb59efd7390883e828b1598291e | |||
| Subproject commit 9143d1bf74584d0ecced69216c5b50773363c8e0 | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Confirm the recorded submodule pointer for ./reflexio
git submodule status
# 2) If the submodule is checked out in this workspace, confirm its HEAD matches the pointer
if [ -d "reflexio/.git" ] || [ -d "reflexio" ]; then
(cd reflexio && git rev-parse HEAD || true)
fi
# 3) If the submodule has a version file/package.json, print the version info
if [ -f "reflexio/package.json" ]; then
node -p "require('./reflexio/package.json').version"
fi
if [ -f "reflexio/pyproject.toml" ]; then
rg -n "version\s*=" reflexio/pyproject.toml || true
fiRepository: ReflexioAI/claude-smart
Length of output: 159
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check if reflexio is a git submodule and inspect its remote
if [ -d "reflexio/.git" ] || [ -d "reflexio" ]; then
echo "=== Reflexio git info ==="
# Try to get remote URL and recent commits
(cd reflexio && git remote -v 2>/dev/null || echo "No remote info available")
# Check for version tags or release info near the new commit
echo ""
echo "=== Checking commit 9143d1bf74584d0ecced69216c5b50773363c8e0 ==="
(cd reflexio && git show --quiet 9143d1bf74584d0ecced69216c5b50773363c8e0 2>/dev/null || echo "Commit not found locally")
# List recent tags in the submodule
echo ""
echo "=== Recent tags in reflexio ==="
(cd reflexio && git tag -l | tail -20 || true)
# Show commit log to see version history
echo ""
echo "=== Recent commits in reflexio ==="
(cd reflexio && git log --oneline -20 || true)
else
echo "reflexio directory not accessible as a git repo"
fiRepository: ReflexioAI/claude-smart
Length of output: 475
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== .gitmodules content ==="
if [ -f ".gitmodules" ]; then
cat -n .gitmodules
else
echo ".gitmodules not found"
fi
echo ""
echo "=== Git config submodule.reflexio.url ==="
git config --file .gitmodules submodule.reflexio.url || echo "No submodule.reflexio.url found"
echo ""
echo "=== Git index/HEAD reference to reflexio ==="
git ls-files -s | grep reflexio || echo "No reflexio entry in index"
echo ""
echo "=== Check if reflexio is listed as a regular directory vs submodule ==="
git status reflexio 2>&1 || trueRepository: ReflexioAI/claude-smart
Length of output: 995
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check previous reflexio commit (before this PR) ==="
# Find what the reflexio submodule pointed to before this change
git log --oneline -20 -- reflexio || echo "No history available"
echo ""
echo "=== Fetch external reflexio repo info to verify the commit ==="
# Use git ls-remote to check the external repo without cloning
git ls-remote https://github.com/ReflexioAI/reflexio.git | grep -E "(9143d1bf|refs/tags/|HEAD)" | head -20Repository: ReflexioAI/claude-smart
Length of output: 755
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check current HEAD for reflexio pointer ==="
git show HEAD:.gitmodules | grep -A2 'submodule "reflexio"' || echo "Unable to read from HEAD"
echo ""
echo "=== Get the diff showing what reflexio commit changed from/to ==="
git diff HEAD~1 HEAD -- reflexio 2>/dev/null | head -40 || echo "No previous commit or cannot diff"
echo ""
echo "=== Direct check: what's the reflexio entry in HEAD vs HEAD~1 ==="
echo "HEAD: $(git ls-tree HEAD reflexio | awk '{print $3}')"
git ls-tree HEAD~1 reflexio 2>/dev/null | awk '{print "HEAD~1: " $3}' || echo "HEAD~1 not available"Repository: ReflexioAI/claude-smart
Length of output: 441
Submodule pointer uses unreleased reflexio code, not the v0.2.17 release.
The PR updates reflexio to commit 9143d1bf74584d0ecced69216c5b50773363c8e0, which is the current main branch HEAD of the external reflexio repository. However, the v0.2.17 release tag points to a different commit (a99ff1df33ed586125b277010aef1ab282fb1421). This means the main repo will consume unreleased wire-model changes from reflexio's main branch, not the finalized v0.2.17 release.
Clarify intent: Is using the unreleased main branch intentional (accepted risk), or should this pin to the v0.2.17 tag commit instead to match the claimed version?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@reflexio` at line 1, The reflexio submodule is currently pinned to the
unreleased main commit 9143d1bf74584d0ecced69216c5b50773363c8e0 instead of the
v0.2.17 release commit a99ff1df33ed586125b277010aef1ab282fb1421; either update
the submodule pointer to the v0.2.17 tag/commit (reset the reflexio submodule to
a99ff1df33ed586125b277010aef1ab282fb1421, commit the change) so the repo
consumes the released wire-model, or if using main is intentional, add a clear
PR note and repository CHANGELOG entry stating that reflexio is intentionally
pinned to main (9143d1bf...) and accept the risk of unreleased changes.
User turns shouldn't carry citations or tool_used metadata; restrict the citation/pending_tools attachment to Assistant turns and harden the _to_wire_citations input checks against non-string id/title/real_id.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
plugin/src/claude_smart/state.py (1)
202-210:⚠️ Potential issue | 🟠 MajorReject malformed
id/titleinstead of emitting empty fields.At Line 208 and Line 209, malformed values are normalized to
"", so invalid citations still get emitted. That weakens payload quality and can produce blank tags/titles downstream.Proposed fix
tag = item.get("id") title = item.get("title") + if not isinstance(tag, str) or not tag: + continue + if not isinstance(title, str): + continue out.append( { "kind": kind, "real_id": real_id, - "tag": tag if isinstance(tag, str) else "", - "title": title if isinstance(title, str) else "", + "tag": tag, + "title": title, } )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plugin/src/claude_smart/state.py` around lines 202 - 210, The code currently normalizes malformed tag/title to empty strings which emits invalid entries; instead validate tag and title before appending: check that tag and title are instances of str (and non-empty if required) and if either is invalid skip/omit the entire out.append for that item (or return/continue) so only well-formed entries with valid tag/title (use the existing variables tag, title, kind, real_id) are emitted; update the logic around the out.append call to perform this validation and optionally log or count skipped items.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@plugin/src/claude_smart/state.py`:
- Around line 202-210: The code currently normalizes malformed tag/title to
empty strings which emits invalid entries; instead validate tag and title before
appending: check that tag and title are instances of str (and non-empty if
required) and if either is invalid skip/omit the entire out.append for that item
(or return/continue) so only well-formed entries with valid tag/title (use the
existing variables tag, title, kind, real_id) are emitted; update the logic
around the out.append call to perform this validation and optionally log or
count skipped items.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: cb28465d-b2a7-4865-a57c-042cc215a986
📒 Files selected for processing (1)
plugin/src/claude_smart/state.py
Summary
cited_itemsonto the publish-timeInteractionData.citationsfield so reflexio (v0.2.17+) can drive playbook/profile reflection from real Assistant citations.tools_usedattachment to Assistant turns only — User turns no longer carry citation metadata into the publish payload.StatusStrEnum +response_model_exclude_nonewire shape — values are lowercase ("archived","pending") and CURRENT rows arrive asnull/missing, not"CURRENT".reflexiosubmodule tov0.2.17.Changes
Citation wiring (server-bound)
plugin/src/claude_smart/state.py— new_to_wire_citationshelper maps{id, kind, title, real_id}(local) →{kind, real_id, tag, title}(wireCitationshape).unpublished_slicenow folds resolvable citations onto the Assistant turn undercitations; entries withoutreal_idor with an unsupportedkindare dropped (the server has aLiteral["playbook", "profile"]).plugin/src/claude_smart/state.py— citation andtools_usedattachment is now guarded byrole == "Assistant". User turns retain their plain shape and never carry citation metadata, even ifcited_itemsis somehow present on the record. Input checks in_to_wire_citationsalso reject non-stringid/title/real_iddefensively.tests/test_state.py— adds coverage for the mapping happy path, omitted-when-empty behavior, invalidkindfiltering, missingreal_idrejection, and non-list / non-dict input safety.Dashboard status fix
plugin/dashboard/lib/types.ts— narrowPlaybookStatus/ProfileStatusto"pending" | "archived"; document the null-for-current invariant.plugin/dashboard/lib/status.ts— new sharedstatusLabelhelper. Consolidates the three previously duplicated copies in playbooks/profiles pages.plugin/dashboard/app/dashboard/page.tsx— filter CURRENT playbooks viastatus == null(matches the wire), drop the dead"CURRENT"string compare.plugin/dashboard/app/playbooks/page.tsx,app/playbooks/[id]/page.tsx,app/profiles/[id]/page.tsx— import the shared helper; lowercase comparisons replace the old uppercase ones.Cleanup-threshold tuning
plugin/scripts/backend-service.sh—INTERACTION_CLEANUP_THRESHOLD1000 → 500 andINTERACTION_CLEANUP_DELETE_COUNT500 → 200. Cap of 500 interactions, evict oldest 200, keep 300.Submodule
reflexio→v0.2.17(single release commit, contains theCitationwire model).Test Plan
ruff check+ruff formatclean onstate.pyandtests/test_state.py.pyrightclean on the same two files.npx tsc --noEmitclean across the dashboard.npx biome checkclean on all touched dashboard files (exit 0).tests/test_state.pyexercise:playbookandprofilecitationscitationskey entirelykindand missingreal_idare filtered[]without raisingcs-citecalls shows up in the publish payload with a populatedcitationsarray, and that User turns in the same payload have nocitationskey.Summary by CodeRabbit
New Features
Improvements
Tests