Skip to content

Latest commit

 

History

History
317 lines (223 loc) · 11.5 KB

revision.mdx

File metadata and controls

317 lines (223 loc) · 11.5 KB
title Revision
description Atomic revision operations with full audit trails: supersede, fork, retract, merge, contest, reinforce, and penalize.

Revision operations modify the lifecycle and status of existing memory records. All operations are atomic — partial revisions are never externally visible. Every operation appends to the record's audit log, preserving a complete history of changes.

For newly created supersede, fork, and merge records, operation metadata is server-owned. Membrane discards supplied audit history, derives the actor and timestamps from the authenticated operation, rebuilds semantic revision lineage, and inherits lifecycle policy from stored source records while resetting the reinforcement clock. Supplied evidence references and hashes remain attributed assertions; opaque evidence values are not probed as memory IDs. The gRPC boundary accepts at most 100 caller-supplied relations and requires every persisted relation target to exist and satisfy the server write policy before the transaction starts.

At the daemon boundary, revision and salience endpoints validate metadata before delegating to the storage layer. Required record identifiers must be non-empty; record IDs, actor names, rationales, merge source IDs, and contesting references must fit within the public string size limit; replacement records for Supersede, Fork, and Merge must satisfy the same MemoryRecord.Validate invariants as stored records; and Penalize amounts must be non-negative and finite.

Episodic records are **immutable** and cannot be revised. Attempting to revise an episodic record returns a `FAILED_PRECONDITION` error. This is by design: episodic memory is append-only raw experience and serves as evidence for later knowledge formation.

Revision status

Semantic records carry a revision.status field in their payload that transitions through:

Status Meaning
active Record is currently valid
contested Conflicting evidence exists; status unresolved
retracted Record has been withdrawn; salience is 0

Supersede

Atomically replaces an existing record with a new version. The old record's salience is set to 0 and its semantic status is set to retracted. The new record is stored with a supersedes relation pointing to the old record, and the old record receives the inverse superseded_by relation pointing to the replacement.

Both the old and new records are persisted. The old record is not deleted; it is retracted so its audit trail remains queryable.

Request fields

ID of the record to supersede. Must not be episodic. Typed `MemoryRecord` that replaces the old one. For semantic records, the payload must include at least one evidence reference or provenance source. Identifier of the agent or user performing the operation. Human-readable reason for the supersession, stored in both audit entries.

Response

Typed `MemoryRecord` — the newly created record with its assigned ID and provenance populated.

Example

// Supersede a semantic record with a new version
superseded, err := m.Supersede(ctx, oldRecordID, newRec, "agent", "Go version updated")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("New record ID: %s\n", superseded.ID)

Fork

Creates a new record derived from an existing source record. Unlike Supersede, both the source and the forked record remain active. Use Fork to create conditional variants of a fact or procedure without invalidating the original.

The forked record receives a derived_from relation pointing to the source, the source receives the inverse derived_semantic relation pointing to the fork, and an audit entry with action fork is appended to the source record. For semantic payloads, the forked record's revision.status is set to active.

Request fields

ID of the record to fork. Must not be episodic. Typed `MemoryRecord` representing the conditional variant. For semantic records, the payload must include at least one evidence reference or provenance source. Identifier of the agent or user performing the operation. Human-readable reason stored in both audit entries.

Response

Typed `MemoryRecord` — the newly created forked record.

Example

// Fork a record for conditional validity (dev vs. prod)
forked, err := m.Fork(ctx, sourceID, conditionalRec, "agent", "different for dev environment")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Forked record ID: %s\n", forked.ID)

Retract

Marks a record as retracted without deleting it. The record's salience is set to 0. For semantic records, the revision status is set to retracted. The record remains in the store so its audit trail is preserved.

Retracted records have salience 0 and are excluded from active retrieval results by default.

Request fields

ID of the record to retract. Must not be episodic. Identifier of the agent or user performing the retraction. Human-readable reason stored in the audit entry.

Response

RetractResponse is empty. A successful call returns gRPC status OK.

Example

// Retract a record that is no longer valid
err := m.Retract(ctx, recordID, "agent", "no longer accurate")
if err != nil {
    log.Fatal(err)
}

Merge

Atomically combines multiple source records into a single merged record. All source records are retracted (salience set to 0, semantic status set to retracted). The merged record receives derived_from relations for every source, each source receives a derived_semantic relation pointing to the merged record, and each source record receives an audit entry with action merge. For semantic payloads, the merged record's revision.status is set to active.

At least one unique source ID is required. All source records must be non-episodic. The entire operation runs in a single transaction.

Request fields

IDs of the records to merge. Must contain at least one unique ID and no more than 10 000. Typed `MemoryRecord` that consolidates the source records. For semantic records, the payload must include at least one evidence reference or provenance source. Identifier of the agent or user performing the merge. Human-readable reason stored in all audit entries.

Response

Typed `MemoryRecord` — the newly created merged record with `derived_from` relations. Source records are updated with inverse `derived_semantic` relations.

Example

// Merge multiple records into one consolidated record
merged, err := m.Merge(
    ctx,
    []string{id1, id2, id3},
    mergedRec,
    "agent",
    "consolidating duplicates",
)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Merged record ID: %s\n", merged.ID)

Contest

Marks a record as contested, indicating that conflicting evidence exists. The record remains active with its salience unchanged, but its semantic revision status transitions to contested. When contesting_ref names an existing memory record that the server write policy also permits, a contested_by relation is added from the contested record to the conflicting record and the conflicting record receives the inverse contests relation. External, missing, and policy-denied references all follow the same non-relational evidence path, so the response does not disclose whether an inaccessible record ID exists.

Contested records are still returned by retrieval, but their status signals to the agent that the information should be treated with caution.

Request fields

ID of the record to mark as contested. Must not be episodic. ID of the conflicting record or evidence. Graph relations are created only when this resolves to an existing write-authorized memory record. Identifier of the agent or user performing the contest. Human-readable reason stored in the audit entry.

Response

ContestResponse is empty. A successful call returns gRPC status OK.

Example

// Contest a record when conflicting evidence appears
err := m.Contest(
    ctx,
    recordID,
    conflictingRecordID,
    "agent",
    "new evidence contradicts this",
)
if err != nil {
    log.Fatal(err)
}

Reinforce

Boosts a record's salience, signalling that it was useful. This resets the decay clock and increments the audit log with an AuditActionReinforce entry. Reinforcement count is tracked in the metrics via retrieval_usefulness.

Request fields

ID of the record to reinforce. Identifier of the agent or user performing the reinforcement. Maximum 100 000 characters. Human-readable reason for the reinforcement. Maximum 100 000 characters.

Response

ReinforceResponse is empty. A successful call returns gRPC status OK.

Example

// Reinforce a record after it contributed to a successful outcome
err := m.Reinforce(ctx, planRecord.ID, "planner-agent", "plan used successfully")
if err != nil {
    log.Fatal(err)
}

Penalize

Reduces a record's salience by a specified amount. Use this to signal that a record contributed to a failed or unhelpful outcome.

Request fields

ID of the record to penalize. Amount by which to reduce salience. Must be non-negative and finite. Salience will not go below 0. Identifier of the agent or user performing the penalization. Maximum 100 000 characters. Human-readable reason stored in the audit entry. Maximum 100 000 characters.

Response

PenalizeResponse is empty. A successful call returns gRPC status OK.

Example

// Reduce salience of a record that led to a bad outcome
err := m.Penalize(ctx, recordID, 0.2, "build-agent", "procedure produced linker error")
if err != nil {
    log.Fatal(err)
}