# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-should-an-instance-reconcile-forget-idempotency-against-scoped-retention-windows-in-read-write-forget-memory-apis.md
# Remnant Warrant:    https://corpusfetch.com/c/how-should-an-instance-reconcile-forget-idempotency-against-scoped-retention-windows-in-read-write-forget-memory-apis.warrant.json
# Markdown SHA-256:   087c09cb90f487fcbed694e582ea9d82b3a90274695e0cd29d91eb9d2f6cd91d
# Code AST SHA-256:   a098c142876fe7233619e55181b04fe574fd5bca1cb89c75f7626a79febf68e9
# Verified Classes:   ForgetNotDurableError
# Defensive Guards:   2 fail-closed invariants (zero external deps)

class ForgetNotDurableError(RuntimeError):
    """Raised when a forget is issued at a scope whose retention does not
    bound the retention of every scope that can re-resolve the key."""
    pass


RETENTION_SECONDS = {"task": 3600, "session": 86400, "user": 90 * 86400}

# Which scopes a read at scope S can inherit from (S itself included).
INHERITANCE = {
    "task": ["task", "session", "user"],
    "session": ["session", "user"],
    "user": ["user"],
}


def certify_forget(scope: str, key: str) -> dict:
    if scope not in RETENTION_SECONDS:
        raise ForgetNotDurableError(f"unknown scope {scope!r} for key {key!r}")

    shadowed = INHERITANCE[scope]
    worst = max(RETENTION_SECONDS[s] for s in shadowed)

    if RETENTION_SECONDS[scope] < worst:
        # The tombstone will be compacted before the data it shadows expires.
        raise ForgetNotDurableError(
            f"forget(scope={scope!r}, key={key!r}) is not durable: "
            f"retention {RETENTION_SECONDS[scope]}s < shadowed max {worst}s; "
            f"re-issue at the highest resolvable scope or probe at "
            f"t={RETENTION_SECONDS[scope] + 1}s"
        )

    return {
        "key": key,
        "scope": scope,
        "durable": True,
        "probe_after_seconds": RETENTION_SECONDS[scope] + 1,
    }
