Skip to main content
Version: v0.9.0a2
Spec
Experimental feature
This feature is Experimental. Breaking changes may occur before it reaches Stable. See feature status definitions →

§15. Decay Semantics

5 min readSpec contributor · Node operatorExperimental · future plugin line

What this section covers

Configurable TTL and confidence-decay policies; POST /v1/decay/sweep. The decay operation is the remediation complement to lint: lint identifies stale/low-confidence facts; the decay sweeper acts on them.

Status: Experimental / dormant source package

Source material: Archived evolutionary spec snapshots. This page is the maintained Spec-X home for decay semantics.

Section body

Each subsection below shows the most recent normative text from the spec source. When earlier spec drafts also contained text for the same subsection, those revisions are collapsed under a Revisions accordion beneath it — open one to see what changed. Subsections that only appear in one draft render as plain text with no accordion.

Pre-reset status: Draft. The decay sweeper (POST /v1/decay/sweep) and decay_scope MCP tool are specified here. Implementation is the D4 deliverable. Wire format and DecayPolicy registry are draft; conformance test vectors (DECAY_VECTORS) will be finalized with D4 implementation.

§15.1 DecayPolicy

A DecayPolicy configures how facts of a given relation (or all relations) decay over time. Operators deploy one or more policies to ensure knowledge does not accumulate indefinitely — stale facts degrade confidence or are retracted without requiring agents to remember which facts they once asserted.

The struct exposes two mutually-exclusive decay modes.

Retraction (ttl_s) is an aggressive binary cut-off suited for ephemeral state. Confidence reduction (half_life_s) provides a smoother signal for beliefs that age gradually.

DecayPolicy {
id: string // unique identifier for this policy
relation: string | "*" // relation this policy applies to; "*" = all relations
scope: FactScope | "*" // scope this policy applies to; "*" = all scopes
mode: DecayMode
ttl_s: integer? // for mode="retract": retract facts older than ttl_s seconds
half_life_s: integer? // for mode="confidence": halve confidence every half_life_s seconds
min_confidence: float? // for mode="confidence": do not reduce below this floor; default 0.0
exempt_relations: string[] // relations that are never decayed by this policy (e.g. stigmem: namespace)
}

DecayMode =
| "retract" // assert confidence=0.0 for matching facts older than ttl_s
| "confidence" // reduce confidence by half every half_life_s seconds; floor at min_confidence
| "dry_run" // same logic as retract/confidence but no writes; returns what would be changed
Configuration
Source
Notes
Env var
STIGMEM_DECAY_POLICIES
JSON array of DecayPolicy objects.
Admin API
/v1/decay/policies
Management endpoint; not yet implemented.
Default policy
no-op
If no policies are configured, the decay sweeper is a no-op. Nodes do not apply any automatic decay by default.
Evaluation order
most-specific-first
Exact relation match before "*", exact scope match before "*". The first matching policy wins.

The stigmem: and rel: namespaces are always exempt from decay.

System-generated facts and reification primitives MUST NOT be retracted or confidence-reduced by the sweeper, regardless of policy configuration. The exempt_relations field may add further exemptions.

§15.2 Decay Sweep Wire Format

The sweep endpoint triggers evaluation of all matching decay policies against a single scope. The request/response split between "actual" and "dry-run" counters is intentional: facts_retracted and facts_reduced are always zero in dry_run mode, while dry_run_would_retract and dry_run_would_reduce are always zero outside it. This avoids ambiguity about whether writes occurred.

Request

POST /v1/decay/sweep
Authorization: Bearer <api-key>
Content-Type: application/json

{
"scope": FactScope, // required: which scope to sweep
"mode": DecayMode?, // optional: override all policies' mode for this run
"policy_id": string? // optional: run only this named policy
}

Response

200 OK
{
"swept_at": string,
"scope": FactScope,
"mode": DecayMode,
"facts_evaluated": integer,
"facts_retracted": integer,
"facts_reduced": integer,
"dry_run_would_retract": integer,
"dry_run_would_reduce": integer,
"policies_applied": string[]
}

