Skip to contents

Graft can preview exclusions and verify the exact objects retained in a separate store. This is one step in a permanent Forget workflow. The application must still authorize Forget, retire the old generation, prevent old backups from being admitted, and arrange disposal. The example uses synthetic content and leaves the source unchanged.

Preview the full effect

A correction leaves the earlier revision in place. Exclusion begins with exact references from the application and follows every declared dependency backwards, so reports derived from excluded evidence are excluded too. To forget a logical record completely, the application must supply every revision covered by the authorization. Selecting one revision does not select other revisions with the same artifact ID.

## 
## Attaching package: 'graft'
## The following object is masked from 'package:base':
## 
##     Recall
source_path <- tempfile("source-")
target_path <- tempfile("replacement-")
source <- graft_store(source_path, create = TRUE)
private <- graft_save(
  source, "Synthetic private observation", id = "note", media_type = "text/plain"
)
report <- graft_save(
  source, "Interpretation of the observation", id = "report",
  media_type = "text/plain", dependencies = private
)
survivor <- graft_save(
  source, "Independent corrected observation", id = "note", media_type = "text/plain"
)
accepted <- graft_accept(
  source, report, stream = "review", key = "review-1", expected = NULL,
  actor = "synthetic-reviewer",
  reason = "Example review", purpose = "research"
)
plan <- graft_plan_replacement(source, forget = list(private))
vapply(plan$removed$artifacts, function(ref) ref$id, character(1))
## [1] "report" "note"
plan$removed$selections
## [1] "07051f563750d861dda14db8925f166b9fff398b40a898ebfd1281f721174a51"
plan$removed$streams
## [1] "review"

The plan excludes the private revision, its report, the selection, and the entire review stream. The stream remains excluded even if it later accepted a correction, because its earlier records retain the forgotten selection. Independent streams keep their original history. A fresh acceptance of surviving material from an excluded stream requires a new application review.

Declare dependencies when saving an artifact. Graft cannot discover private passages copied into unrelated payloads, titles, IDs, or decision reasons. The application supplies any additional artifact roots and forget_streams. Shared content bytes remain when a surviving revision still uses them. Vocabulary releases follow the same exact dependency rules, including dictionary sources and literal context.

Copy and verify the remaining artifacts

The target must be empty. Graft rejects an edited plan or changed source before copying. After copying, it verifies every target object and rechecks the source.

target <- graft_store(target_path, create = TRUE)
verified <- graft_replace(source, target, plan)
identical(verified, plan$target)
## [1] TRUE
graft_read(target, survivor)@data
## [1] "Independent corrected observation"
# The source is deliberately unchanged by replacement.
graft_read(source, private)@data
## [1] "Synthetic private observation"

Excluded references and selections cannot be read from the replacement. The application must reject references to the retired generation. It must not silently resolve an old reference against the replacement, even when its content survived. Graft’s low-level reads do not check whether the generation is admitted.

A partial target may remain after failure. Keep it quarantined and retry with a fresh empty store. These functions never delete either store or expose a candidate to application readers. With PostgreSQL, commit the application-owned transaction successfully before treating a verified candidate as retained.

Inspect a complete logical image

manifest <- graft_manifest(target)
manifest$format
## [1] "graft-artifact-manifest/1"
length(manifest$objects)
## [1] 2
identical(manifest$id, verified$id)
## [1] TRUE
unlink(c(source_path, target_path), recursive = TRUE)

A manifest records each object’s kind, storage key, byte count, and digest. It includes historical revisions, selections, and decisions, rather than only current accepted heads. Well-formed orphan content is inventoried but omitted from a replacement. Unknown entries, staging files, symbolic links, corrupt bytes, incomplete dependencies, or invalid history prevent certification.

The manifest and plan can contain private identifiers and linkable hashes. Keep them as restricted operational material rather than public audit logs. Their digests provide integrity; they provide neither authenticity nor freshness.

By default, the inventory is limited to 10,000 objects and 64 MiB of stored bytes. max_metadata_bytes limits selection metadata and each complete decision journal. Artifact payload and revision limits also come from the store handle. A local store’s manifests, plans, and copies hold its store lock exclusively, so writers in other processes wait for them rather than changing the store midway. An application switching to a replacement holds the source’s lock across the plan, the copy, and the switch with graft_with_store_lock(), and checks inside each of its own writes that the store is still current. PostgreSQL scope locks run inside application-owned transactions; copying between stores is not a distributed transaction.

Clean up after failed saves

A failed save can leave content that no revision references. A plan with nothing to forget copies everything else:

cleanup <- graft_plan_replacement(store)
graft_replace(store, graft_store(fresh_path, create = TRUE), cleanup)

The application then switches to the fresh store as it would after a Forget.

Keep the restore decision outside the backup

Before replacing an affected Reader generation, the application must retire it durably in an independent Forget journal. The journal must remain available when an older content backup is restored. The application checks whether access is currently allowed for reads, background jobs, cache delivery, imports, and restore. Missing or stale journal state denies access. A checksum-matching old backup remains retired.

The application also inventories external exports, provider context, Commons working files, indexes, and reuse checkpoints. Deleting a Conversation does not silently delete a separately accepted Memory or Artifact. Each requires the appropriate preview and authorization.

Generation retirement prevents an application from resurrecting content only when every access path enforces it. It does not destroy offline copies, invalidate available encryption keys, or prove physical secure erasure. Graft’s synthetic replacement and restore tests do not establish those production guarantees.

The Forget and restore gate remains open until Rill’s actual workflow, independent durable journal, closed backups, and failure recovery are verified.

Next implementation tasks are Rill Reader Forget and publication and journal recovery. Graft now supplies closed backup bundles and strict restore verification. These mechanics do not complete the application recovery protocol.