Files
inter-hub/contracts/core/append-only-events-v1.md
Bernd Worsch 0f505feb2d feat(WP-0013): IHF Phase 12 — Platform Memory and Continuous Learning
Closes the long-range feedback loop: outcome signals now enrich the full
traceability chain and feed back into routing, triage, and AI proposals.

Schema (T01):
- outcome_correlations (CHECK correlation_type)
- pattern_performance_records
- adaptive_threshold_configs
- institutional_knowledge_entries (GIN tsvector FTS)
- learning_insights (CHECK insight_type)
- ALTER TABLE decision_records + requirement_candidates: outcome_summary JSONB
- AFTER INSERT trigger trg_enrich_lineage on outcome_signals
- contracts/core/ updated (outcome-summary-columns-v1, append-only addendum)

Correlation engine (T02):
- Application/Helper/CorrelationEngine.hs: pure annotation→outcome SQL
- Web/Controller/OutcomeCorrelations.hs: ComputeCorrelationsAction + index

Pattern performance (T03):
- Web/Controller/PatternPerformance.hs: ComputePatternPerformanceAction

Adaptive thresholds (T04):
- Web/Controller/AdaptiveThresholds.hs: CalibrateThresholdsAction
- Application/Helper/FrictionScore.hs: applyAdaptiveWeights

Institutional knowledge (T05):
- DistilDecisionAction in DecisionRecords controller
- Web/Controller/InstitutionalKnowledge.hs: QueryKnowledgeBaseAction

Lineage enrichment (T06):
- Web/Controller/LineageEnrichment.hs: EnrichLineageAction (batch backfill)
- enrich_lineage_on_outcome_batch() PL/pgSQL helper in migration

Learning dashboard (T07):
- Web/Controller/LearningDashboard.hs: 5-panel autoRefresh view
- "Learning" nav link in FrontController

API v2 learning endpoints (T08):
- GET /api/v2/outcome-correlations, /pattern-performance, /knowledge-base/{id}
- OpenAPI schemas: OutcomeCorrelation, PatternPerformanceRecord, InstitutionalKnowledgeEntry

GAAF scorecard + docs (T09):
- Core 3.8→3.9, Functional 3.6→3.8, overall 3.61→3.68
- CLAUDE.md: IHF v0.2 complete, no active workplan

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 12:34:07 +00:00

3.6 KiB

Append-Only Events Contract

Name: append-only-events Version: 1.0 Date: 2026-03-31 Status: Active Layer: Core Immutable: Yes — this invariant is permanent and cannot be relaxed


Purpose

Interaction events and outcome signals are the primary observational record of the IHF. Their integrity as an append-only log is a foundational invariant: governance, traceability, and antifragility all depend on the fact that the historical record cannot be silently rewritten.


Invariant

The following tables are append-only:

Table Trigger (no update) Trigger (no delete)
interaction_events interaction_events_no_update interaction_events_no_delete
outcome_signals outcome_signals_no_update outcome_signals_no_delete

No row in either table may be modified or deleted after insertion. This invariant is enforced at the PostgreSQL level by BEFORE UPDATE and BEFORE DELETE triggers that raise exceptions. It cannot be bypassed by application code.


Enforcement

-- Implemented in Application/Schema.sql
CREATE TRIGGER interaction_events_no_update
    BEFORE UPDATE ON interaction_events
    FOR EACH ROW EXECUTE FUNCTION prevent_interaction_event_mutation();

CREATE TRIGGER interaction_events_no_delete
    BEFORE DELETE ON interaction_events
    FOR EACH ROW EXECUTE FUNCTION prevent_interaction_event_mutation();

-- Same pattern for outcome_signals

The trigger function raises:

"interaction_events is append-only: UPDATE and DELETE are not permitted"

Correction Policy

Erroneous events must not be corrected by modifying the original row. The correct approach is to insert a new event:

{
  "event_type": "retracted",
  "metadata": {
    "retracted_event_id": "<uuid of original event>",
    "reason": "incorrect actor attribution"
  }
}

The original event remains in the table. Downstream analysis should treat retracted events as markers that exclude the referenced event from calculations.


Failure Mode

Any attempt to UPDATE or DELETE a row in interaction_events or outcome_signals raises a PostgreSQL exception with SQLSTATE P0001. The calling transaction is aborted. No partial mutation is possible.


Scope

This contract applies only to interaction_events and outcome_signals.

Other append-oriented tables (triage_states, widget_ownerships, stewardship_roles) use the same conceptual pattern (soft expiry instead of update, no hard delete) but are not covered by DB-trigger enforcement. Their append semantics are enforced by application-layer controller conventions.


Phase 12 Addendum — Lineage Enrichment Trigger

Added in Phase 12 (IHUB-WP-0013 T06): outcome_signals now has an AFTER INSERT trigger trg_enrich_lineage that calls enrich_lineage_on_outcome().

This trigger:

  • Is AFTER INSERT only — it never fires on UPDATE or DELETE.
  • Does not modify outcome_signals — it only enriches upstream records (decision_records.outcome_summary, requirement_candidates.outcome_summary).
  • Is consistent with the append-only invariant: the outcome_signals row itself remains immutable after insertion.

Implementation Reference

  • Functions: prevent_interaction_event_mutation(), prevent_outcome_signal_mutation() in Application/Schema.sql
  • Phase 12: enrich_lineage_on_outcome() — AFTER INSERT trigger on outcome_signals; enriches non-append-only columns on upstream tables only
  • The architectural fitness function Test/Architecture/LayerBoundarySpec.hs (Test 1) verifies these trigger names are present in the schema