Error responses

HTTP
Code
Condition
400
validation
scope missing or invalid; policy_id not found; invalid mode override.
403
authorization
Caller's key lacks write access to the requested scope.
202
async
Scope exceeds 100,000 facts (async path; same pattern as §14.5).

The caller's API key MUST have write access to the requested scope. The decay sweep writes retractions (new facts with confidence=0.0) or confidence-update facts; these are regular fact assertions subject to all normal write invariants.

§15.3 Decay and Immutability

The decay sweeper does not mutate existing facts.

All decay actions are expressed as new immutable fact assertions. The original fact is retained.

Action
Output fact
Source
Retraction
confidence=0.0
New fact (entity, relation, scope, confidence=0.0, source="system:stigmem:decay").
Confidence reduction
exponential
New fact with confidence = original_confidence * exp(-ln(2) / half_life_s * elapsed_s), floored at min_confidence.

Both produce entries in the normal facts table and are visible in GET /v1/facts responses. source="system:stigmem:decay" allows callers to distinguish sweep-induced retractions from agent-authored retractions.

§15.4 Performance Contract

Sync within 60 s

For scopes with fewer than 100,000 facts.

Async over 100k

HTTP 202 following the same async job pattern as §14.5.

Dry-run always sync

Performs no writes and MUST respond within 30 seconds regardless of scope size.

§15.5 MCP Tool: decay_scope

The decay_scope MCP tool exposes POST /v1/decay/sweep to any MCP-aware agent.

{
"name": "decay_scope",
"description": "Apply configured decay policies to a Stigmem scope. Retracts or reduces confidence of stale/aged facts per operator-defined DecayPolicy. Use mode='dry_run' to preview what would change without writing. Complement to lint_scope — lint identifies decay candidates; decay_scope acts on them.",
"inputSchema": {
"type": "object",
"properties": {
"scope": {
"type": "string",
"enum": ["local", "team", "company", "public"],
"description": "The fact scope to sweep."
},
"mode": {
"type": "string",
"enum": ["retract", "confidence", "dry_run"],
"description": "Optional. Override decay mode for this run. Omit to use configured policy modes."
},
"policy_id": {
"type": "string",
"description": "Optional. Run only the named decay policy."
}
},
"required": ["scope"]
}
}

§15.6 Cron-Friendly Operation

The decay sweeper is designed for scheduled operation:

# Run decay sweep on company scope daily at 02:00
0 2 * * * curl -X POST $STIGMEM_URL/v1/decay/sweep \
-H "Authorization: Bearer $STIGMEM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"scope": "company"}'

The sweep is idempotent.

Running it twice in a row on the same data produces the same result.

Operators define decay policies as a JSON array in the STIGMEM_DECAY_POLICIES environment variable. Each entry declares a relation glob, scope, mode, and the relevant timing parameter. Multiple policies can coexist so that different relation families decay at different rates.

STIGMEM_DECAY_POLICIES=[
{
"id": "memory-context-decay",
"relation": "memory:*",
"scope": "company",
"mode": "confidence",
"half_life_s": 604800,
"min_confidence": 0.1
},
{
"id": "stale-roadmap-retract",
"relation": "roadmap:status",
"scope": "company",
"mode": "retract",
"ttl_s": 2592000
}
]

§15.7 Conformance Test Vectors

DECAY_VECTORS are defined in sdks/stigmem-py/tests/conformance_vectors.py.

Vector ID
Mode
Expected outcome
decay-confidence-reduction
confidence
Fact with half_life_s=3600; asserted 7200 s ago → new fact with confidence ≈ 0.25.
decay-retraction
retract
Fact older than ttl_s → new fact with confidence=0.0, source="system:stigmem:decay".
decay-scope-filter
retract
Stale fact in public scope; sweep company → no facts retracted (scope isolation).
decay-dry-run
dry_run
Fact older than ttl_sdry_run_would_retract=1; no new facts in store.
decay-exempt
retract
Fact with relation="stigmem:received_from" → not retracted (exempt namespace).

All five vectors MUST pass for conformance.