Skip to content

fix(delete): require explicit group scope for chunk/source episode deletes - #412

Merged
verveguy merged 9 commits into
mainfrom
fabrik/issue-406
Aug 16, 2026
Merged

fix(delete): require explicit group scope for chunk/source episode deletes#412
verveguy merged 9 commits into
mainfrom
fabrik/issue-406

Conversation

@verveguy

Copy link
Copy Markdown
Owner

Closes #406

Summary

knowledge_delete_chunk_episode and knowledge_delete_by_source silently deleted Episodic rows across every group whenever the caller omitted group_ids (or passed null/[]). The liminis app omits it on every call, so an ordinary heading rename — which enqueues one deleteChunkEpisode/deleteBySource per affected chunk — could destroy another group's episode data. This is the same failure class as #368, which 0.13.0 treated as release-blocking.

Both methods now require an explicit, non-empty group_ids and reject the call outright (naming the missing parameter) rather than defaulting to "all groups."

Changes

  • crates/core/src/db.rs: Conn::remove_episodes_by_chunk_id and remove_episodes_by_source take group_ids: &[&str] (no longer Option), so an unscoped query is unrepresentable at the data-access layer, not merely blocked one layer up.
  • crates/core/src/handlers.rs: added extract_required_group_ids (non-empty array of non-empty strings, deduped, actionable error) and switched handle_delete_chunk_episode/handle_delete_by_source to it. handle_delete_by_group's existing inline validation was refactored to share the same helper. extract_optional_group_ids is untouched — still correctly used by the five read/search handlers where "absent = all groups" is the intended semantic.
  • crates/service/src/mcp/tools.rs: group_ids moved into required for both tool schemas; descriptions updated to state the scope is mandatory.
  • Tests (crates/core/tests/tier1c_deletion.rs): fixed the 5 existing tests to pass explicit group_ids, and added rejection tests (omitted/null/[], asserting both groups' rows survive) and cross-group isolation tests (same name or source_description/prefix colliding across two groups, scoped delete only touches the named group) for both methods.
  • crates/service/tests/mcp_real_corpus_mutation_e2e.rs: added the now-required group_ids to its one unscoped knowledge_delete_chunk_episode call.
  • CHANGELOG.md: added a [0.13.2] entry.

Notes for reviewers

Test plan

  • cargo fmt --all — clean
  • cargo test — 1250 passed, 8 ignored (64 suites)
  • cargo clippy --all-targets -- -D warnings — no issues

…letes

Conn::remove_episodes_by_chunk_id and remove_episodes_by_source took
Option<&[&str]> group_ids and silently ran an unscoped, all-groups
DETACH DELETE whenever the caller omitted the filter. The liminis app
omits it on every call, so an ordinary heading rename could delete
another group's episode data.

Make group_ids mandatory (&[&str]) at the data-access layer so an
unscoped query is unrepresentable, and add extract_required_group_ids
in handlers.rs (factored out of handle_delete_by_group's existing
validation) so both handle_delete_chunk_episode and
handle_delete_by_source reject missing/null/empty group_ids with an
actionable error instead of defaulting to "all groups". Update the MCP
tool schemas to mark group_ids required for both tools.
@verveguy

Copy link
Copy Markdown
Owner Author

🏭 Fabrik — stage: Implement
branch: fabrik/issue-406 | commit: f751a96 | main: 0a92e6f | 2026-08-15 21:03 UTC

Everything looks correct and matches the plan exactly. All work is committed, pushed, tests pass (1250 passed, 8 ignored), and clippy is clean. Now emitting the PR creation marker.

Made group_ids mandatory for knowledge_delete_chunk_episode and knowledge_delete_by_source at both the handler and data-access layers, closing the unscoped-delete gap described in the issue. Added extract_required_group_ids (shared with handle_delete_by_group), updated the MCP tool schemas, fixed/added tests covering rejection and cross-group isolation for both methods, and added a CHANGELOG entry. Full test suite (1250 tests) and clippy pass; branch is pushed and the Plan task checklist is checked off.


Used 23/100 turns, 4.0M input (45 raw + 3.9M cache-read + 177k cache-write) / 5k output tokens.

@verveguy
verveguy marked this pull request as ready for review August 15, 2026 21:03
Copilot AI lite review requested due to automatic review settings August 15, 2026 21:03
@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@verveguy, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 59 minutes

Limit details: You’ve used all 1 included review currently available under your plan.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 863a82db-0a8c-4324-8d4e-082631295c28

📥 Commits

Reviewing files that changed from the base of the PR and between f751a96 and 41a7e3d.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • crates/core/src/db.rs
  • crates/core/src/handlers.rs
  • crates/service/src/mcp/tools.rs
  • specs/406-knowledge-delete-chunk-episode/spec.md
📝 Walkthrough

Walkthrough

Deletion by source and deletion by chunk now require explicit, non-empty group_ids. Handlers validate and deduplicate scopes before deletion. Database helpers always apply group filters. MCP schemas document the required scope. Tests cover invalid scopes, group isolation, exact and prefix matching, and zero-match results.

Possibly related issues

  • verveguy/liminis-context-graph issue 406: Defines the required group-scoped behavior implemented by this PR.
  • verveguy/liminis-context-graph issue 403: Covers mandatory group_ids validation for chunk and source deletion.

Possibly related PRs


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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5571edfe-50a3-4884-a14a-602e75a0adbd

📥 Commits

Reviewing files that changed from the base of the PR and between 0a92e6f and f751a96.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • crates/core/src/db.rs
  • crates/core/src/handlers.rs
  • crates/core/tests/tier1c_deletion.rs
  • crates/service/src/mcp/tools.rs
  • crates/service/tests/mcp_real_corpus_mutation_e2e.rs
  • specs/406-knowledge-delete-chunk-episode/spec.md

Comment thread crates/service/src/mcp/tools.rs

@handarbeit-pruefer handarbeit-pruefer 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.

Reviewed the changes closing #406: knowledge_delete_chunk_episode and knowledge_delete_by_source now require an explicit, non-empty group_ids, enforced at both the MCP schema level (required in tools.rs) and the data-access layer (Conn::remove_episodes_by_chunk_id/remove_episodes_by_source now take group_ids: &[&str] instead of Option), making an unscoped delete unrepresentable rather than merely blocked in the handler. The new extract_required_group_ids helper correctly rejects absent/null/empty arrays and non-string/empty-string elements, dedupes while preserving order, and is shared cleanly with handle_delete_by_group's existing validation. extract_optional_group_ids, used by read/search handlers where "absent = all groups" is intended, is untouched. Cypher queries are properly parameterized with group_id IN $gids, and the second-phase DETACH DELETE only touches UUIDs already scoped by the first query. Verified both lcg-core and lcg-service compile cleanly with these changes, checked all call sites of the two Conn methods (only the two handlers use them), and reviewed the new rejection/cross-group-isolation tests in tier1c_deletion.rs, which correctly assert that rows in other groups survive an unscoped or wrongly-scoped call. No correctness, security, or design issues found; the PR's own caveat about the liminis-app rollout sequencing (client not yet sending group_ids) is explicitly out of scope for this repo and accurately documented.

Copilot AI 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.

Pull request overview

This PR closes #406 by removing the dangerous “omit group_ids = delete across all groups” behavior from knowledge_delete_chunk_episode and knowledge_delete_by_source, making cross-group destructive deletes unrepresentable and rejected at the handler boundary.

Changes:

  • Made Conn::remove_episodes_by_chunk_id / remove_episodes_by_source require an explicit group scope (no Option), and updated the handlers to enforce a non-empty group_ids.
  • Updated MCP tool schemas to mark group_ids as required for both delete tools.
  • Expanded and updated deletion tests to cover rejection of unscoped calls and cross-group isolation.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
specs/406-knowledge-delete-chunk-episode/spec.md Adds Spec Kit spec defining required group scoping and test scenarios for the fix.
crates/core/src/db.rs Makes group scoping mandatory in the DB deletion queries.
crates/core/src/handlers.rs Enforces required group_ids for destructive deletes via a shared helper.
crates/core/tests/tier1c_deletion.rs Updates existing tests and adds new cross-group isolation + rejection tests.
crates/service/src/mcp/tools.rs Updates MCP tool schemas to require group_ids for the two delete tools.
crates/service/tests/mcp_real_corpus_mutation_e2e.rs Updates E2E mutation test to pass group_ids to chunk deletion.
CHANGELOG.md Documents the patch behavior change under 0.13.2.
Suppressed comments (2)

crates/service/src/mcp/tools.rs:376

  • Same as above: the schema marks group_ids as required, but it still permits an empty array. Adding minItems: 1 (and non-empty string items) makes the schema accurately reflect the handler contract and avoids a round-trip just to discover the request is invalid.
                        "group_ids": {
                            "type": "array", "items": {"type": "string"},
                            "description": "Non-empty array of groups to restrict deletion to (required). An unscoped, all-groups delete is not supported."
                        }

crates/core/src/db.rs:909

  • group_ids is documented as mandatory and non-empty, but an empty slice is still representable at the type level. A defensive is_empty() check here prevents accidental unscoped behavior or DB-specific edge cases around IN [], and makes failures clearer if a new call site ever regresses.
    pub fn remove_episodes_by_chunk_id(
        &self,
        chunk_id: &str,
        group_ids: &[&str],
    ) -> Result<Vec<String>, Error> {
        let match_sql =
            "MATCH (ep:Episodic) WHERE ep.name = $name AND ep.group_id IN $gids RETURN ep.uuid";
        let params = serde_json::json!({ "name": chunk_id, "gids": group_ids });

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread crates/core/src/db.rs
Comment thread crates/service/src/mcp/tools.rs
@verveguy

Copy link
Copy Markdown
Owner Author

🏭 Fabrik — stage: Review
branch: fabrik/issue-406 | commit: f751a96 | main: 0a92e6f | 2026-08-15 21:10 UTC

All required review checks pass locally (fmt, full test suite of 1250 tests, clippy with -D warnings), the implementation matches the plan and spec exactly, and the branch is already up to date with main (0 commits behind) with no conflicts. Per project convention, I don't wait on CI (only Validate stage does that). No issues found — nothing to fix.

Review Findings

Verified

  • Rebased/up-to-date: branch is 0 commits behind origin/main already (based on 0a92e6f, matching the merge-base check)
  • No external PR review comments to address (checked gh pr view 412 --comments)
  • Correctness: Conn::remove_episodes_by_chunk_id/remove_episodes_by_source (db.rs) now take group_ids: &[&str] (non-Option) and always emit AND ep.group_id IN $gids — unscoped query is genuinely unrepresentable at the data layer (FR-005). Handlers use the new extract_required_group_ids and propagate its error; handle_delete_by_group was refactored onto the same helper with no behavior change (dedup fix is a real, harmless improvement to a pre-existing WAL mis-routing edge case). MCP tool schemas correctly mark group_ids as required with updated descriptions.
  • Call-site audit: grepped every caller of the two Conn methods (only the two handlers) and every test invocation of both MCP methods (tier1c_deletion.rs, mcp_real_corpus_mutation_e2e.rs, wal_population.rs) — all pass explicit group_ids now; none left unscoped.
  • Test coverage: new tests cover both methods' rejection paths (omitted/null/[], FR-001/002, SC-001/SC-004) and cross-group isolation including multi-row-per-group and prefix-match cases (FR-003/004/006/007, SC-002/003/005) — matches every acceptance scenario in the spec.
  • Docs: CHANGELOG 0.13.2 entry is accurate; docs/ipc-mcp-reference.md correctly left untouched (it defers to handlers.rs as source of truth for parameter details, doesn't enumerate required/optional per-param); docs/llms-full.txt drift check passed in CI.
  • cargo fmt --all --check: clean
  • cargo test: 1250 passed, 8 ignored (64 suites, ~298s)
  • cargo clippy --all-targets -- -D warnings: no issues

Fixed

  • None needed.

Blocking

  • None.

Reviewed the mandatory-group-scope fix for knowledge_delete_chunk_episode/knowledge_delete_by_source. Implementation matches the plan exactly, all call sites are updated, test coverage hits every FR/SC in the spec, and fmt/full test suite (1250 tests)/clippy all pass clean. No issues found — PR is ready for human review.


Used 28/100 turns, 1.6M input (37 raw + 1.5M cache-read + 71k cache-write) / 6k output tokens.

…layer

CodeRabbit and Copilot both flagged that the required `group_ids` MCP
schemas still permitted an empty array, and that the DB-layer functions
documented group_ids as mandatory/non-empty without enforcing it. Add
`minItems: 1` to both delete tool schemas for earlier client-side
validation, and a defensive `is_empty()` check in
`remove_episodes_by_source`/`remove_episodes_by_chunk_id` that returns
`Error::Ipc` rather than silently running an empty `IN $gids` clause.
@verveguy

Copy link
Copy Markdown
Owner Author

🏭 Fabrik — stage: Review (review feedback addressed)
branch: fabrik/issue-406 | commit: fbb03ec | main: 0a92e6f | 2026-08-15 21:18 UTC

All three threads resolved. No issue body changes are needed — this was a code-only fix in response to review bot feedback, not a spec change.

Summary

Addressed three related review-bot findings (CodeRabbit, GitHub Copilot) on PR #412:

  1. MCP schema: added "minItems": 1 to group_ids in both knowledge_delete_by_source and knowledge_delete_chunk_episode tool schemas (crates/service/src/mcp/tools.rs), so clients get earlier validation rather than a round-trip to discover an empty array is rejected.
  2. DB layer defense in depth: added an is_empty() check to Conn::remove_episodes_by_source and Conn::remove_episodes_by_chunk_id (crates/core/src/db.rs) that returns Error::Ipc if group_ids is empty, guarding against a future caller bypassing handler-layer validation and hitting an IN [] clause.
  3. The handarbeit-pruefer bot review was informational only (no findings) — no action needed there.

Verified: cargo fmt --all, cargo build, cargo test -p lcg-core -p lcg-service (1024 passed, 8 ignored), cargo clippy --all-targets -- -D warnings all clean. Committed as fbb03ec and pushed. Resolved all three associated review threads via GraphQL.


Threads addressed:

  • crates/service/src/mcp/tools.rs:359 — resolved
  • crates/core/src/db.rs:871 — resolved
  • crates/service/src/mcp/tools.rs:357 — resolved

Resolved 3 review thread(s) across 6 comment(s).

@handarbeit-pruefer handarbeit-pruefer 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.

Reviewed the diff for PR #412 (via gh pr diff since the local checkout lacked main/a remote). The change closes the unscoped-delete gap cleanly: group_ids is now mandatory end-to-end — enforced in the JSON schema (minItems: 1), in the shared extract_required_group_ids handler helper, and defensively in the DB layer (remove_episodes_by_source/remove_episodes_by_chunk_id now take &[&str] and reject empty slices). Both prior review threads (CodeRabbit's minItems request and Copilot's DB-layer defense-in-depth request) are fully addressed with no new defects introduced by the fix. I verified the two DB functions have no other callers left on the old Option signature, and that extract_optional_group_ids is still correctly scoped to the five read/search handlers. Test coverage (rejection for omitted/null/empty, and cross-group isolation for both name and source-prefix matching) looks thorough. Only issue found is a stale doc comment left over from the refactor.

Additional findings (could not anchor to diff)

crates/core/src/handlers.rs:1325: This doc comment (and the inline comment just below at the let group_ids line) still describes the pre-refactor state — group_ids validation for handle_delete_by_group used to be inline here, contrasted explicitly with extract_optional_group_ids. Now that validation is delegated to the shared extract_required_group_ids helper (used by all three destructive handlers), the comment's framing ("validated explicitly here", "silently dropping a malformed element") no longer matches what the code at this call site actually does — the real explanation now lives on extract_required_group_ids itself. Worth a follow-up touch-up so a future reader isn't misled about where the validation logic lives.

@verveguy

Copy link
Copy Markdown
Owner Author

🏭 Fabrik — stage: Validate
branch: fabrik/issue-406 | commit: fbb03ec | main: 0a92e6f | 2026-08-15 21:24 UTC

All requirements verified against the spec, the full local gate (fmt/test/clippy) passes clean, and the branch is rebased and mergeable. Here is the validation report.

Validation Report

Requirements: 7/7 FRs and 5/5 SCs passed

  • FR-001/FR-002 (mandatory scope, actionable error, no default substitution): extract_required_group_ids (handlers.rs) rejects absent/null/[] group_ids with an error naming group_ids, never substitutes DEFAULT_GROUP_ID. Verified by delete_by_source_requires_group_ids and delete_chunk_episode_requires_group_ids tests (cover omitted, null, and [] in one loop each).
  • FR-003/FR-007 (scoped deletes touch only named groups, prefix match confined to group): Conn::remove_episodes_by_source/remove_episodes_by_chunk_id always emit AND ep.group_id IN $gids. Verified by delete_by_source_scoped_to_one_group (exact + prefix match, cross-group isolation) and delete_chunk_episode_scoped_to_one_group.
  • FR-004 (multi-row-per-scope deletion in one call): delete_chunk_episode_all_revisions (2 revisions, 1 call, deleted_count: 2) and delete_by_source_scoped_to_one_group (deleted_count: 2 for exact+prefix in one group).
  • FR-005 (unscoped query unrepresentable at data-access layer): both Conn methods take group_ids: &[&str] (non-Option), plus a defense-in-depth is_empty() check returning Error::Ipc.
  • FR-006 (scope matching nothing is success, not error): delete_chunk_episode_no_matchsuccess: true, deleted_count: 0.
  • SC-001–SC-005: all directly covered by the tests above (fixtures use colliding name/source_description across two groups in every case).

Test Suite: PASSED

  • cargo fmt --all -- --check: clean
  • cargo test: 1250 passed, 8 ignored (64 suites, 304.68s)
  • cargo clippy --all-targets -- -D warnings: clean

Regressions: None detected

  • Call-site audit: no remaining unscoped callers of either Conn method or either MCP method in tests or handlers.
  • handle_delete_by_group refactored onto the shared extract_required_group_ids with no behavior change; its own tests still pass.
  • extract_optional_group_ids (used by 5 read/search handlers) untouched.

Code completeness

  • No TODO/FIXME/debug logging in the diff.
  • CHANGELOG 0.13.2 entry present and accurate.
  • All Plan checklist items checked off in the issue body.

Branch state

Verdict: READY TO MERGE

Validation passed. PR mergeable: MERGEABLE, mergeStateStatus: BLOCKED (CI in progress on HEAD, not a conflict). All 7 FRs / 5 SCs verified against spec, full local suite passes (1250 tests, fmt/clippy clean), no regressions, branch rebased and pushed.


Used 29/100 turns, 1.6M input (41 raw + 1.6M cache-read + 71k cache-write) / 5k output tokens.

…equired_group_ids refactor

Comment described the old inline validation logic that was moved into the
shared extract_required_group_ids helper; it no longer matched what this
call site does.
@verveguy

Copy link
Copy Markdown
Owner Author

🏭 Fabrik — stage: Validate (review feedback addressed)
branch: fabrik/issue-406 | commit: e4f1590 | main: 0a92e6f | 2026-08-15 21:42 UTC

Fix applied, verified, committed, and pushed. This was a bot-review finding (stale doc comment), not an explicit user "resolved" signal, so I'm not advancing the pipeline.

Fixed the stale doc comment on handle_delete_by_group (handlers.rs:1320-1340) flagged by the bot review — it still described the pre-refactor inline validation instead of pointing to the now-shared extract_required_group_ids helper. Verified fmt/build/clippy clean, committed as e4f1590, and pushed. Not signaling stage completion since this was a bot finding, not an explicit user sign-off.


Threads addressed:

Resolved 0 review thread(s) across 1 comment(s).

@handarbeit-pruefer handarbeit-pruefer 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.

Reviewed the fix that makes group_ids mandatory for knowledge_delete_chunk_episode and knowledge_delete_by_source, closing the unscoped-delete gap in issue #406. The change is layered correctly: the new extract_required_group_ids handler helper rejects absent/null/empty/malformed group_ids with an actionable error before reaching the DB layer; Conn::remove_episodes_by_chunk_id/remove_episodes_by_source now take &[&str] instead of Option<...> and additionally defend against an empty slice at the data-access layer; and the MCP tool schemas mark group_ids required with minItems: 1, addressing both prior CodeRabbit/Copilot review threads. Single-group calls route WAL flush to that group's writer directly, multi-group calls fall back to the documented default-group routing (consistent with handle_delete_by_group's existing behavior). Test coverage is thorough — rejection tests for omitted/null/empty group_ids that verify no cross-group data loss, and cross-group isolation tests for both exact and prefix source matches. No correctness issues found; note (already called out in the PR description) that the liminis app's own call sites don't send group_ids yet, which is a deployment-sequencing concern tracked separately (liminis#998) rather than a defect in this PR.

@verveguy

Copy link
Copy Markdown
Owner Author

🏭 Fabrik — stage: Validate
branch: fabrik/issue-406 | commit: e4f1590 | main: 0a92e6f | 2026-08-15 21:49 UTC

Confirmed all three call sites (handle_delete_by_source, handle_delete_chunk_episode, handle_delete_by_group) share the validator. This matches the plan and spec exactly.

Validation Report

Requirements: 7/7 FRs and 5/5 SCs passed

  • FR-001/FR-002: extract_required_group_ids (handlers.rs:4355) rejects absent/null/[] group_ids with an actionable error naming group_ids; no silent DEFAULT_GROUP_ID fallback anywhere in the diff.
  • FR-003/FR-007: remove_episodes_by_source/remove_episodes_by_chunk_id (db.rs:868-926) unconditionally emit AND ep.group_id IN $gids; prefix match is confined to the group predicate.
  • FR-004: multi-row-per-scope deletion covered by delete_chunk_episode_all_revisions and the source-variant multi-match test.
  • FR-005: both Conn methods take group_ids: &[&str] (non-Option) — unscoped query is unrepresentable at the data layer, plus a defense-in-depth is_empty()Error::Ipc guard added during Review.
  • FR-006: scope matching nothing returns success: true, deleted_count: 0.
  • SC-001–SC-005: covered by the rejection and cross-group isolation tests in tier1c_deletion.rs.

Test Suite: PASSED

  • cargo fmt --all -- --check: clean
  • cargo test: 1250 passed, 0 failed, 8 ignored (full workspace, ~9 min)
  • cargo clippy --all-targets -- -D warnings: clean

Regressions: None detected

  • handle_delete_by_group refactored onto the same shared extract_required_group_ids helper with no behavior change; its own tests still pass.
  • extract_optional_group_ids (5 other read/search handlers) untouched.
  • Only change since the last full Validate pass (commit fbb03ece4f1590) is a doc-comment fix on handle_delete_by_group — verified via git diff, no logic touched.

Code completeness

  • No stray TODO/FIXME introduced by this diff (one pre-existing, unrelated TODO at handlers.rs:4127).
  • All Plan checklist items checked off in the issue body.
  • CHANGELOG 0.13.2 entry present.

Branch state

  • Rebased onto latest origin/main (already up to date, no conflicts).
  • Working tree clean, all commits pushed.
  • PR fix(delete): require explicit group scope for chunk/source episode deletes #412: mergeable: MERGEABLE, mergeStateStatus: BLOCKED (not CONFLICTING/DIRTY — per gate rules this does not block completion; BLOCKED reflects pending required CI checks, which the engine handles via wait_for_ci).

Verdict: READY TO MERGE

Validation passed. PR #412 mergeable: MERGEABLE, mergeStateStatus: BLOCKED (pending CI, not a conflict). All 7 FRs / 5 SCs verified against spec, full local suite passes (1250 tests, 0 failed, fmt/clippy clean), no regressions since last validation — only change was a doc-comment fix.


Used 18/100 turns, 1.2M input (33 raw + 1.2M cache-read + 60k cache-write) / 4k output tokens.

@verveguy

Copy link
Copy Markdown
Owner Author

🏭 Fabrik — stage: Validate (review feedback addressed)
branch: fabrik/issue-406 | commit: e4f1590 | main: 0a92e6f | 2026-08-15 22:05 UTC

The handarbeit-pruefer bot review found no correctness issues — it confirmed the implementation is layered correctly (handler validation, mandatory &[&str] at the DB layer, MCP schema minItems: 1) and the test coverage is thorough. It flagged one point already known and tracked separately: the liminis app's own call sites don't send group_ids yet, which is the deployment-sequencing concern tracked in liminis#998, not a defect in this PR.

No action is required — this is an informational bot finding with no requested fix, not an explicit user "resolved" signal, so I'm not making any changes and not signaling stage completion.

Summary

Reviewed the handarbeit-pruefer bot's validation-stage comment on PR #412. It reported zero correctness issues after reviewing the mandatory-group_ids fix, the layered validation (handler → DB layer → MCP schema), and the test coverage. Its one caveat (liminis app not yet sending group_ids) is already documented in the PR description and tracked as a separate coordinated rollout via liminis#998. Working tree is clean, nothing to commit or push.


Threads addressed:

Resolved 0 review thread(s) across 1 comment(s).

… wording

CHANGELOG's 0.13.2 section read as a pure bug fix; add a ### Changed
subsection marking the group_ids requirement as BREAKING for any caller
that previously omitted it. Also correct the spec's description of the
liminis app's two unscoped delete calls: they are reached via an
if/else on isChunkEntry(entry), not invoked back to back.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@handarbeit-pruefer handarbeit-pruefer 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.

Reviewed the group-scoping fix for knowledge_delete_chunk_episode/knowledge_delete_by_source across db.rs, handlers.rs, tools.rs, and the test suites. The implementation matches the PR description closely: Conn::remove_episodes_by_source/remove_episodes_by_chunk_id now take a mandatory &[&str] with a defense-in-depth empty check, extract_required_group_ids correctly rejects absent/null/empty/malformed values while deduping, both MCP schemas now require group_ids with minItems: 1 (resolving the two prior review threads), and single-vs-multi-group WAL routing correctly falls back to the documented default-group limitation only for genuinely multi-group calls. Tests cover rejection (omitted/null/[]) with cross-group survival checks and scoped-deletion isolation for both source and chunk-id matching, including exact and prefix cases. No other internal call sites of the changed Conn methods exist outside the delete handlers, and the WAL replay path doesn't re-invoke these methods, so the signature change is safe. No defects found.

@verveguy

Copy link
Copy Markdown
Owner Author

🏭 Fabrik — stage: Validate
branch: fabrik/issue-406 | commit: 41a7e3d | main: 0a92e6f | 2026-08-15 23:30 UTC

Everything checks out. All requirements from the spec are met, the requested CHANGELOG breaking-change callout and spec wording fix are both in place, the local gate (fmt/test/clippy) is clean, and the branch is rebased with no conflicts. Writing the final validation report.

Validation Report

Requirements: 7/7 FRs and 5/5 SCs passed

  • FR-001/FR-002: extract_required_group_ids rejects absent/null/[] group_ids with an actionable error naming the parameter; no silent DEFAULT_GROUP_ID fallback.
  • FR-003/FR-007: remove_episodes_by_source/remove_episodes_by_chunk_id unconditionally apply AND ep.group_id IN $gids; prefix match confined to scope.
  • FR-004: multi-row-per-scope deletion in one call verified by tests.
  • FR-005: both Conn methods take group_ids: &[&str] (non-Option) — unrepresentable at data layer, plus defense-in-depth empty-slice guard.
  • FR-006: non-matching valid scope returns success: true, deleted_count: 0.
  • SC-001–SC-005: covered by rejection and cross-group isolation tests in tier1c_deletion.rs.

User's Validate-stage feedback: addressed

  • CHANGELOG.md now has an explicit ### Changed BREAKING callout under ## [0.13.2] stating group_ids is required on both MCP/IPC surfaces, the remedy (explicit group, no default), and why it ships in a patch release.
  • Spec wording corrected from "back to back, in the same code path" to "in the same unlink handler, on either side of an isChunkEntry branch."
  • CHANGELOG date (2026-08-15) already matches today's date — no change needed.

Test Suite: PASSED

  • cargo fmt --all -- --check: clean
  • cargo test: 1250 passed, 8 ignored (64 suites, ~564s)
  • cargo clippy --all-targets -- -D warnings: clean

Regressions: None detected

Branch state

  • Rebased onto latest origin/main (already up to date, no conflicts)
  • Working tree clean, all commits pushed

Verdict: READY TO MERGE

Validation passed. PR #412 mergeable: MERGEABLE, mergeStateStatus: BLOCKED (pending CI, not a conflict). All 7 FRs / 5 SCs verified, the requested CHANGELOG breaking-change callout and spec wording fix are confirmed present, full local suite passes (1250 tests, fmt/clippy clean).


Used 13/100 turns, 907k input (23 raw + 846k cache-read + 61k cache-write) / 3k output tokens.

@verveguy

Copy link
Copy Markdown
Owner Author

🏭 Fabrik merge-train — Landed via batch PR #415.

@verveguy
verveguy merged commit eb7dedb into main Aug 16, 2026
17 checks passed
@verveguy
verveguy deleted the fabrik/issue-406 branch August 16, 2026 00:03
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.

knowledge_delete_chunk_episode deletes across all groups when group_ids is omitted

2 participants