Memory as Civic Infrastructure: Retention, Forgetting, and Reconstruction

Community Article Published February 4, 2026

Memory Governance for SIM / SIS / MEM / Genius Library

Draft v0.1 — Non-normative supplement to SI-Core / SI-NOS / SIM/SIS / MEM / ETH specs

This document is non-normative. It describes how to think about and govern memory in a Structured Intelligence (SI) stack: what to store, how to forget, and what can be safely reconstructed.

Normative contracts live in the SIM/SIS specs, SI-Core / SI-NOS design, the Ethical Redaction / GDPR documents, and the Semantic Compression & Genius Replay articles.


0. Conventions used in this draft (non-normative)

This draft follows the portability conventions used in 069/084+ when an artifact might be exported, hashed, or attested (retention policies, lifecycle transitions, erasure/redaction tombstones, compliance bundles, audit records):

  • created_at is operational time (advisory unless time is attested).
  • as_of carries markers only (time claim + optional revocation view markers) and SHOULD declare clock_profile: "si/clock-profile/utc/v1" when exported.
  • trust carries digests only (trust anchors + optional revocation view digests). Never mix markers into trust.
  • bindings pins meaning as {id,digest} (meaningful identities must not be digest-only).
  • Avoid floats in policy-/digest-bound artifacts: prefer scaled integers (*_bp, *_ppm) and integer micro/milliseconds.
  • If you hash/attest legal/procedural artifacts, declare canonicalization explicitly: canonicalization: "si/jcs-strict/v1" and canonicalization_profile_digest: "sha256:...".
  • digest_rule strings (when present) are explanatory only; verifiers MUST compute digests using pinned schemas/profiles, not by parsing digest_rule.

Numeric conventions used in examples:

  • For weights and ratios in [0,1], export as basis points: x_bp = round(x * 10000).

  • For probabilities in [0,1], export as basis points: p_bp = round(p * 10000).

  • For very small probabilities, ppm is acceptable: p_ppm = round(p * 1_000_000).

  • If a clause uses a legal threshold with decimals, either:

    • export value_scaled_int + explicit scale, or
    • keep the raw numeric payload in a referenced artifact and export only its digest (*_ref / *_hash) plus display-only summaries.

Internal computation may still use floats; the convention here is about exported/hashed representations.


1. Why “memory as civic infrastructure”

In SI-Core, memory is not just a technical detail.

It is closer to roads, water, and electricity:

  • Everyone builds on it (Jump engines, evaluators, dashboards, audits).
  • It has long-term externalities (privacy, fairness, institutional memory).
  • Failures are structural, not just local bugs.

We already have:

  • Ethical Redaction / GDPR articles — what must be erasable, what counts as “erased”.
  • Semantic Compression — how to reduce raw streams into goal-aware semantics.
  • Genius Replay Protocol — how to keep and reuse high-value decision traces.

This piece focuses on the lifecycle of memory itself:

From raw → semantic → summarized → forgotten → (optionally) reconstructed.

And how to treat this as governed civic infrastructure rather than “whatever the logging system happens to do.”


2. The memory map: SIM, SIS, Genius Library, EvalTrace, AuditLog

A minimal SI deployment has at least five structurally distinct “memory types”:

2.1 SIM — Semantic Intelligence Memory (operational)

  • Short- to medium-term semantic state used by SI-Core for live decisions.
  • Backed by semantic compression; must be fast, structured, and queryable.
  • Carries strong OBS / ETH / ID invariants.

Typical examples:

  • Current city flood risk state per district.
  • Learner state vectors (mastery, wellbeing, accommodations) for the last 90 days.
  • System health summaries (SLIs/SLOs) for orchestrators.

2.2 SIS — Semantic Intelligence Store (analytical / archival)

  • Longer-term store for semantic history and selected raw archives.

  • Used for:

    • model training and evaluation,
    • forensic analysis,
    • regulatory reporting,
    • replay / simulation.

SIS is where retention policies bite hardest: what stays raw, what becomes structural only, what gets erased.

2.3 Genius Library

From the Genius Replay article:

  • A curated library of Genius Traces:

    • high-GCS, robust, generalizable Jump sequences,
    • with attached OBS / goal surface / ETH / EVAL metadata.
  • Exists on top of SIM/SIS:

    • it references semantic histories,
    • but is conceptually a “code/plan library” more than a “data lake”.

Key property:

Genius Library wants to preserve structure (how we reasoned and acted), while often discarding or aggressively anonymizing who and where.

2.4 EvalTrace

  • Per-experiment / per-E-Jump traces:

    • which populations were assigned to which variants,
    • what outcomes were observed,
    • what causal / statistical evaluations were run,
    • which stop rules fired (if any).

EvalTrace is the memory that makes EVAL and PoLB auditable.

2.5 AuditLog

  • The hash-chained, append-only audit layer:

    • who (ID) did what,
    • with which role / delegation,
    • under which ETH policy,
    • on which memory objects.

AuditLog sits at the MEM × ID × ETH intersection and is usually maximally durable (within legal constraints).

2.6 A mental picture

