Skip to content

fix(chat-knowledge): contexts can be deleted, cascade with their chat, and orphans get cleaned (#16490) - #16503

Merged
mrveiss merged 3 commits into
mainfrom
issue-16490-chat-knowledge-delete
Sep 13, 2026
Merged

mrveiss merged 3 commits into
mainfrom
issue-16490-chat-knowledge-delete

Conversation

@mrveiss

@mrveiss mrveiss commented Sep 12, 2026 •

Copy link
Copy Markdown
Owner

Closes #16490

Single-issue rationale: #16490 covers deleting chat-knowledge contexts: an authorized DELETE route, the cascade from chat and session delete, and orphan discovery and cleanup. No other open issue shares that scope. #16502, which reports the cascade result in the session-delete response, was split out because schemas_chat.py sits exactly at its file-size ceiling and needs a module split first. #15160, the never-assigned chat_knowledge_manager module global, is a different defect with its own fix.

Thinking Path

A test probe on 2026-09-12 left an empty chat-knowledge context on an install, and there was no supported way to remove it. Contexts could be created and read but never deleted, and chat deletion didn't reach them. The fix needed an authorized delete, a cascade from the existing session-delete path, and a way to find contexts whose chat no longer exists.

All chat-knowledge state is in memory on ChatKnowledgeManager (chat_contexts, file_associations, pending_decisions), so there are no DB or Redis rows to migrate. Persistent KB facts stay with _cleanup_knowledge_base_facts, as before.

What Changed

  • api/chat_knowledge_delete.py (new, mounted from chat_knowledge.py, the way chat.py mounts chat_sessions.py):
    • DELETE /api/chat-knowledge/context/{chat_id} removes the context, its temporary knowledge, its file associations, and files that upload_file_to_chat itself wrote. Files that are only associated are left alone.
    • Existence is checked with peek_chat_knowledge_manager before authorization, so an unknown id is a 404 and is never claimed by validate_chat_ownership's legacy-migration path.
    • Authorization reuses validate_chat_ownership: owner, org admin, or feat(knowledge): Share KB facts when sharing conversations #689 shared access.
  • Orphans: admin-only GET / DELETE /api/chat-knowledge/context-orphans, dry-run by default, the same shape as /session-orphans. The path is hyphenated so /context/{chat_id} can never shadow it.
  • Cascade: _perform_all_session_cleanup runs for both DELETE /chats/{chat_id} and DELETE /chat/sessions/{session_id}, and now also calls _cleanup_chat_knowledge_context.
  • api/chat_sessions_delete_cleanup.py (new): the six existing KB-fact and transcript cleanup helpers move here verbatim, which makes room for the cascade. chat_sessions.py's ceiling is lowered from 2090 to 1953; no ceiling was raised.
  • Two new test files, 21 cases:
    • owner delete removes the dependents
    • a non-owner gets 403
    • an unknown id, or no manager yet, gets 404
    • orphan discovery, and dry run versus real cleanup
    • a non-admin gets 403 on the orphan routes
    • the cascade runs on session delete
    • the cleanup tuple has the expected shape
  • Known gap, filed as bug(chat): session delete drops the chat-knowledge cleanup result from its response #16502: SessionDeleteData has no field for the new result, so the response drops it. schemas_chat.py is exactly at its ceiling. The cleanup itself happens and is tested at the backend-state level.

Verification

  • Static checks only. flake8, black, isort, py_compile and the full pre-commit suite (including the file-size guard) passed, and the pre-push hook's own test selection passed. No tests were run by hand. CI is the first full run of the 21 new cases.
  • Collision sweep: none of the other open PRs touches chat_knowledge*.py, chat.py or chat_sessions.py. Other PRs edit different ceiling entries in the shared size-baseline files.
  • AC mapping:
    • AC1: the owner, admin and 403 tests in chat_knowledge_delete_test.py.
    • AC2: the cascade test in chat_sessions_delete_cleanup_test.py.
    • AC3: the orphan discovery and dry-run versus cleanup tests.

Model Used

Claude Opus 5 (coordinator session), with an implementation subagent.

🤖 Generated with Claude Code

@mrveiss mrveiss added this to the v0.10.0 milestone Sep 12, 2026
@mrveiss mrveiss added bug Something isn't working priority: medium backend labels Sep 12, 2026
@mrveiss mrveiss linked an issue Sep 12, 2026 that may be closed by this pull request
1 of 3 tasks
@mrveiss mrveiss self-assigned this Sep 12, 2026
@coderabbitai

coderabbitai Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 54304890-5457-4e98-91e3-7bd5872dc683


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.

@github-actions

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No new hardcoded values of either class — ssot and other both block.

Known backlog in pipeline-scripts/hardcoded_values_baseline.txt is suppressed and tracked in #14371.

@github-actions

Copy link
Copy Markdown
Contributor

AutoBot Phase Validation Results

System Maturity: 96.5%

Phase Status:

PASS Phase 1: Core Infrastructure: 100.0%
PASS Phase 2: Knowledge Base and Memory: 100.0%
IN PROGRESS Phase 3: LLM Integration: 66.7%
PASS Phase 4: Security and Authentication: 100.0%
PASS Phase 5: Agent Orchestration: 100.0%
PASS Phase 6: Enhanced UI/UX: 100.0%
PASS Phase 7: Testing and Validation: 100.0%
PASS Phase 8: Advanced Features: 100.0%
PASS Phase 9: Multi-Modal AI: 100.0%
PASS Phase 10: Production Readiness: 100.0%

Recommendations:

  • 🟡 MEDIUM: Phase 3: LLM Integration requires attention (66.7% complete): Review and implement
  • ✅ System is production-ready - consider advanced features and scaling: Review and implement

@github-actions

Copy link
Copy Markdown
Contributor

Notice: 29 open PRs — past the runaway threshold (25)

There is no PR queue limit, and this is not a request to defer this PR. Work proceeds one issue at a time without a cap on open PRs; review capacity is the constraint.

This notice only means the count is high enough to be worth a glance for a runaway — something opening PRs in a loop, or a merge pipeline that has stalled so nothing is draining.

Currently open:

If the queue is draining normally, ignore this. Otherwise:

  1. Check whether CI is dispatching at all — see the ci-dispatch-watchdog status on these PRs
  2. Merge the ones whose CI has finished and review has passed: gh pr merge <number> --squash --delete-branch
  3. Look for a loop opening near-identical PRs

Warn-only runaway detector — .github/workflows/pr-queue-gate.yml. It never blocks a merge.

@mrveiss

mrveiss commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

Review of 4ed6dd5b5: approve. 0 behind, 0 trailers, closingIssuesReferences matches body ([16490]).

New api/chat_knowledge_delete.py, split out to stay under the file-size ceiling (same pattern as #15160). DELETE /context/{chat_id} checks 404 (via peek_chat_knowledge_manager, never the constructing accessor) before authorization, with a specifically-cited reason: validate_chat_ownership silently claims an unowned chat_id on its legacy-migration path, so authorizing before the existence check would return "authorized, nothing to delete" for a probe against a chat_id nothing created, instead of a clean 404. Reuses the existing ownership check rather than a new authz path. Chat-deletion cascade lives in chat_sessions_delete_cleanup.py, called from chat_sessions.py's existing delete route.

All three ACs have dedicated tests: owner-delete-cascades (test_owner_deletes_and_every_dependent_record_is_gone) plus non-owner-403 and unknown/no-manager-404; chat-deletion cascade (test_deletes_the_session_context_and_reports_it, test_the_context_is_gone_after_cleanup_runs); orphan sweep with dry-run vs real delete and non-admin-403 on both orphan routes.

@github-actions

Copy link
Copy Markdown
Contributor

AutoBot Phase Validation Results

System Maturity: 96.5%

Phase Status:

PASS Phase 1: Core Infrastructure: 100.0%
PASS Phase 2: Knowledge Base and Memory: 100.0%
IN PROGRESS Phase 3: LLM Integration: 66.7%
PASS Phase 4: Security and Authentication: 100.0%
PASS Phase 5: Agent Orchestration: 100.0%
PASS Phase 6: Enhanced UI/UX: 100.0%
PASS Phase 7: Testing and Validation: 100.0%
PASS Phase 8: Advanced Features: 100.0%
PASS Phase 9: Multi-Modal AI: 100.0%
PASS Phase 10: Production Readiness: 100.0%

Recommendations:

  • 🟡 MEDIUM: Phase 3: LLM Integration requires attention (66.7% complete): Review and implement
  • ✅ System is production-ready - consider advanced features and scaling: Review and implement

Comment thread autobot-backend/api/chat_sessions_delete_cleanup.py Fixed
Comment thread autobot-backend/api/chat_sessions_delete_cleanup.py Fixed
mrveiss and others added 3 commits September 13, 2026 02:21
…, and orphans get cleaned (#16490)

Adds an owner-or-admin-scoped DELETE /api/chat-knowledge/context/{chat_id}
(existence checked before authorization, so an unknown chat_id is a clean
404 rather than being silently claimed by validate_chat_ownership's
legacy-migration path), removing the context together with its file
associations (and any file the manager itself wrote to disk for them) and
its pending-decisions entry. Deleting a chat session now cascades to its
chat-knowledge context via _perform_all_session_cleanup. Admin-only
GET/DELETE /api/chat-knowledge/context-orphans discover and clean contexts
whose chat_id matches no chat, mirroring api/knowledge_maintenance.py's
/session-orphans shape.

api/chat_knowledge.py and api/chat_sessions.py were both within a few lines
of scripts/check_python_file_size.py's ceiling (chat_sessions.py exactly at
its grandfathered ceiling), so the new routes and the cascade helper live in
two new sibling modules (api/chat_knowledge_delete.py,
api/chat_sessions_delete_cleanup.py) instead, composed in the same way
api/chat.py already composes api/chat_sessions.py. Extracting six
pre-existing, unchanged helpers into the second module shrinks
chat_sessions.py enough to lower its ceiling in both
scripts/python_file_size_known_large.py and
repo_tests/python_file_size_ratchet_baseline.py, rather than raising it.
Auto-regenerated (py3.14) to match the backend and/or SLM backend
schema so the required verify-generated-types gate(s) pass.
Triggered by auto-fix-generated-types.yml.
…he guard (#16490)

py/path-injection raised two alerts on the transcript delete: validate_relative_path contains the path, but CodeQL only credits a guard in the sink's own scope, spelled realpath + startswith(root + os.sep), the form #16229 and #16236 settled on. The validator call stays; the sink now uses the realpath it checks.
@mrveiss
mrveiss force-pushed the issue-16490-chat-knowledge-delete branch from 4ed6dd5 to f9f0b32 Compare September 12, 2026 23:27
@mrveiss

mrveiss commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

Delta 4ed6dd5b5..f9f0b3290: approve. The only file whose change differs from the approved net diff is autobot-backend/api/chat_sessions_delete_cleanup.py:

  • The new check: before the delete, _cleanup_conversation_transcript resolves both PATH.DATA_DIR / "conversation_transcripts" and transcript_path with os.path.realpath and requires real.startswith(root + os.sep). Otherwise it raises ValueError("Invalid session ID"). It then removes real.
  • The same root: transcript_path comes from validate_relative_path(..., PATH.DATA_DIR / "conversation_transcripts", ...), so legitimate deletes still pass. A symlink inside the directory that points outside it is now refused, which is stricter.
  • The same error handling: the new raise sits inside the same try as the existing "Invalid session ID" raise, so the function's except Exception catches it and it becomes the cleanup result, exactly like the old refusal.
  • The one behaviour change, as stated: a name that resolves to the directory itself is refused up front. os.remove would refuse a directory anyway.

The CodeQL result on this head was not in yet when I posted, so whether CodeQL now credits the check is still unconfirmed.

@mrveiss

mrveiss commented Sep 13, 2026

Copy link
Copy Markdown
Owner Author

CodeQL at f9f0b3290: NEUTRAL, with no annotations. This settles the point my delta approval left open. At the earlier head, CodeQL flagged two new py/path-injection alerts at api/chat_sessions_delete_cleanup.py:180-181. At this head it reports none, so it now credits the in-scope os.path.realpath + startswith(root + os.sep) check beside the delete. The only note in the check-run is "2 configurations not found", which is the PR matrix analysing only the languages this change touches, as designed, not a finding.

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

Labels

backend bug Something isn't working priority: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(chat-knowledge): contexts can be created but never deleted, so orphans accumulate

2 participants