Skip to content

feat(cli): aelf delete subcommand (#440) - #451

Merged
robotrocketscience merged 6 commits into
mainfrom
feat/issue-440-cli-delete
May 6, 2026
Merged

feat(cli): aelf delete subcommand (#440)#451
robotrocketscience merged 6 commits into
mainfrom
feat/issue-440-cli-delete

Conversation

@yoshi280

@yoshi280 yoshi280 commented May 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds aelf delete — explicit hard-delete of one belief from the store. Sibling of aelf unlock. Closes #440.

Default user posture stays "decay, don't delete" (retention class #290 + Beta-Bernoulli posterior). aelf delete is the escape hatch: hand-entered duplicates, beliefs promoted in error, privacy removals.

Contract

aelf delete <belief-id> [--yes] [--force]
  • Hard-delete via existing MemoryStore.delete_belief (row + FTS + edges + entity index).
  • One audit row written to feedback_history BEFORE the cascade (valence=-1.0, source=user_deleted or user_deleted_force). feedback_history has no FK to beliefs, so the orphan row pins the forensic record.
  • Confirmation prompt by default — prints belief content, requires user to type the first 8 chars of the id (terraform-destroy style, beats y/N for muscle-memory safety on a destructive op).
  • --yes skips the prompt.
  • Refuses locked (lock_level=user) beliefs without --force. --force does not skip the prompt — pair with --yes for non-interactive locked-belief deletion.

Decisions made (deferred from issue body to spec memo)

The issue body lists hard-delete vs. soft-delete (deleted_at column + SUPERSEDES edge) as a design choice. Spec memo (docs/feature-aelf-delete-cli.md) picks hard-delete + audit-row-in-feedback_history:

  1. Soft-delete requires a schema migration and rewrite of every retrieval path to filter deleted_at IS NULL — large diff with subtle test surface.
  2. There is no consumer for SUPERSEDES edges today.
  3. The audit-row approach preserves "X existed and was deleted at T" forensically, which is the actual user need.

If a future consumer wants recoverable delete (undelete, graph-walks across deleted beliefs), file a separate issue with that consumer named.

MCP aelf_delete port deferred per #382 Track E ratification (A6, 2026-05-04): only confirm (#390) and unlock/promote/demote (#391) ship in v2.0; delete waits on filed user demand AND demonstrated bench impact.

Commit structure

docs(feature-aelf-delete-cli): spec memo for #440
feat(cli): add aelf delete subcommand (#440)
test(cli): unit + integration tests for aelf delete (#440)
feat(slash_commands): add /aelf:delete (#440)
docs(COMMANDS): add aelf delete to command reference (#440)
test(slash_commands): add delete to EXPECTED_COMMANDS (#440)

The EXPECTED_COMMANDS/HIDDEN_SUBCOMMANDS invariant (the slash-file presence test compares both against the directory) means the actual EXPECTED_COMMANDS move had to happen in commit 4 atomically with the slash file. Commit 6 expands the registration comment to document which two test assertions enforce the invariant — kept as a separate commit to make the registration auditably visible.

Acceptance (mirrors issue #440)

  • Spec memo (hard- vs. soft-delete decision + justification): docs/feature-aelf-delete-cli.md
  • Confirmation prompt by default; --yes to bypass.
  • Refuses to delete locked beliefs without --force.
  • Unit tests + integration tests: tests/test_cli_delete_command.py (28 cases — not-found, locked-without-force, prompt mismatch/empty/match, --yes, --force, --force --yes, audit-row source/valence, orphan-survives-cascade, cascade-outgoing/incoming-edges, exact-output).
  • docs/COMMANDS.md entry.
  • Slash command /aelf:delete mirroring unlock.md.
  • EXPECTED_COMMANDS registration in tests/test_slash_commands.py.

Test plan

  • uv run pytest tests/ --ignore=tests/e2e -q — full suite green locally.
  • Targeted: uv run pytest tests/test_cli_delete_command.py tests/test_slash_commands.py -q — 126 passed in 0.57s.
  • All commits SSH-signed (G).
  • Discretion grep clean across the full diff.

Summary by Sourcery

Add a hard-delete CLI and slash command for beliefs with audit logging and safety prompts.

New Features:

  • Introduce aelf delete CLI subcommand to hard-delete a single belief with optional confirmation bypass and forced deletion of locked beliefs.
  • Expose a corresponding /aelf:delete slash command that mirrors the CLI delete behavior.

Enhancements:

  • Record an audit feedback event before belief deletion so deletions leave a persistent forensic trail.
  • Document the new delete command in the commands reference and add a detailed feature spec for the delete CLI behavior.

Tests:

  • Add unit and integration tests covering delete CLI behavior, including not-found, locking, confirmation flows, audit logging, and edge cascade semantics.
  • Extend slash command tests to cover the new delete command and maintain parity between CLI and slash surfaces.

@sourcery-ai

sourcery-ai Bot commented May 5, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements a new aelf delete CLI subcommand and matching slash command that hard-deletes a belief via MemoryStore.delete_belief, with a confirmation flow, lock-override semantics, and an audit-row-in-feedback_history design, plus documentation and comprehensive tests.

Sequence diagram for the new aelf delete CLI flow

sequenceDiagram
    actor User
    participant CLI as aelf_cli
    participant Parser as argparse_parser
    participant DeleteCmd as cmd_delete
    participant Store as MemoryStore
    participant FeedbackHistory as feedback_history

    User->>CLI: aelf delete <belief_id> [--yes] [--force]
    CLI->>Parser: parse argv
    Parser-->>CLI: args(belief_id, yes, force)
    CLI->>DeleteCmd: _cmd_delete(args, out)

    DeleteCmd->>Store: _open_store()
    DeleteCmd->>Store: get_belief(belief_id)
    Store-->>DeleteCmd: belief_or_none

    alt belief not found
        DeleteCmd->>CLI: print stderr belief not found
        DeleteCmd->>Store: close()
        DeleteCmd-->>CLI: return exit_code 1
    else belief found
        alt locked and not force
            DeleteCmd->>CLI: print stderr locked message
            DeleteCmd->>Store: close()
            DeleteCmd-->>CLI: return exit_code 1
        else not locked or force
            alt yes flag not set
                DeleteCmd->>CLI: print stderr belief summary
                DeleteCmd->>User: prompt type first 8 chars
                User-->>DeleteCmd: confirmation_input
                alt input matches prefix
                    Note over DeleteCmd,User: proceed with delete
                else mismatch
                    DeleteCmd->>CLI: print stderr aborted confirmation
                    DeleteCmd->>Store: close()
                    DeleteCmd-->>CLI: return exit_code 1
                end
            else yes flag set
                Note over DeleteCmd: skip confirmation prompt
            end

            DeleteCmd->>FeedbackHistory: insert_feedback_event(belief_id, valence=-1.0, source=user_deleted_or_force)
            DeleteCmd->>Store: delete_belief(belief_id)
            DeleteCmd->>CLI: print stdout deleted message
            DeleteCmd->>Store: close()
            DeleteCmd-->>CLI: return exit_code 0
        end
    end
Loading

Entity relationship diagram for beliefs and feedback_history in delete

erDiagram
    BELIEFS {
        string belief_id PK
        string content
        string lock_level
    }

    FEEDBACK_HISTORY {
        string id PK
        string belief_id
        float valence
        string source
        string created_at
    }

    BELIEFS ||--o{ FEEDBACK_HISTORY : logs_events_for

    %% BELIEFS rows are hard-deleted by delete_belief
    %% FEEDBACK_HISTORY has no foreign key to BELIEFS; audit rows remain as orphans
Loading

File-Level Changes

Change Details Files
Add _cmd_delete implementation wiring a new delete CLI subcommand to hard-delete beliefs with confirmation, lock checks, and audit logging.
  • Introduce _cmd_delete function that loads a belief by id, handles not-found and locked cases, prompts for confirmation unless --yes is set, writes a feedback_history audit row with user_deleted/user_deleted_force, and calls store.delete_belief then prints a success message.
  • Extend argparse parser construction to register a delete subcommand with belief_id positional plus --yes and --force flags, wired to _cmd_delete and described as a hard-delete with audit row.
src/aelfrice/cli.py
Document and expose the new delete operation in CLI docs and slash commands, including command count update and safety semantics.
  • Update commands reference to include delete <belief_id> [--yes] [--force] with detailed semantics, exit codes, and interaction with locks and audit rows, and bump the documented subcommand count from twenty-nine to thirty.
  • Add a feature spec memo describing aelf delete design (hard-delete vs soft-delete, audit trail via feedback_history, confirmation behaviour, lock handling, and out-of-scope items).
  • Introduce a /aelf:delete slash command markdown file mirroring CLI behaviour, emphasizing hard-delete semantics, audit row, confirmation prompt, and that slash invocation does not imply --yes.
  • Register delete in EXPECTED_COMMANDS in the slash command tests, documenting the invariant enforced by tests about slash files vs visible CLI subcommands.
docs/COMMANDS.md
docs/feature-aelf-delete-cli.md
src/aelfrice/slash_commands/delete.md
tests/test_slash_commands.py
Add unit and integration tests validating aelf delete CLI behaviour, including prompts, lock enforcement, audit rows, and cascade semantics.
  • Create a new test module for aelf delete using an isolated DB fixture and in-process CLI harness to cover not-found, locked-without-force, prompt mismatch/empty, prompt match, --yes, --force (with and without --yes), and exact success output.
  • Add integration-style tests that open a real MemoryStore to assert that feedback_history audit rows are written with correct source/valence and survive belief deletion, and that delete cascades to remove outgoing and incoming edges while preserving unrelated beliefs.
tests/test_cli_delete_command.py

Assessment against linked issues

Issue Objective Addressed Explanation
#440 Design and specify the aelf delete semantics (hard vs soft delete, audit-trail discipline) and implement the chosen behavior in the CLI.
#440 Add a safe aelf delete CLI subcommand that hard-deletes a belief with an audit trail, includes a confirmation prompt by default with --yes to bypass, and refuses deletion of locked beliefs unless --force is provided.
#440 Provide tests and documentation (including command reference and related surfaces) for the aelf delete functionality.

Possibly linked issues

  • #[v2.0] aelf delete CLI: PR is the concrete implementation of the aelf delete CLI behavior, safeguards, docs, and tests requested.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@robotrocketscience has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 44 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1a288271-77c5-44ca-8025-1ee6415426df

📥 Commits

Reviewing files that changed from the base of the PR and between de444a1 and da6bfaa.

📒 Files selected for processing (6)
  • docs/COMMANDS.md
  • docs/feature-aelf-delete-cli.md
  • src/aelfrice/cli.py
  • src/aelfrice/slash_commands/delete.md
  • tests/test_cli_delete_command.py
  • tests/test_slash_commands.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-440-cli-delete

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.

@yoshi280 yoshi280 added the attn:review Needs review (PR open, awaiting reviewer) label May 5, 2026
@yoshi280

yoshi280 commented May 5, 2026

Copy link
Copy Markdown
Collaborator Author

[claim:review:Kulili:2026-05-05T19:43:14Z]

@github-actions github-actions Bot added the attn:merge-conflict PR branch needs rebase label May 5, 2026
@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown

This PR is now behind main. Rebase locally so your commit signatures stay intact:

git fetch origin && git checkout 'feat/issue-440-cli-delete' && git rebase origin/main
# resolve conflicts if any, then
git push --force-with-lease

Auto-rebase was removed because the bot has no signing key; rebasing as the bot strips author signatures and the required_signatures rule on main then blocks the merge. See #341.

@sourcery-ai sourcery-ai 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.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@yoshi280

yoshi280 commented May 5, 2026

Copy link
Copy Markdown
Collaborator Author

Reviewed at 2889231. All 6 commits signed (G), CI green, discretion grep clean. Code review:

  • Audit-row-in-feedback_history design is sound: row survives the cascade because feedback_history has no FK to beliefs (verified in store.py:87 schema). Source labels user_deleted / user_deleted_force differentiate the two paths cleanly.
  • Confirmation pattern (type first 8 chars of id) is a good footgun guard.
  • 22-test coverage: not-found, locked-without-force, prompt-mismatch (3 paths), --yes, --force --yes, --force-without-yes-still-prompts, audit row written, force source label, audit row survives cascade, edges cascade in both directions. Comprehensive.
  • /aelf:delete slash command correctly does NOT imply --yes (safety note in objective body).

Blocked on FF: branch is no longer an ancestor of github/main after PR #449 merged. Rebase needed. No structural conflict expected (#449 was docs-only addition); should be a clean rebase.

Flagging attn:merge-conflict; releasing review claim. Re-flag attn:review after rebase + force-push.

@yoshi280 yoshi280 added attn:merge-conflict PR branch needs rebase and removed attn:review Needs review (PR open, awaiting reviewer) attn:merge-conflict PR branch needs rebase labels May 5, 2026
@yoshi280

yoshi280 commented May 5, 2026

Copy link
Copy Markdown
Collaborator Author

[release:review:Kulili:2026-05-05T19:45:14Z]

@yoshi280

yoshi280 commented May 6, 2026

Copy link
Copy Markdown
Collaborator Author

[claim:review:Toug:2026-05-06T21:03:33Z]

@robotrocketscience
robotrocketscience force-pushed the feat/issue-440-cli-delete branch from 2889231 to ba6da44 Compare May 6, 2026 21:04
Hard-delete one belief via MemoryStore.delete_belief. Writes an audit
row to feedback_history (valence=-1.0, source=user_deleted[_force])
before the cascade. Locked beliefs require --force; confirmation prompt
requires typing the first 8 chars of the id (--yes to bypass).
Temporarily placed in HIDDEN_SUBCOMMANDS until the slash file lands.
Covers: not-found, locked-without-force, prompt-mismatch, prompt-match,
--yes path, --force path, --force --yes, audit-row written to
feedback_history (source and valence), cascade through edges (src and
dst), and output string exactness.
Mirrors unlock.md. Includes the spec-required safety note: the slash
form does not imply --yes; the invoking surface must let the user
respond to the confirmation prompt. Moves "delete" from HIDDEN_SUBCOMMANDS
to EXPECTED_COMMANDS in the slash-command test (required for
test_no_extra_files_in_slash_commands_dir to pass).
Entry follows the unlock row (lifecycle siblings). Covers: cascade scope,
audit-row details, confirmation prompt mechanic, --yes/--force flags,
and exit codes. Updated subcommand count from twenty-nine to thirty.
Expands the registration comment to explicitly document which two
test assertions enforce the invariant (no-extra-files + cli-surface
match). Confirms "delete" is in EXPECTED_COMMANDS and absent from
HIDDEN_SUBCOMMANDS.
@robotrocketscience
robotrocketscience force-pushed the feat/issue-440-cli-delete branch from ba6da44 to da6bfaa Compare May 6, 2026 21:06
@robotrocketscience
robotrocketscience merged commit da6bfaa into main May 6, 2026
20 checks passed
@robotrocketscience
robotrocketscience deleted the feat/issue-440-cli-delete branch May 6, 2026 21:08
@yoshi280

yoshi280 commented May 6, 2026

Copy link
Copy Markdown
Collaborator Author

[release:review:Toug:2026-05-06T21:08:27Z]

@yoshi280 yoshi280 removed the attn:merge-conflict PR branch needs rebase label May 6, 2026
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.

[v2.0] aelf delete CLI — sibling of aelf unlock, ship-as-deferred-by-default

2 participants