Very roughly:

             ┌───────────────┐
             │   AuditLog    │  (hash-chained, durable)
             └──────┬────────┘
                    │
          ┌─────────┼─────────┐
          │                   │
      ┌───▼───┐           ┌───▼────────┐
      │ Eval  │           │ Genius     │
      │ Trace │           │ Library    │
      └───┬───┘           └────┬───────┘
          │                    │
      ┌───▼────────┐      ┌────▼───────┐
      │   SIM      │◀────▶│   SIS      │
      │ (operational)     │ (archival) │
      └───────────────────┴────────────┘

This article asks:

For each of these layers, what is retained in raw form, what survives only structurally, what must be forgettable, and what may be reconstructible?


3. What counts as “original”: raw vs structural retention

Before talking about erasure and redaction, we need a vocabulary for “originalness” of memory.

3.1 Four classes of originalness

We’ll use four rough classes:

originalness_levels:
  raw:
    description: "Byte-identical record of the original input or internal state."
    examples:
      - "Sensor tick streams"
      - "Exact chat transcripts"
      - "Full-resolution images/audio"
  
  semantic_reconstructable:
    description: "Semantic units that, together with reference tables, allow partial reconstruction."
    examples:
      - "Per-learner state vectors with stable pseudonyms"
      - "Exact timestamps + detailed context embeddings"
  
  structural_only:
    description: "Abstract structure of decisions and flows, no direct link to individuals."
    examples:
      - "Genius Jump DAGs with anonymized context slots"
      - "EvalTrace schemas without raw IDs"
  
  aggregate:
    description: "Population-level aggregates and statistics."
    examples:
      - "Histogram of mastery progress over a semester"
      - "City-wide p95 flood risk per month"

The core policy question for SIM/SIS/MEM:

For each memory type, what is the maximal originalness level allowed and for how long?

3.2 Policy skeleton

Non-normative example:

memory_retention_policy:
  created_at: "2028-05-01T00:00:00Z"  # operational time (advisory unless time is attested)

  as_of:
    time: "2028-05-01T00:00:00Z"
    clock_profile: "si/clock-profile/utc/v1"

  trust:
    trust_anchor_set_digest: "sha256:..."

  canonicalization: "si/jcs-strict/v1"
  canonicalization_profile_digest: "sha256:..."

  # Pin meaningful identities by {id,digest} (trust anchors included).
  bindings:
    trust_anchor_set:
      id: "si/trust-anchors/example/v1"
      digest: "sha256:..."
    ethical_redaction_contract:
      id: "policy:ethical-redaction-contract/v1"
      digest: "sha256:..."

  sim:
    default_originalness: "semantic_reconstructable"
    max_raw_ttl: "24h"
    max_semantic_ttl: "90d"

  sis:
    default_originalness: "structural_only"
    raw_ttl_by_domain:
      low_risk_ux: "7d"
      education: "30d"
      city_critical: "365d"
    semantic_ttl: "5y"

  genius_library:
    allowed_originalness: "structural_only"
    personal_identifiers: "forbidden"

  eval_trace:
    allowed_originalness: "semantic_reconstructable"
    subject_ids:
      policy: "pseudonymized"
      reidentification_permit: "requires legal basis + ETH approval"

  audit_log:
    # Audit commitments are durable, but subject linkability must be governable.
    payload_originalness: "structural_only"
    subject_linking: "via erasable index (not in immutable entries)"
    erasure_handling:
      tombstone: true
      crypto_shred: true
      index_delete: true
      contract:
        id: "policy:ethical-redaction-contract/v1"
        digest: "sha256:..."

The point is not the exact numbers, but:

  • memory is typed by originalness, and
  • policies are explicit, machine-checkable, and tied into [ETH]/[MEM]/[ID].

4. Operations over memory: erasure, redaction, aggregation, pseudonymization

These four words get overloaded. In SI-Core context we want them structurally precise.

4.1 Erasure (hard forget)

Erasure means:

After the operation, the system cannot recover the erased information, even using all other retained data, except where explicitly allowed by aggregate constraints.

Crucial AuditLog note (to avoid ambiguity):

  • AuditLog is append-only: past audit entries are not rewritten.
  • Erasure is achieved by (a) removing/transforming personal payloads, (b) destroying keys (crypto-shred), and (c) deleting erasable indices that connect subjects to otherwise durable audit commitments.
  • AuditLog may retain structural commitments (hashes, approvals, policy IDs) and tombstones proving that erasure occurred.

Rough contract sketch:

operation: erasure
semantics:
  input:
    subject_id: "learner:1234"
    scope: "all personal data in SIM/SIS/EvalTrace (and any subject-indexed views)"
  guarantees:
    - "Direct identifiers removed or irreversibly transformed."
    - "Quasi-identifiers transformed such that reconstruction risk (RRS) < threshold."
    - "Subject-linking indices removed (or rotated) so durable logs no longer resolve to the subject."
    - "Links from AuditLog resolve only to tombstones/redacted stubs (commitment preserved, payload gone)."
    - "Any remaining aggregates meet documented group-size and/or ε-differential privacy constraints."
  outputs:
    tombstone:
      object_id: "memobj:..."
      erased_at: "..."
      reason: "subject_request | policy | legal_hold_release"
      proof_ref: "auditlog:hashchain:..."
  exceptions:
    - "Regulator-mandated safekeeping (legal hold), if documented in ETH/MEM policy and audited."

