Skip to main content

internal/infra/pg/entitypurge.go

internal/infra/pg · 196 lines · 3 declarations · source

Declarations

type EntityPurge

type EntityPurge struct {
EntityID string `json:"entity_id"`
Name string `json:"canonical_name"`
Reason string `json:"reason,omitempty"`
// Previewed is true when nothing was committed: this is what a confirmed call would do.
Previewed bool `json:"previewed"`
// Removed is per table, so a reader can see that the words were not among them.
Removed map[string]int `json:"removed"`
// Residual counts, after the deletes and in the same transaction, what still refers to the
// entity. Anything but zero is a purge that did not do what it claimed.
Residual map[string]int `json:"residual"`
// Subjects is how many distinct people contributed the claims being removed, and Anonymous is
// how many of the claims came from turns attributed to nobody. An entity is shared by
// construction, so a purge reaches other people's knowledge; the number is reported before the
// caller confirms rather than discovered afterwards.
Subjects int `json:"contributing_subjects"`
Anonymous int `json:"claims_from_unattributed_turns"`
// Observations is how many source turns supported the removed claims. They are NOT removed: the
// words a person said are not the system's inference about them, and an entity purged because
// the extractor was wrong must not take the evidence of that error with it.
Observations int `json:"source_turns_kept"`
CompletedAt time.Time `json:"completed_at"`
}

EntityPurge is what a purge did, or what it would have done.

Why one type serves both

The preview is the purge, rolled back. Counting what a delete would remove with one set of queries and removing it with another is two predicates wearing one name, and they diverge on the day somebody edits one — which is the day an operator approves a preview and gets something else. So the transaction runs in full either way and is committed only when the caller confirmed.

source

method EntityPurge.Clean

func (p EntityPurge) Clean() bool

Clean reports whether nothing that should have gone survived.

source

method RecordStore.PurgeEntity

func (s *RecordStore) PurgeEntity(ctx context.Context, scope, id, reason string, confirm bool, record domain.AuditEntry) (EntityPurge, error)

PurgeEntity removes one entity and the claims that stand on it, and reports what that cost.

What it is for, and what it is not

An entity is an inference: the extractor decided that these words name a thing and that thing is this node. When that inference is wrong — two people merged into one, a phrase read as a company — every claim anchored there is wrong with it, and no erasure reaches the problem, because erasure is about a person's data and this is about the system's own mistake. So this is the operation for withdrawing a node, and its receipt is shaped like an erasure's for the same reason: a count of what went and a count of what is still there, taken against the same predicate inside one transaction.

It is not erasure and must never be offered as it. The turns stay, their messages stay, and a rebuild from those turns can propose the entity again — which is correct, because the words have not changed and the next extractor may be better. An operator who needs the words gone wants erasure by subject or by source.

The ledger row commits with the purge

A confirmed purge writes its own ledger row, with the rows it removed as the magnitude, inside the transaction that removes them. Written after the commit, a failed ledger insert left a purge done and nowhere recorded — and a purge, unlike a read, is the operation an auditor asks about. Now the two commit together or not at all. A preview rolls back and keeps its row on the ordinary path, because nothing it did survives to be accounted for.

source