Repository navigation
fix(cortex): explicit forget cascade and hosted scoped erasure - #243
Conversation
Reworked the forget log entry path to reduce duplication and make the control flow easier to follow. Behaviour is unchanged. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The forget endpoint now reads a cascade field that defaults to derived_only, which drops beliefs derived from the named events while keeping the events themselves, and rejects any cascade other than derived_only or redact_events with a 400. This matches the documented cascade semantics so callers can remove derived beliefs without destroying their source events. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test covering that every forget request sent by the engine explicitly names the redact_events cascade, since CortexDB's default derived_only cascade would leave the stored events in place. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The erase path was removing entries by their position in the log, which shifted as soon as any earlier entry was deleted and could drop the wrong record. It now looks entries up by id so each erase targets exactly the entry requested. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Erase and descriptor paths now propagate transport errors rather than treating a failed request as an empty result, so callers can distinguish a genuine miss from an unreachable backend. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a testing module with routes that exercise the cortex integration endpoints, giving integration tests a way to drive the cortex surface without standing up the full stack. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace the test asserting the hosted wire refuses to erase with tests for the new behaviour: erasure goes through the backend passthrough, releases the write key, and polls a running erasure until it settles, with a non-completing erasure surfacing as an error. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The Cortex integration docs now describe the hosted erasure path through the backend's `memory/v1/erasures` passthrough, including polling a running erasure until it settles, and note that forget always names the `redact_events` cascade because CortexDB's default keeps the events. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the doc comment on the erasure test to note that the hosted `memory/v1/erasures` passthrough is covered by the wire double until it is live on the backend, rather than stating the hosted wire has no erasure route. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Tiny Sweeper reviewThis revision lands hosted scoped erasure through the TinyHumans backend's `memory/v1/erasures` passthrough, explicit forget cascades, and Direct erasure polling, with a thorough wire-double test suite. Two findings from earlier pushes remain open: the hosted erasure retry loop generates a fresh idempotency key per attempt rather than reusing one across the retry of the same destructive request, and the hosted passthrough and the forget cascade are covered only against the repository's own wire double (the live backend route is not yet available, per the live test comment). One lane raised a missing overflow test for the hosted scope count, though the diff shows a `usize::try_from` guard already exists in `log/erase.rs`; no test drives it. State: Changes requested Review snapshot
Completeness: Complete What changedOn the TinyHumans wire, an erase narrower than the whole tree now goes scope by scope through the backend's `memory/v1/erasures` passthrough (`{scope, audit_note}` only, answered unwrapped in CortexDB's dialect) instead of refusing with `Unsupported`; the whole tree still erases in one `DELETE memory`, and a missing route (404) still surfaces as `Unsupported` so callers can fall back to `forget`. The forget body now names `cascade: "redact_events"` explicitly, since CortexDB's `derived_only` default keeps the events. `Log::erase` returns an `Erased` struct (scope count plus erasure ids) rather than a bare id, and `EraseReport` propagates the backend's counts and receipts. Direct erasures that answer `running` are polled at `v1/erasures/{id}` under a configurable `Timing::erasure` deadline with strict status validation. Responses on hosted paths under `memory/v1/` are parsed without the `{success,data}` envelope, and failure parsing falls back from `errorCode` to `error_code` when the former is unusable. Features
Tests
Findings
Previously reported and still active
Resolved this pass
Before merge
Agent review detailscritique
security
tests
commits
description
e2e
Evidence and run details
|
|
Warning Review limit reached
This review includes 4 billable files and costs up to $1.00. Or wait 25 minutes for your next included review. View limit detailsLimit details: You’ve used the included review currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (4)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (18)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe Cortex integration now specifies the ChangesForget Cascade
Cortex Erasure
Priority: ➖ Normal Estimated code review effort: 4 (Complex) | ~45 minutes Change: Bug fix Sequence Diagram(s)sequenceDiagram
participant Engine as erase_scopes
participant Log as Log::erase
participant Hosted as memory/v1/erasures
participant Direct as Direct v1/erasures
Engine->>Log: erase each scope
alt TinyHumans wire
Log->>Hosted: POST scope and audit_note
Hosted-->>Log: synchronous erasure result
else Direct wire
Log->>Direct: POST scope with confirm_all
Direct-->>Log: erasure ID and initial status
opt status is running
Log->>Direct: GET erasure status
Direct-->>Log: updated status
end
end
Log-->>Engine: scope count and erasure IDs
Suggested reviewers: Merge Risk: ⚪ Minimal · up to The change adds hosted scoped erasure, Direct erasure polling, and an explicit forget cascade, with test coverage in the repository. No concrete merge-blocking defect was found. Live-backend verification of the hosted route remains pending until the backend ships it. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to Deletion behavior improves, but explicitly listing every memory kind permits broad hosted erasure without the documented whole-tree confirmation. Tenant isolation and recovery after partial deletion also depend on backend guarantees that could not be verified. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
A rabbit checks each scope with care, Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 76cf658b28
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Requesting changes: 1 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0317 · 627,945 in / 34,682 out · 49,401 cached (8%) · gpt-5.6-luna, glm-5.3-flash, deepseek-v4.1-flash
critique: $0.0173 · 319,357 in / 18,249 out · 36,615 cached (11%) · gpt-5.6-luna, glm-5.3-flash
security: $0.0138 · 244,223 in / 10,311 out · 12,530 cached (5%) · gpt-5.6-luna
tests: $0.0002 · 25,822 in / 2,143 out · 128 cached (0%) · glm-5.3-flash
description: $0.0001 · 12,322 in / 264 out · 64 cached (1%) · glm-5.3-flash
e2e: $0.0001 · 13,718 in / 1,150 out · 64 cached (0%) · glm-5.3-flash
Keep main's whole-tree DELETE memory erase on the hosted wire and route
anything narrower through memory-api's scoped erasure passthrough
(memory/v1/erasures: {scope, audit_note}, synchronous, retried on a
retriable 502 ERASURE_INCOMPLETE, 404 reported as Unsupported).
Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f77ef497dc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Requesting changes: 1 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0368 · 722,784 in / 66,515 out · 85,646 cached (12%) · gpt-5.6-luna, glm-5.3-flash
critique: $0.0188 · 378,443 in / 29,841 out · 50,221 cached (13%) · gpt-5.6-luna, glm-5.3-flash
security: $0.0171 · 254,358 in / 25,496 out · 32,225 cached (13%) · gpt-5.6-luna
tests: $0.0004 · 36,042 in / 5,575 out · 1,600 cached (4%) · glm-5.3-flash
description: $0.0002 · 17,001 in / 1,593 out · 1,408 cached (8%) · glm-5.3-flash
e2e: $0.0002 · 18,451 in / 2,174 out · 64 cached (0%) · glm-5.3-flash
Introduce erase operations across the cortex log and engine so stored entries can be removed on demand. Supporting test helpers and a transport failure path were added to exercise the new behaviour. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Cover the direct engine path, erase behaviour, and transport failure handling with new integration tests so regressions in these areas are caught before they reach the higher-level flows. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replaced the log_for_tests helper call with CortexLog::default in the cascade validation test, and reformatted two long expressions in the erase tests and testing routes to satisfy rustfmt. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ed0e299027
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Requesting changes: 2 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0413 · 787,409 in / 59,166 out · 53,801 cached (7%) · gpt-5.6-luna, glm-5.3-flash
critique: $0.0212 · 399,967 in / 23,650 out · 30,523 cached (8%) · gpt-5.6-luna, glm-5.3-flash
security: $0.0192 · 280,786 in / 27,315 out · 23,278 cached (8%) · gpt-5.6-luna
tests: $0.0002 · 20,980 in / 2,170 out · 0 cached (0%) · glm-5.3-flash
description: $0.0002 · 20,794 in / 1,845 out · 0 cached (0%) · glm-5.3-flash
e2e: $0.0002 · 22,303 in / 1,788 out · 0 cached (0%) · glm-5.3-flash
Hosted erasure retries now trigger solely on the ERASURE_INCOMPLETE code, since any other failure leaves the outcome unknown and repeating a destructive request would hide it. Scope counts saturate instead of overflowing, and a Direct erasure answer that omits status is treated as completed without polling. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
|
Correction to the thread replies of the second round: those fixes (retry only on ERASURE_INCOMPLETE, saturating erased-scope count, bounded-poll-count timeout test, synchronous-status test) landed in 9f438b4, not in the commit hash quoted there. |
There was a problem hiding this comment.
Requesting changes: 2 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0278 · 566,340 in / 47,130 out · 55,097 cached (10%) · gpt-5.6-luna, glm-5.3-flash
critique: $0.0123 · 227,662 in / 17,402 out · 28,111 cached (12%) · gpt-5.6-luna, glm-5.3-flash
security: $0.0145 · 224,643 in / 19,768 out · 26,858 cached (12%) · gpt-5.6-luna
tests: $0.0004 · 45,922 in / 3,979 out · 0 cached (0%) · glm-5.3-flash
description: $0.0002 · 21,629 in / 1,652 out · 64 cached (0%) · glm-5.3-flash
e2e: $0.0002 · 22,936 in / 1,339 out · 0 cached (0%) · glm-5.3-flash
A hosted erasure answer whose `scopes` count exceeds the platform's `usize` range was silently clamped to `usize::MAX`, hiding a malformed response. It now returns an engine error naming the scope and offending count, and a test covers the string-typed count case. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
Requesting changes: 3 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0159 · 330,129 in / 27,582 out · 25,779 cached (8%) · gpt-5.6-luna, glm-5.3-flash
critique: $0.0068 · 114,151 in / 11,276 out · 13,237 cached (12%) · gpt-5.6-luna, glm-5.3-flash
security: $0.0081 · 124,675 in / 10,596 out · 12,542 cached (10%) · gpt-5.6-luna
tests: $0.0002 · 21,852 in / 1,118 out · 0 cached (0%) · glm-5.3-flash
description: $0.0002 · 21,814 in / 872 out · 0 cached (0%) · glm-5.3-flash
e2e: $0.0002 · 23,173 in / 368 out · 0 cached (0%) · glm-5.3-flash
Add a test double hook that forces the status of polled Direct erasure answers, and use it to check that a status which is not a known string never completes the erasure. Also cover a hosted scope erased by an engine that never held it, and relax the poll-count assertion to a lower bound so it no longer depends on backoff timing. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5bcab7038d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Requesting changes: 3 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0217 · 500,180 in / 32,268 out · 45,071 cached (9%) · gpt-5.6-luna, glm-5.3-flash
critique: $0.0108 · 226,454 in / 12,217 out · 22,965 cached (10%) · gpt-5.6-luna, glm-5.3-flash
security: $0.0098 · 155,099 in / 12,236 out · 15,898 cached (10%) · gpt-5.6-luna
tests: $0.0004 · 47,626 in / 1,979 out · 3,072 cached (6%) · glm-5.3-flash
description: $0.0002 · 22,600 in / 789 out · 1,408 cached (6%) · glm-5.3-flash
e2e: $0.0002 · 23,960 in / 2,212 out · 1,728 cached (7%) · glm-5.3-flash
A hosted erasure response that claims scopes were erased but returns no erasure_ids is now treated as an incompatible answer rather than a success, since the receipts are the audit trail of a destructive call. The test double's forced poll status also no longer consumes the running budget, and new malformed-answer cases cover the missing-receipt responses. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 50ff48af0d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Requesting changes: 3 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0252 · 531,932 in / 47,594 out · 44,494 cached (8%) · gpt-5.6-luna, glm-5.3-flash
critique: $0.0137 · 262,815 in / 19,549 out · 26,842 cached (10%) · gpt-5.6-luna, glm-5.3-flash
security: $0.0103 · 145,472 in / 16,252 out · 16,116 cached (11%) · gpt-5.6-luna
tests: $0.0005 · 50,805 in / 8,127 out · 0 cached (0%) · glm-5.3-flash
description: $0.0002 · 22,910 in / 999 out · 1,408 cached (6%) · glm-5.3-flash
e2e: $0.0002 · 24,211 in / 454 out · 0 cached (0%) · glm-5.3-flash
Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
Requesting changes: 2 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0131 · 356,846 in / 33,210 out · 32,691 cached (9%) · gpt-5.6-luna, glm-5.3-flash
critique: $0.0050 · 83,687 in / 9,427 out · 9,166 cached (11%) · gpt-5.6-luna, glm-5.3-flash
security: $0.0065 · 102,123 in / 7,945 out · 10,533 cached (10%) · gpt-5.6-luna
tests: $0.0006 · 71,023 in / 7,042 out · 5,504 cached (8%) · glm-5.3-flash
description: $0.0002 · 23,052 in / 1,170 out · 1,408 cached (6%) · glm-5.3-flash
e2e: $0.0005 · 51,365 in / 5,268 out · 5,952 cached (12%) · glm-5.3-flash
| #[tokio::test] | ||
| async fn the_hosted_wire_refuses_to_erase_without_a_request() { | ||
| async fn the_hosted_wire_erases_a_subtree_through_the_backend_passthrough() { | ||
| let (endpoint, state) = hosted_double().await; |
There was a problem hiding this comment.
Exercise hosted passthrough against a real CortexDB backend
The hosted subtree and exact-scope tests use hosted_double, so they only prove the client and the test double agree. They do not verify the actual memory/v1/erasures request, backend scope semantics, or response parsing against CortexDB. Add an integration case using the repository's real backend harness for the hosted passthrough path.
[RULE] missing-real-backend-coverage ·
|
|
||
| #[tokio::test] | ||
| async fn an_incomplete_hosted_erasure_is_retried() { | ||
| let (endpoint, state) = hosted_double().await; |
There was a problem hiding this comment.
Exercise hosted polling against a real CortexDB backend
The incomplete hosted erasure retry path is validated only by a configured double. That does not establish that a real backend returns the incomplete response shape expected by the retry logic or that the eventual completion is handled correctly. Add a real-backend polling/retry integration test.
[RULE] missing-real-backend-coverage ·
| #[tokio::test] | ||
| async fn the_hosted_wire_refuses_to_erase_without_a_request() { | ||
| async fn the_hosted_wire_erases_a_subtree_through_the_backend_passthrough() { | ||
| let (endpoint, state) = hosted_double().await; |
There was a problem hiding this comment.
Exercise the forget cascade against a real CortexDB
The changed hosted tests cover erasure requests through a double, but none exercises the caller's fallback from an unsupported scoped erasure into the forget cascade against a real CortexDB. Because this behavior depends on backend route availability and the resulting deletion semantics, add an integration test using the real backend harness.
[RULE] missing-real-backend-coverage ·
| }; | ||
| // The deadline bounds the sleep and every retry of the request, | ||
| // not just the gap between polls. | ||
| let job = tokio::time::timeout(remaining, poll) |
There was a problem hiding this comment.
Cover the direct erasure polling timeout path
The new deadline behavior is a user-visible failure path, but this change adds no deterministic test that exercises a job remaining in a pending status until the timeout expires. Without one, regressions that omit the timeout, poll past the deadline, or return the wrong error can pass unnoticed. Add a paused-time test with a pending response and assert that it returns Error::Unavailable.
[RULE] missing-timeout-test ·
| let mut attempt = 0; | ||
| let answer = loop { | ||
| attempt += 1; | ||
| match self |
There was a problem hiding this comment.
Reuse one idempotency key across hosted erasure retries
When the backend returns ERASURE_INCOMPLETE, this loop sends the same destructive request again, but the request carries no idempotency key. If the first request actually reached the backend and completed while its response was lost or classified as incomplete, the retry is a separate operation rather than a deduplicated replay. That can produce multiple erasure records and inconsistent audit results. Generate one idempotency key before the loop and attach it to every hosted attempt, or avoid retrying unless the transport/backend provides equivalent deduplication.
Additional critique observation
Preserve one idempotency key across hosted erasure retries
[RULE] idempotent-retry
When the backend returns ERASURE_INCOMPLETE, this loop sends another POST, but it does not create or pass an idempotency key. The hosted wire contract says writes claim Idempotency-Key; if the first request erased the scope but lost its response, the retry is a distinct destructive operation and can create duplicate erasure records or otherwise be processed twice. Generate one key before the loop and attach that same key to every attempt, or use the transport API that provides this guarantee.
[RULE] missing-idempotency-key ·
| serde_json::json!({ "erased": true, "scopes": "1", "erasure_ids": [] }), | ||
| // Scopes erased, but no receipt for them. | ||
| serde_json::json!({ "erased": true, "scopes": 1, "erasure_ids": [] }), | ||
| serde_json::json!({ "erased": true, "scopes": 2, "erasure_ids": null }), |
There was a problem hiding this comment.
Cover hosted erasure counts outside the platform range
The malformed-answer cases cover string counts and missing receipts, but none supplies a numeric count larger than usize. The conversion is a platform-range boundary and needs an explicit oversized numeric response to ensure it is rejected rather than truncated or accepted inconsistently.
[RULE] missing-overflow-test ·
| } | ||
|
|
||
| /// Direct only: the hosted wire has no erasure route. | ||
| /// Direct only for now: the hosted `memory/v1/erasures` passthrough is |
There was a problem hiding this comment.
Exercise the hosted erasure passthrough against a real backend
The hosted passthrough's wire behaviour is asserted only against the test double: the real backend's dialect (no envelope, error_code in failures, the exact 502 ERASURE_INCOMPLETE shape, whether it truly omits confirm_all acceptance) is unverifiable from doubles. The live test file still marks erasure as Direct only. Once the route is live on the backend, extend the live test to erase a subtree through the hosted engine.
[RULE] live-coverage-gap ·
| /// The cascade every removal sends: the named events go, with everything | ||
| /// derived from them. Never left to the engine's default (`derived_only`), | ||
| /// which keeps the events. | ||
| pub(crate) const FORGET_CASCADE: &str = "redact_events"; |
There was a problem hiding this comment.
Exercise the forget cascade against a real CortexDB
The cascade: "redact_events" claim — that it deletes the events, unlike derived_only — is pinned only against the crate's own double, which was written in the same commit to model the desired behaviour. If real CortexDB rejects the value or spells it differently, every forget fails in production with no test having run against the real server. Extend the live CortexDB test to store an item, forget it by id, and assert it is gone.
[RULE] live-coverage-gap ·
Summary
Makes CortexDB deletions actually delete.
/v1/forget(and the hostedmemory/forget) now sendscascade: "redact_events". CortexDB's default,derived_only, removes what was derived from the events and leaves the events in place, so a forget without a cascade did not remove anything the user wrote (memory-audit S1,docs/memory-scopes.md§7). memory-api tombstones forgets bymemory_ids, which are what this crate sends.DELETE memory. Anything narrower now erases scope by scope, as on Direct, instead of refusing withUnsupported. It goes through the backend'smemory/v1/erasurespassthrough of memory-api's scoped erasure (tinyhumansai/cortexdb-saas#21):{scope, audit_note}, with noconfirm_all, because memory-api refuses unknown fields{erased: true, scope, scopes, erasure_ids};scopes: 0is still a success502 ERASURE_INCOMPLETEis retried up to 3 timesUnsupported, so callers can fall back to a forgetmemory/v1/*answers without the{success,data}envelope, and hosted errors also readerror_code.running/pending/queued/acceptedanswer is polled atv1/erasures/{id}for up to 5 minutes. Any final status other thancompletedis an error.derived_onlykeeps events, an unknown cascade gets a 400) and serves memory-api's scoped erasure contract unwrapped.Depends on tinyhumansai/backend#1410 (the
/memory/v1/erasurespassthrough) and tinyhumansai/cortexdb-saas#21.Tests
every_forget_names_the_cascade_that_removes_the_eventsthe_hosted_wire_erases_a_subtree_through_the_backend_passthroughthe_hosted_erasure_names_only_the_fields_memory_api_acceptsan_incomplete_hosted_erasure_is_retrieda_hosted_erasure_that_stays_incomplete_is_unavailablea_backend_without_the_scoped_erasure_route_is_unsupporteda_narrower_hosted_erase_goes_scope_by_scope_not_whole_memorya_running_direct_erasure_is_polled_until_it_completes,a_direct_erasure_that_does_not_complete_is_an_errorUnsupported.cargo test --workspace --all-featuresandcargo clippy --workspace --all-features --all-targetspass.Summary by CodeRabbit