Erasure is where GDPR right-to-be-forgotten semantics live, but the system must be explicit about what stays as an immutable commitment vs what becomes unrecoverable.

4.2 Redaction (masking within a larger object)

Redaction:

Remove or obfuscate parts of an object while leaving the surrounding structure intact.

Example:

  • keep a Jump trace’s decision structure,

  • but redact:

    • exact timestamps,
    • fine-grained location,
    • direct identifiers.

In SI terms:

operation: redaction
semantics:
  input: "memory_object_id"
  output: "memory_object_id (same identity, reduced payload)"
  invariants:
    - "Structural fields remain present (schema preserved)."
    - "ETH-annotated 'must_remove_on_erasure' fields are removed."
    - "GCS-relevant fields are retained if allowed by ETH/MEM."

Redaction is the typical pathway from semantic_reconstructable → structural_only.

4.3 Aggregation (population-level summarization)

Aggregation:

Combine many individuals’ data into population-level statistics, such that only group properties remain.

SI-Core cares about:

  • minimum group size,
  • privacy budget (if using differential privacy),
  • fairness coverage (don’t hide harms to small groups).
operation: aggregation
semantics:
  input: "set of memory objects"
  output: "aggregate object"
  constraints:
    k_anonymity_min: 50

    # Avoid floats in exported policy artifacts: epsilon = scaled_int / scale.
    dp_epsilon_max_scaled_int: 1000
    dp_epsilon_scale: 1000   # 1000/1000 == 1.0

    fairness_checks:
      must_cover_dimensions:
        - "age_band"
        - "region"
        - "accommodation_status"

Aggregation is often the final step before true forgetting.

4.4 Pseudonymization (reversible identity transform)

Pseudonymization:

Replace direct identifiers with stable, reversible tokens, controlled by a key under strict governance.

This is not erasure. It is indirection:

operation: pseudonymization
semantics:
  direct_identifier: "user@example.com"
  pseudonym: "uid:7f3a19..."
  key_holder: "privacy_officer"
  reidentification_requires:
    - "legal_basis"
    - "ETH board approval"

Within SI-Core:

  • SIM may use pseudonyms extensively,
  • SIS may keep only pseudonymized IDs, with a separate, tightly-controlled mapping service,
  • Genius Library usually drops even pseudonyms, keeping only structural types (“learner A”, “district X”).

5. Genius Traces without personal data

From the Genius Replay article: Genius Traces are exactly where we want to keep the “shape” of genius, but not the personal substrate it came from.

5.1 Decomposing a Genius Trace

A simplified GeniusTrace:

genius_trace:
  id: "GRP-2028-0421-city-flood"
  domain: "city_flood_control"
  context_signature:
    raw:
      sector_ids: [12, 13]
      hospitals: ["HOSP-42"]
      timestamp: "2028-04-21T03:12:00Z"
    abstract:
      topology_pattern: "two_critical_nodes"
      population_band: "100k-200k"
      rainfall_regime: "flash_flood"
  goal_surface_snapshot:
    objectives: ["safety", "hospital_access", "cost"]
  jump_sequence:
    - jump: "assess_flood_risk"
    - jump: "prioritize_hospitals"
    - jump: "reconfigure_gates"
  eval_summary:
    # Exported form avoids floats: values in [0,1] use basis points (bp).
    gcs_improvement_bp: 3200          # 0.32
    robustness_score_bp: 9100         # 0.91
    generalizability_score_bp: 8700   # 0.87
  ethics_trace:
    violations: []

For Genius Library, we want:

  • jump_sequence, goal_surface_snapshot, and abstract context,

  • but we do not want:

    • direct district IDs,
    • real hospital IDs,
    • precise timestamps tied to real events (in many jurisdictions).

5.2 Structuralization pipeline

Non-normative pipeline:

class GeniusTraceStructuralizer:
    def structuralize(self, trace: GeniusTrace) -> GeniusTrace:
        """
        Convert a full GeniusTrace into a structural-only variant
        suitable for long-term retention in the Genius Library.
        """

        # 1. Drop direct identifiers
        trace.context_signature.raw = None

        # 2. Keep only abstract features (clusters, pattern ids, bands)
        trace.context_signature = self._abstract_context(trace.context_signature)

        # 3. Ensure ethics_trace has no personal identifiers
        trace.ethics_trace = self._scrub_ethics_trace(trace.ethics_trace)

        # 4. Attach a 'structural_only' flag for MEM/ETH enforcement
        trace.metadata["originalness_level"] = "structural_only"

        return trace

The Genius Library retention policy can then say:

  • allowed: structural GeniusTrace variants,
  • forbidden: any trace whose originalness_level != structural_only.

5.3 “Re-use without re-identification”

When a new context arrives, Genius Replay:

  • matches abstract context_signature,
  • proposes candidate Genius Traces,
  • but never tries to recreate the original individuals.

The SI-Core invariant:

Replaying a Genius Trace may reuse structure (Jump sequence, goal surface pattern) but must never attempt to reconstruct specific past persons or sensitive identities unless there is explicit legal and ETH authorization.


6. Reconstruction vs privacy: GCS / ETH contracts

We now have all the pieces to express the core trade-off:

How much reconstructability do we allow, and how do we constrain it via GCS and ETH?

6.1 Reconstruction risk as a first-class metric

Define a rough Reconstruction Risk Score (RRS):

reconstruction_risk_score:
  domain: "education"
  subject_id: "learner:1234"
  inputs:
    - "semantic features retained"
    - "aggregates that include this subject"
    - "external knowledge assumptions"
  output:
    # Exported form avoids floats: probabilities use basis points (bp).
    score_bp: 1800   # 0.18
    band: "low"      # low/medium/high

At MEM/ETH level:

  • we can set per-domain thresholds:
rrs_thresholds:
  # Exported form avoids floats: thresholds use basis points (bp).
  low_risk_ux_bp: 3000     # 0.30
  education_bp: 2000       # 0.20
  health_bp: 500           # 0.05
  city_critical_bp: 1000   # 0.10

If RRS for a subject crosses a threshold, we can:

  • trigger further aggregation or redaction,
  • deny certain GeniusTrace promotions,
  • or refuse certain queries against SIS.

6.2 GCS-aware forgetting

For each goal ( g ), we can reason about how forgetting impacts GCS:

  • ( GCS^{with_mem}_g ) — expected goal contribution with current memory.
  • ( GCS^{after_forget}_g ) — expected contribution after erasure/aggregation.

We can impose:

GCS_after_forget_g ≥ GCS_min_g

for safety-critical goals, where ( GCS_min_g ) is a minimum acceptable performance.

Non-normative API sketch:

class MemoryChangeEvaluator:
    def assess_forgetting(self, change_plan, goals) -> dict:
        """
        Estimate impact of a memory change (erasure, aggregation, redaction)
        on each goal's GCS, using sandbox replay / models.
        """
        deltas = {}
        for g in goals:
            gcs_before = self._estimate_gcs_with_current_mem(g)
            gcs_after = self._estimate_gcs_after_change(g, change_plan)
            deltas[g.id] = gcs_after - gcs_before

        return deltas

Then ETH/MEM can insist on a contract:

  • the forgetting action is allowed only if:
RRS ≤ domain_threshold
AND GCS_after_forget_g ≥ GCS_min_g  for all safety-critical goals g

The civic-infrastructure intuition:

We want to forget enough to protect people, without forgetting so much that we degrade safety, fairness, or accountability.


7. Lifecycle policy: retention, summarization, forgetting, reconstruction

Putting it together as a lifecycle:

Raw → Semantic → Summarized → Aggregate-only → Gone
                     ↑
                  (optional
                 reconstruction
                  budget)

7.1 Lifecycle stages

Non-normative lifecycle spec (originalness-consistent):

originalness-consistent means: every downstream memory form preserves a verifiable derivation link (hash/proof) to its upstream form, even if the payload is redacted or summarized.

memory_lifecycle:
  stages:
    - name: "raw"
      originalness: "raw"
      max_ttl:
        default: "7d"
        city_critical: "365d"
      transition:
        - to: "semantic"
          trigger: "SCE pipeline"

    - name: "semantic"
      originalness: "semantic_reconstructable"
      max_ttl: "90d"
      transition:
        - to: "summarized"
          trigger: "scheduled summarization job"

    - name: "summarized"
      # Still linkable (pseudonymized), so it is NOT structural_only.
      originalness: "semantic_reconstructable"
      description: "summarized semantics; pseudonymized subject linkage permitted under ID/ETH governance"
      max_ttl: "5y"
      transition:
        - to: "structural_only"
          trigger: "policy boundary (e.g., long-term retention) OR RRS > threshold"

    - name: "structural_only"
      # No stable subject linkage.
      originalness: "structural_only"
      description: "decision/flow structure only; no stable subject identifiers; context generalized to bands/clusters"
      max_ttl: "indefinite"
      # legal constraints live in policy profiles, not free-form TTL strings
      legal_profile: "retention_policy_v1"
      transition:
        - to: "aggregate_only"
          trigger: "privacy hardening / federation requirements"

    - name: "aggregate_only"
      originalness: "aggregate"
      description: "population stats only; no individual-level reconstruction"
      max_ttl: "indefinite"
      legal_profile: "retention_policy_v1"
      transition:
        - to: "gone"
          trigger: "sunset_policy"

Notes:

  • The optional “reconstruction budget” applies only to transitions and views that remain semantic_reconstructable, and must be explicitly authorized by ID/ETH + legal basis.

7.2 Governance hooks

Each transition is a first-class action:

  • has an ID (who proposed / approved),
  • is subject to ETH policies (e.g., require extra review for health data),
  • is recorded in AuditLog with a link to the MemoryChangeEvaluator results.

Example audit record snippet:

audit_entry:
  id: "AUD-2028-05-01-12345"
  created_at: "2028-05-01T00:00:00Z"

  as_of:
    time: "2028-05-01T00:00:00Z"
    clock_profile: "si/clock-profile/utc/v1"

  trust:
    trust_anchor_set_digest: "sha256:..."

  canonicalization: "si/jcs-strict/v1"
  canonicalization_profile_digest: "sha256:..."

  actor: "si:memory_governor:v1"
  action: "transition_stage"
  from_stage: "semantic"
  to_stage: "summarized"
  population_scope: "learners_enrolled_2027"

  # Exported form avoids floats: impacts in [-1,1] use basis points (bp).
  gcs_impact_estimate_bp:
    mastery_bp: -100   # -0.01
    safety_bp: 0       # 0.00

  # Exported form avoids floats: RRS uses basis points (bp).
  rrs_before_bp: 2700  # 0.27
  rrs_after_bp: 1200   # 0.12

  approved_by: ["eth_board", "data_protection_officer"]

8. Implementation patterns

8.1 Memory governance engine

Non-normative API:

class MemoryGovernanceEngine:
    """
    Central coordinator for retention, forgetting, and reconstruction
    across SIM, SIS, Genius Library, EvalTrace, and AuditLog.
    """

    def propose_change(self, request) -> "MemoryChangeProposal":
        """
        Given a high-level request (e.g., 'reduce reconstruction risk in domain X'),
        propose a concrete plan: which tables to aggregate, which fields to redact, etc.
        """
        # 1. Analyze current RRS and GCS impact
        rrs = self._estimate_rrs(request.scope)
        gcs_deltas = self.mem_change_eval.assess_forgetting(request.plan, request.goals)

        # 2. Construct plan respecting thresholds
        plan = self._construct_plan(request, rrs, gcs_deltas)

        return MemoryChangeProposal(
            plan=plan,
            rrs_before=rrs,
            gcs_deltas=gcs_deltas,
        )

    def execute_change(self, proposal: "MemoryChangeProposal", approvers) -> "MemoryChangeResult":
        """
        Execute a previously proposed plan, after ETH/ID approvals.
        """
        self._check_approvals(proposal, approvers)
        applied = self._apply_plan(proposal.plan)
        audit_ref = self._log_to_audit(proposal, approvers)

        return MemoryChangeResult(
            applied=applied,
            audit_ref=audit_ref,
        )

8.2 Domain examples

Education:

  • SIM retains per-learner state for 90 days.

  • After 90 days:

    • per-learner history is summarized into a few structural archetypes (“late bloomer”, “high variance”, etc.),
    • Genius Traces may keep structural Jump sequences that worked well for certain archetypes,
    • EvalTrace retains pseudonymized IDs with clear re-identification governance.

CityOS:

  • SIS keeps raw sensor history for safety-critical infrastructure for up to 1 year.

  • After 1 year:

    • raw is dropped,
    • only semantic flood risk histories + aggregated outcomes remain.
  • Genius Traces describe flood-response strategies, but no longer know which block belonged to which specific person or household.


9. Summary

Memory in Structured Intelligence is:

  • SIM / SIS / Genius Library / EvalTrace / AuditLog, not just “logs”.
  • A civic infrastructure: it shapes what the system can do, who can be harmed or protected, and how accountable we are.

This article sketched:

  • a map of memory types (operational, archival, structural, evaluative, audit),
  • a vocabulary for originalness (raw, semantic reconstructable, structural only, aggregates),
  • clear operations (erasure, redaction, aggregation, pseudonymization) in SI terms,
  • how to keep Genius structures while dropping personal substrate,
  • GCS/ETH contracts for reconstruction vs privacy,
  • and a lifecycle where retention, summarization, forgetting, and reconstruction are governed, observable actions—not accidents of implementation.

Combined with:

  • Ethical Redaction / GDPR guidance (what must be forgettable),
  • Semantic Compression (how to produce meaningful semantic views),
  • Genius Replay (how to reuse high-value decision structure),

…this gives you a coherent view of MEM as governed infrastructure rather than a pile of logs and backups.

10. Reconstruction Risk Score (RRS) calculation

Challenge: Reconstruction Risk Score must be computable and auditable, not just a metaphor.

In §6 we treated RRS conceptually. Here is a non-normative sketch of how to calculate it from observable components.

10.1 Components of RRS

RRS is a composite score over four main factors:

  1. Direct identifier exposure
  2. Quasi-identifier exposure (k-anonymity style)
  3. Linkage risk via plausible external datasets
  4. Aggregate participation (how often this subject’s data enters aggregates / DP queries)

Non-normative implementation sketch:

class ReconstructionRiskCalculator:
    def calculate_rrs(self, subject_id, memory_scope) -> "ReconstructionRiskScore":
        """
        Calculate reconstruction risk for a subject within a given memory scope.
        Non-normative: exact scales and weights are policy decisions.
        """

        # 1. Direct identifier exposure (e.g., emails, national IDs, exact names)
        direct_score = self._count_direct_identifiers(subject_id, memory_scope)

        # 2. Quasi-identifier exposure (age, region, rare attributes...)
        quasi_score = self._assess_quasi_identifiers(subject_id, memory_scope)

        # 3. Linkage risk via external data sources
        linkage_score = self._estimate_linkage_risk(subject_id, quasi_score)

        # 4. Aggregate participation and DP exposure
        aggregate_score = self._assess_aggregate_participation(subject_id, memory_scope)

        # 5. Composite score (non-normative weighting)
        rrs_value = self._compute_composite_rrs(
            direct_score,
            quasi_score,
            linkage_score,
            aggregate_score,
        )

        return ReconstructionRiskScore(
            score=rrs_value,
            band=self._classify_band(rrs_value),  # e.g. low / medium / high
            components={
                "direct": direct_score,
                "quasi": quasi_score,
                "linkage": linkage_score,
                "aggregate": aggregate_score,
            },
        )

10.2 Quasi-identifier assessment

Quasi-identifiers are attributes that are not unique alone, but become identifying when combined.

def _assess_quasi_identifiers(self, subject_id, memory_scope) -> float:
    """
    Assess risk from quasi-identifiers (k-anonymity-style).
    Return a risk score in [0,1] where 1 is highest risk.
    """

    quasi_ids = self._extract_quasi_identifiers(subject_id, memory_scope)
    # Example: {"age": 42, "zip": "94110", "gender": "F", "diagnosis": "X123"}

    # Estimate k-anonymity for this quasi-ID pattern
    k = self._estimate_k_anonymity(quasi_ids, memory_scope.population)

    # Lower k → higher risk (non-normative thresholds)
    if k < 5:
        return 0.9  # High risk
    elif k < 50:
        return 0.5  # Medium risk
    else:
        return 0.1  # Low risk

This does not require exact k-anonymity; approximate counts or sketches are fine, as long as the methodology is documented.

10.3 Linkage risk estimation

Linkage risk is about what a reasonably capable adversary could do with external data.

def _estimate_linkage_risk(self, subject_id, quasi_score) -> float:
    """
    Estimate linkage risk with plausible external datasets.
    Input may include the quasi-score as a prior.
    """

    external_datasets = self._model_external_knowledge()

    max_linkage_prob = 0.0
    for ext in external_datasets:
        linkage_prob = self._compute_linkage_probability(
            subject_id=subject_id,
            ext_dataset=ext,
            quasi_score=quasi_score,
        )
        max_linkage_prob = max(max_linkage_prob, linkage_prob)

    return max_linkage_prob

The details of _model_external_knowledge and _compute_linkage_probability are domain-specific, but the structure makes clear that:

  • assumptions about external data are explicit and auditable,
  • the “worst plausible linkage” drives the component score.

10.4 Differential privacy contribution to RRS

If parts of the system use differential privacy, the subject...governed by the cumulative ε spent on queries that include them.

Terminology note: ε here is the differential-privacy parameter. When discussing semantic compression fidelity elsewhere, use a distinct name (e.g., ε_loss / loss_budget) to avoid confusion.

def calculate_rrs_with_dp(self, subject_id, memory_scope, epsilon_total: float) -> float:
    """
    RRS component derived from DP budget consumption.
    Non-normative mapping from epsilon to risk.
    """

    # Queries whose DP mechanisms included this subject
    queries = self._find_queries_including(subject_id, memory_scope)

    epsilon_spent = sum(q.epsilon for q in queries)

    # Simple monotone mapping: higher epsilon → higher risk
    # e.g., 0 risk when epsilon_spent=0, saturating below 1
    rrs_dp = 1.0 - np.exp(-epsilon_spent / max(epsilon_total, 1e-6))

    return rrs_dp

DP here is not a magic shield; it’s another measurable term in the RRS composite.

10.5 RRS monitoring and auditability

RRS is only useful if it is tracked and explainable.

rrs_monitoring:
  continuous_tracking:
    - "RRS per subject updated on memory writes and lifecycle transitions"
    - "Alerts when RRS exceeds domain-specific thresholds"
    - "Automatic remediation actions (aggregation, redaction, DP) when thresholds breached"

  audit_requirements:
    - "All RRS calculations + parameters logged to AuditLog"
    - "Methodology and weights documented for DPO / regulators"
    - "Regular validation using simulated privacy attacks"

11. GCS-aware forgetting: from concept to implementation

Challenge: Before we forget or aggregate, we want a quantitative estimate of how much this will degrade goal performance.

Section 6 defined the conceptual contract; here is how it can be implemented in practice.

11.1 GCS impact estimator

class GCSImpactEstimator:
    """
    Non-normative: estimate GCS impact of a proposed memory change
    using replay / modeling.
    """

    def estimate_gcs_impact(self, memory_change, goals) -> dict:
        """
        Returns a map goal_id -> impact summary.
        """
        impacts = {}

        for goal in goals:
            gcs_current = self._estimate_gcs_current(goal)
            gcs_after = self._simulate_gcs_after_change(goal, memory_change)

            impacts[goal.id] = {
                "current": gcs_current,
                "after": gcs_after,
                "delta": gcs_after - gcs_current,
                "delta_pct": (gcs_after - gcs_current) / max(gcs_current, 1e-9) * 100.0,
            }

        return impacts

11.2 Method 1: Historical replay

Replay historical decision contexts with a “modified memory” view.

def _simulate_gcs_after_change(self, goal, memory_change) -> float:
    """
    Historical replay under modified memory.
    """

    # Load a representative sample of historical decision contexts
    contexts = self._load_historical_contexts(goal.domain)

    # Apply memory change to obtain a "post-change" view
    modified_mem = self._apply_change(self.current_memory, memory_change)

    gcs_values = []
    for ctx in contexts:
        decision = self.decision_engine.decide(ctx, modified_mem)
        gcs = goal.evaluate(decision, ctx.outcome)
        gcs_values.append(gcs)

    return float(np.mean(gcs_values))

This is more expensive but conceptually straightforward.

11.3 Method 2: Counterfactual modeling

When full replay is too costly, approximate with models.

def _counterfactual_gcs_estimate(self, goal, memory_change) -> float:
    """
    Model-based counterfactual estimate of GCS after a memory change.
    """

    model = self._train_gcs_model(goal)  # learned mapping: memory_features → GCS

    current_features = self._extract_memory_features(self.current_memory)
    modified_features = self._extract_memory_features_after_change(memory_change)

    gcs_after = model.predict(modified_features)
    return float(gcs_after)

The key is to treat GCS as a function of memory features, and be explicit about the model’s limitations.

11.4 Forgetting constraints as a checker

Putting it together:

class ForgetConstraintChecker:
    """
    Enforce GCS-aware constraints before executing memory changes.
    """

    def __init__(self, gcs_estimator: GCSImpactEstimator):
        self.gcs_estimator = gcs_estimator

    def check_forgetting_constraints(self, memory_change, goals):
        """
        Returns a CheckResult with ok/violations/impacts.
        """

        impacts = self.gcs_estimator.estimate_gcs_impact(memory_change, goals)
        violations = []

        for goal in goals:
            impact = impacts[goal.id]

            if goal.is_safety_critical:
                # Hard minimum for safety-critical goals
                if impact["after"] < goal.gcs_min:
                    violations.append(f"Goal {goal.id} below minimum GCS")
            else:
                # Non-critical: allow bounded degradation
                if impact["delta_pct"] < -10.0:  # > 10% degradation
                    violations.append(f"Goal {goal.id} degrades by >10%")

        return CheckResult(
            ok=(len(violations) == 0),
            violations=violations,
            impacts=impacts,
        )

Example in the learning domain:

  • mastery can degrade slightly,
  • wellbeing and fairness have stricter minima.

12. Differential privacy as a first-class mechanism

Challenge: DP is often bolted on to analytics; here we treat it as a core part of memory governance.

12.1 DP mechanisms

Non-normative DP “engine” abstraction:

class DifferentialPrivacyEngine:
    def __init__(self, epsilon_total: float, delta: float = 1e-6):
        self.epsilon_total = epsilon_total
        self.delta = delta
        self.epsilon_spent = 0.0

    def add_laplace_noise(self, true_value, sensitivity, epsilon):
        """
        Laplace mechanism with budget accounting.
        """
        self._check_budget(epsilon)
        scale = sensitivity / max(epsilon, 1e-9)
        noise = np.random.laplace(0.0, scale)
        self.epsilon_spent += epsilon
        return true_value + noise

    def add_gaussian_noise(self, true_value, sensitivity, epsilon, delta=None):
        """
        Gaussian mechanism for (ε,δ)-DP.
        """
        self._check_budget(epsilon)
        delta = delta or self.delta
        sigma = sensitivity * np.sqrt(2 * np.log(1.25 / delta)) / max(epsilon, 1e-9)
        noise = np.random.normal(0.0, sigma)
        self.epsilon_spent += epsilon
        return true_value + noise

    def _check_budget(self, epsilon):
        if self.epsilon_spent + epsilon > self.epsilon_total:
            raise PrivacyBudgetExceeded(
                f"Budget exceeded: requested {epsilon}, remaining "
                f"{self.epsilon_total - self.epsilon_spent}"
            )

12.2 Domain-level privacy budget tracking

Budgets are typically per domain or per cohort.

class PrivacyBudgetTracker:
    def __init__(self, domains, total_per_domain=1.0, audit_log=None):
        self.budgets = {
            d: {"total": float(total_per_domain), "spent": 0.0}
            for d in domains
        }
        self.audit_log = audit_log or []

    def allocate(self, domain, operation, epsilon):
        budget = self.budgets[domain]
        if budget["spent"] + epsilon > budget["total"]:
            raise PrivacyBudgetExceeded(
                f"Domain {domain}: budget exceeded; requested {epsilon}, "
                f"remaining {budget['total'] - budget['spent']}"
            )

        budget["spent"] += epsilon

        # Log to AuditLog for governance
        self.audit_log.append(
            {
                "operation": operation,
                "domain": domain,
                "epsilon": epsilon,
                "epsilon_remaining": budget["total"] - budget["spent"],
            }
        )

12.3 DP aggregation in the lifecycle

DP fits naturally into the semantic → summarized → aggregate transitions (§7):

dp_memory_lifecycle:
  raw_to_semantic:
    mechanism: "None (exact semantics, local scope)"
    epsilon_scaled_int: 0
    epsilon_scale: 1000

  semantic_to_summarized:
    mechanism: "Laplace noise on per-group aggregates"
    epsilon_per_query_scaled_int: 100
    epsilon_scale: 1000   # 100/1000 == 0.1

  summarized_to_aggregate_only:
    mechanism: "Gaussian noise for public reporting"
    epsilon_scaled_int: 500
    epsilon_scale: 1000   # 500/1000 == 0.5

    # Avoid scientific-notation floats in exported policy artifacts.
    delta_ppm: 1          # 1e-6 == 1 per million

The exact numbers are non-normative; the point is that DP mechanisms and budgets are explicit, not sprinkled ad-hoc.


13. Testing memory governance

Challenge: Privacy and GCS constraints must be tested, not just believed.

13.1 Testing layers

A non-normative testing pyramid:

  • Unit tests

    • RRS calculation logic
    • GCS impact estimation routines
    • DP noise functions and budget accounting
    • Pseudonymization reversibility (and correct separation of keys)
  • Integration tests

    • Full memory lifecycle (raw → semantic → summarized → aggregate → erasure)
    • ETH policy enforcement on memory changes
    • AuditLog integrity and coverage
  • Adversarial tests

    • Re-identification attacks
    • Membership inference
    • Reconstruction attacks against aggregates / DP outputs

13.2 Privacy attack simulation

A rough adversarial test harness:

class PrivacyAttackSimulator:
    """
    Simulate privacy attacks and compare empirical re-ID to predicted RRS.
    """

    def simulate_reidentification_attack(self, memory_scope):
        external_data = self._model_external_knowledge()

        successful = 0
        subjects = memory_scope.subjects

        for subject in subjects:
            quasi_ids = self._extract_quasi_ids(subject, memory_scope)
            if self._attempt_linkage(quasi_ids, external_data):
                successful += 1

        reid_rate = successful / max(len(subjects), 1)
        avg_rrs = np.mean(
            [self.rrs_calc.calculate_rrs(s, memory_scope).score for s in subjects]
        )

        return AttackResult(
            reid_rate=reid_rate,
            predicted_rrs=avg_rrs,
            discrepancy=abs(reid_rate - avg_rrs),
        )

This lets you check whether your RRS model is pessimistic enough.

13.3 Property-based and DP tests

Property-based checks for GCS constraints:

from hypothesis import given, strategies as st

@given(memory_change=st.sampled_from(valid_memory_changes))
def test_gcs_constraints_enforced(memory_change):
    result = forget_constraint_checker.check_forgetting_constraints(
        memory_change,
        safety_critical_goals,
    )

    if not result.ok:
        # Executing this change must be rejected by governance
        with pytest.raises(GCSConstraintViolation):
            mem_governor.execute_change(memory_change)

And empirical DP sanity checks (sketch):

def test_dp_mechanism_behaves_reasonably():
    dp_engine = DifferentialPrivacyEngine(epsilon_total=1.0, delta=1e-6)

    D1 = [1, 2, 3, 4, 5]
    D2 = [1, 2, 3, 4, 6]  # neighboring dataset

    outputs_D1 = [
        dp_engine.add_laplace_noise(sum(D1), sensitivity=1, epsilon=0.1)
        for _ in range(5000)
    ]
    outputs_D2 = [
        dp_engine.add_laplace_noise(sum(D2), sensitivity=1, epsilon=0.1)
        for _ in range(5000)
    ]

    # Check that the two empirical distributions are close enough
    # given the theoretical DP guarantees (details omitted).
    ...

The goal is not formal proof, but continuous pressure-testing of privacy and GCS guarantees.


14. Cross-domain memory governance

Challenge: A single subject can span multiple domains (education, health, social services, etc.), with different retention rules and regulators.

14.1 Cross-domain scenario

Non-normative example:

cross_domain_scenario:
  subject: "learner:1234"
  domains:
    education:
      memory: "learning history, mastery, curriculum coverage"
      retention: "5y, structural-only"
    health:
      memory: "accommodations, clinical stress indicators"
      retention: "10y (regulatory)"
    social_services:
      memory: "family context, support episodes"
      retention: "until majority + 3y"

14.2 Cross-domain access checks

Access mediation between domains:

class CrossDomainMemoryGovernor:
    """
    Enforce cross-domain sharing policies and consent.
    """

    def check_cross_domain_access(
        self,
        requesting_domain: str,
        subject_id: str,
        target_domain: str,
    ):
        policy = self.cross_domain_policies.get(
            (requesting_domain, target_domain)
        )
        if not policy:
            return AccessDenied("No cross-domain sharing policy")

        if policy.requires_consent:
            if not self._has_consent(subject_id, requesting_domain, target_domain):
                return AccessDenied("Consent not present")

        view = self._apply_view_restrictions(policy, subject_id, target_domain)
        return AccessGranted(view=view)

The returned view will typically be aggregated or structural, not raw personal records.

14.3 Federated memory: “no central panopticon”

A federated pattern allows queries across domains without centralizing raw data:

class FederatedMemoryStore:
    """
    Federated queries across domains, with local execution and sanitized results.
    """

    def query_federated(self, subject_id, domains):
        results = {}
        for domain in domains:
            local_result = self._query_at_domain(domain, subject_id)
            results[domain] = self._sanitize_for_federation(local_result)
        return results

This is especially important for cross-jurisdictional scenarios (e.g. city + school system + health provider).

14.4 Consent as a first-class object

Consent must be granular and lifecycle-managed:

consent_framework:
  granular_consent:
    - from_domain: "education"
      to_domain: "health"
      purpose: "accommodation_support"
      granted: true
    - from_domain: "education"
      to_domain: "social_services"
      purpose: "family_support"
      granted: false

  consent_lifecycle:
    - "All consents logged to AuditLog (with purpose and scope)"
    - "Consents revocable by subject/guardian"
    - "Consent expirations reviewed regularly"

This keeps MEM as civic infrastructure even when data crosses institutional boundaries.

Community

Sign up or log in to comment