{"slug":"strategy/product-strategy/EDGE_CONDITIONS_AND_GOVERNED_LEARNING_SPEC","title":"Edge Conditions and Governed Auto-Learning","section":"strategy","sectionLabel":"Strategy","status":"unclassified","type":"document","path":"strategy/product-strategy/EDGE_CONDITIONS_AND_GOVERNED_LEARNING_SPEC.mdx","bytes":31344,"description":null,"updated":null,"source":"integratewise/Integratewise-docs-wiki","content":"\n**Status:** Proposed\n**Document type:** Specification\n**Authority:** Subordinate to [`INTEGRATEWISE_ARCHITECTURE.md`](../../architecture/INTEGRATEWISE_ARCHITECTURE.md)\n**Canonical sections implemented:** §8 (Truth, memory scopes, promotion) · §9 (Four flows, Normalizer/Triage) · §10 (Capability effect classes, governance) · §11.4 (Response state) · §14 (Tenancy)\n**Owner:** Platform + Data\n**Version:** 1.0\n**Last verified against:** HEAD `d941a422`\n**Last verified date:** 2026-07-31\n**Resolves:** **S0b** (`org_memory` write ambiguity) · **M1** (store vs cache) · **M3** (agent memory citation)\n\n> Adds implementation precision. Does not redefine kernel vocabulary, ownership boundaries,\n> truth authority, runtime ordering or governance invariants.\n\n---\n\n## 1. Invariants\n\n1. **Spine is the system of record.** Brain, Twin, Knowledge, Workbench and analytical stores\n   are projections or reasoning layers. **None of them is a parallel canonical store.**\n2. **Uncertainty is represented explicitly.** `unknown` · `unresolved` · `insufficient_data`\n   · `conflicted` · `stale` · `failed` are valid persisted states. The platform never converts\n   uncertainty into a guessed canonical value.\n3. **Tenant boundaries fail closed.** Missing or invalid workspace context returns zero rows\n   and rejects writes.\n4. **Autonomy is granted by policy, never inferred by an agent.** Agents may discover patterns\n   and propose rules. Only Governance authorises autonomous execution.\n5. **All derived behaviour is versioned and reversible.** Rules, score formulas, mappings,\n   learned patterns and execution policies require version history, provenance, evaluation\n   evidence and rollback.\n\nInvariant 4 is the agent-scoped corollary of the **Authority Law** (_confidence increases\ntrust; confidence never grants authority_). It is not a ninth law — it is the same law applied\nwhere it is most often violated.\n\n---\n\n## 2. No dual-write: the transactional outbox\n\n**The system must not perform independent synchronous writes to Spine and to any projection.**\n\n```\nSource event\n    ↓\n┌─ Spine transaction ──────────────────────┐\n│  canonical entity mutation                │\n│  field lineage mutation                   │\n│  transactional outbox event               │   ← same transaction, always\n└──────────────────┬───────────────────────┘\n                   ↓  asynchronous consumers\n     Brain projection · search index · vector index\n     signal engine · Workbench projection · org-memory cache\n```\n\nThe canonical mutation and the outbox event **commit in the same database transaction**.\nEverything downstream is an asynchronous consumer of that event.\n\n### 2.1 Delivery semantics\n\nAt-least-once delivery · stable event identifiers · idempotent consumers · monotonic entity\nversions · retry with exponential backoff and jitter · dead-letter queues · reconciliation\njobs.\n\n**The architecture does not claim cross-store exactly-once delivery.** Any document that does\nis wrong.\n\n### 2.2 Partial failure\n\n| Failure                              | Resulting state                               |\n| ------------------------------------ | --------------------------------------------- |\n| Spine transaction fails              | No canonical change **and** no outbox event   |\n| Spine succeeds, projection fails     | Canonical state valid; outbox retries         |\n| Search/vector projection fails       | Canonical valid; that projection is `lagging` |\n| Retry limit exceeded                 | Event enters the projection repair queue      |\n| Projection receives an older version | Successful no-op                              |\n| Projection receives a duplicate      | Successful no-op                              |\n\n```ts\ninterface ProjectionStatus {\n  projectionName: string;\n  entityId: string;\n  canonicalVersion: number;\n  projectedVersion: number;\n  status: \"current\" | \"lagging\" | \"failed\" | \"rebuilding\";\n  lastSuccessfulAt?: string;\n  lastErrorCode?: string;\n}\n```\n\n### 2.3 This resolves S0b and M1\n\nArchitecture **S0b** records `org_memory` as a documented cache with six direct writers and no\ndefined reconciliation. The outbox settles it:\n\n> **Neon `memory.org_memory` is the store. The D1 `org_memory` table is a projection.**\n> Exactly one writer — the promotion gate, inside the Spine transaction. The other five\n> services become read-only consumers of the outbox.\n\n**M1 is closed** on that basis. The same reasoning retires the \"Brain as second write\ndestination\" framing entirely: **Brain is a reasoning and learning projection used by the\nTwin, not a truth store.** The durable control path is unchanged:\n\n```\nSpine → Twin proposal → Governance → Hermes execution → re-ingestion into Spine\n```\n\n---\n\n## 3. Edge Condition Registry\n\nEvery pipeline stage registers its known edge conditions:\n\n`edge_code` · `stage` · `detection_condition` · `severity` · `handling_strategy` ·\n`fallback_state` · `retry_policy` · `owner` · `user_visibility` · `audit_required`\n\n### 3.1 Canonical edge conditions\n\n| Code                              | Condition                                          | Required handling                                              | Fallback                       |\n| --------------------------------- | -------------------------------------------------- | -------------------------------------------------------------- | ------------------------------ |\n| `SOURCE_UNKNOWN`                  | Source system unidentifiable                       | Deterministic fingerprinting; classifier _recommendation_ only | Quarantine for triage          |\n| `SOURCE_CLASSIFICATION_AMBIGUOUS` | Multiple source candidates within margin           | **Do not assign automatically**                                | Quarantine with candidate list |\n| `SCHEMA_VERSION_UNKNOWN`          | Payload schema unsupported                         | Preserve raw; request mapping                                  | Dead-letter                    |\n| `FIELD_COERCION_FAILED`           | Value not convertible to canonical type            | Store raw; emit field-level error                              | Canonical field null           |\n| `REQUIRED_FIELD_MISSING`          | Required canonical field absent                    | Configured derivation only                                     | Incomplete provisional record  |\n| `IDENTITY_COLLISION`              | Multiple candidates exceed threshold               | **Require disambiguation; do not merge**                       | Unresolved identity queue      |\n| `IDENTITY_LOW_CONFIDENCE`         | No candidate meets threshold                       | Provisional identity only where policy permits                 | Quarantine                     |\n| `IDENTITY_SPLIT_SUSPECTED`        | Entity appears to represent multiple real entities | Suspend automatic updates to conflicting fields                | Identity-review case           |\n| `IDENTITY_MERGE_CONFLICT`         | Merge would overwrite incompatible values          | Field-level merge proposal                                     | Leave entities separate        |\n| `STALE_EVENT`                     | Event older than stored watermark                  | Record; **do not overwrite** current state                     | Historical event store         |\n| `OUT_OF_ORDER_EVENT`              | Sequence gap or reorder                            | Buffer where possible; else reconciliation marker              | Reconciliation queue           |\n| `DUPLICATE_EVENT`                 | Idempotency key already seen                       | Successful no-op                                               | Audit duplicate                |\n| `SPINE_COMMIT_FAILED`             | Canonical transaction fails                        | Retry transient only                                           | Dead-letter and alert          |\n| `PROJECTION_UPDATE_FAILED`        | Projection consumer error                          | Async retry from outbox                                        | Projection repair queue        |\n| `HEALTH_INPUT_INCOMPLETE`         | Required dimensions absent                         | Calculate only where permitted; expose completeness            | `insufficient_data`            |\n| `HEALTH_INPUT_STALE`              | Inputs exceed freshness policy                     | Reduce confidence or suppress                                  | `stale`                        |\n| `RLS_CONTEXT_MISSING`             | Workspace context unset                            | **Reject; return no data**                                     | Security audit event           |\n| `AUTOMATION_POLICY_MISSING`       | No applicable approval policy                      | **Block execution**                                            | Governance queue               |\n| `ACTION_PARTIAL_FAILURE`          | Multi-step action partly completed                 | Stop dependents; compensate where defined                      | Manual recovery case           |\n| `LEARNING_DRIFT_DETECTED`         | Rule performance degrades                          | **Suspend autonomous mode**                                    | Return rule to shadow          |\n\n---\n\n## 4. Source identity\n\nResolution order — deterministic evidence first, classifier last but one:\n\n1. Signed connector or integration identifier\n2. Registered ingress endpoint\n3. OAuth installation or connection identifier\n4. Source-specific event signature\n5. Registered payload fingerprint\n6. Classifier recommendation\n7. Manual triage\n\n**An AI classification result is never authoritative source identity by itself.**\n\n```ts\ninterface SourceClassification {\n  candidates: Array<{ provider: string; confidence: number; evidence: string[] }>;\n  modelVersion: string;\n  classifiedAt: string;\n}\n```\n\nAutomatic assignment requires **all three**:\n\n```\ntop_confidence >= configured_minimum\nAND  top_confidence − second_confidence >= configured_margin\nAND  no deterministic evidence contradicts the result\n```\n\nOtherwise: quarantine.\n\nThis is the same shape as the capability effect classes (§10.0): a classifier is a `derive`\ncapability, and a `derive` capability produces candidates, never facts.\n\n---\n\n## 5. Normalization and coercion\n\nNormalization never silently discards or mutates an invalid source value.\n\n```ts\ninterface CanonicalFieldValue<T> {\n  value: T | null;\n  rawValue?: unknown;\n  sourceSystem: string;\n  sourceRecordId: string;\n  observedAt: string;\n  normalizedAt: string;\n  normalizationStatus: \"valid\" | \"coerced\" | \"invalid\" | \"missing\" | \"conflicted\";\n  normalizationRuleVersion: string;\n  confidence?: number;\n}\n```\n\n| Input condition                      | Behaviour                                            |\n| ------------------------------------ | ---------------------------------------------------- |\n| Safe, lossless conversion            | Convert; mark `coerced`                              |\n| Locale-dependent value               | Use source or workspace locale                       |\n| Ambiguous date or currency           | **Reject automatic conversion**                      |\n| Numeric field contains units or text | Parse only through a registered rule                 |\n| Value exceeds canonical range        | Preserve raw; mark `invalid`                         |\n| Enumerated value unknown             | Map to `unknown` — **never the nearest known value** |\n| Null vs blank vs absent              | **Preserve the distinction**                         |\n\n```json\n{\n  \"field\": \"arr\",\n  \"value\": null,\n  \"rawValue\": \"around 2 lakh\",\n  \"normalizationStatus\": \"invalid\",\n  \"errorCode\": \"FIELD_COERCION_FAILED\"\n}\n```\n\n---\n\n## 6. Identity resolution\n\nDeterministic identifiers before probabilistic similarity:\n\n1. Verified external system identifier\n2. Verified email or domain identifier\n3. Exact normalized identifier match\n4. Composite deterministic match\n5. Probabilistic candidate generation\n6. Human disambiguation\n\n**Threshold alone is insufficient — separation is required:**\n\n```\nautomatic_match allowed when:\n  top_score >= match_threshold\n  AND (top_score − second_score) >= ambiguity_margin\n  AND no immutable identifier conflicts\n```\n\nWhen several candidates clear the threshold without separation: **do not pick the highest, do\nnot merge.** Open an identity-resolution case preserving all candidate scores and evidence.\n\n```json\n{\n  \"resolution_status\": \"unresolved\",\n  \"canonical_status\": \"provisional\",\n  \"eligible_for_automation\": false\n}\n```\n\n---\n\n## 7. Health score semantics\n\nThree independent outputs. One number cannot carry all three.\n\n```ts\ninterface HealthScore {\n  score: number | null;\n  band: \"critical\" | \"at_risk\" | \"healthy\" | \"insufficient_data\" | \"stale\";\n  completeness: number;\n  confidence: number;\n  calculatedAt: string;\n  formulaVersion: string;\n  missingDimensions: string[];\n  staleDimensions: string[];\n}\n```\n\n**Bands are half-open**, so boundaries are deterministic:\n\n| Band     | Definition           |\n| -------- | -------------------- |\n| Critical | `0 <= score < 50`    |\n| At Risk  | `50 <= score < 80`   |\n| Healthy  | `80 <= score <= 100` |\n\nExactly 50 is **At Risk**. Exactly 80 is **Healthy**.\n\nPartial calculation is permitted only when `available_weight >= minimum_weight_coverage` **and**\nrequired dimensions are present; otherwise `score: null`, `band: \"insufficient_data\"`.\n\n**A partial score is never displayed without its completeness indicator.** A stale dimension is\neither excluded or decayed by a versioned function — and which one is explicit in the formula\nversion.\n\n---\n\n## 8. Row-level security fails closed\n\nWhen `app.current_org_id` is absent, empty, malformed or unauthorised: reads return zero rows,\nwrites are rejected, a security-policy violation is recorded. **No default organisation is\ninferred, and no organisation is selected from request data alone.**\n\n```sql\ncreate or replace function security.current_org_id()\nreturns uuid language plpgsql stable security invoker as $$\ndeclare value text;\nbegin\n  value := current_setting('app.current_org_id', true);\n  if value is null or btrim(value) = '' then return null; end if;\n  begin return value::uuid;\n  exception when invalid_text_representation then return null; end;\nend; $$;\n\ncreate policy workspace_isolation on spine.entities\n  using      (org_id = security.current_org_id())\n  with check (org_id = security.current_org_id());\n```\n\nA privileged service role may bypass RLS **only** inside an isolated system worker that\nreceives an explicit organisation identifier, verifies connector ownership, writes the\norganisation identifier into every mutation, produces an immutable audit record, and does not\nprocess records from multiple organisations in one transaction unless explicitly designed for\nadministrative operations.\n\n---\n\n## 9. Governed auto-learning\n\nLearning is a governed evidence pipeline, not direct mutation of agent behaviour.\n\n```\nobservation → candidate → suggested → shadow → approval review → active\n                                                      ↓\n                            remain active · return to shadow · suspend · retire\n```\n\n| State                 | Meaning                                         | Execution permission |\n| --------------------- | ----------------------------------------------- | -------------------- |\n| `observation`         | Individual event or outcome                     | None                 |\n| `candidate`           | Statistically recurring pattern                 | None                 |\n| `suggested`           | Human-readable proposed behaviour               | None                 |\n| `shadow`              | Evaluated against live opportunities, no action | None                 |\n| `approved_proactive`  | May present recommendations                     | User action required |\n| `approved_autonomous` | May execute within a policy envelope            | Governed execution   |\n| `suspended`           | Disabled by drift, error or policy change       | None                 |\n| `retired`             | Permanently inactive, retained for audit        | None                 |\n\n**An agent may move a pattern from `observation` to `candidate` or `suggested`. An agent may\nnever promote a rule to `approved_autonomous`.**\n\nThis extends architecture §8.2c-i: Triage classifies, the Promotion Gate governs. Here the\nsame separation applies to behaviour rather than memory.\n\n### 9.1 Candidate eligibility\n\nMinimum observations · distinct entities · distinct users or workflows · observation period ·\nmaximum single-entity concentration · outcome consistency · no unresolved policy violation ·\nno prohibited sensitive-data dependency.\n\n```yaml\nminimum_observations: 10\nminimum_distinct_entities: 3\nminimum_observation_days: 7\nmaximum_single_entity_concentration: 0.50\nminimum_outcome_consistency: 0.75\n```\n\nInitial operating defaults — not permanent constants.\n\n### 9.2 Bounded pattern types (first release)\n\nTrigger → recommended action · trigger → classification · trigger → routing destination ·\nrepeated approval decision · repeated field correction · repeated workflow sequence ·\nrepeated risk or opportunity signal.\n\n**Free-form policy generation is not permitted in the first release.**\n\n### 9.3 Graduation\n\n**Suggested → shadow** requires a machine-testable trigger and output, available and\nsufficiently complete data, explicitly scoped entities and actions, no unresolved security or\ncompliance issue, and a rollback path.\n\n**Shadow evaluation** measures support · precision · recall where measurable · false-positive\nrate · false-negative rate · abstention rate · coverage · outcome impact · performance by\nsegment · performance over time.\n\n```ts\ninterface RuleEvaluation {\n  ruleVersionId: string;\n  opportunityId: string;\n  predictedOutcome: unknown;\n  observedDecision?: unknown;\n  observedOutcome?: unknown;\n  matched: boolean;\n  correct?: boolean;\n  falsePositive?: boolean;\n  falseNegative?: boolean;\n  evaluatedAt: string;\n}\n```\n\n**Proactive approval** requires minimum evidence volume, precision above threshold, no critical\npolicy violations, an explanation the approver understands, and a recorded Governance approval.\nProactive rules may create suggestions and proposals. **They may not perform external writes.**\n\n**Autonomous approval** requires successful shadow _and_ proactive operation, false-positive\nrate within policy, every possible action explicitly enumerated, an action budget, a rollback\nor compensation strategy, authorisation by required approvers of that rule version, and a\nGovernance-specified envelope:\n\n```ts\ninterface AutonomousPolicyEnvelope {\n  allowedActionTypes: string[];\n  allowedEntityTypes: string[];\n  maximumActionsPerHour: number;\n  maximumValueAtRisk?: number;\n  allowedTimeWindows?: string[];\n  requireFreshDataWithinMinutes: number;\n  minimumRuleConfidence: number;\n  minimumInputCompleteness: number;\n  approvalExpiresAt: string;\n  emergencyDisableSupported: true;\n}\n```\n\n### 9.4 Feedback is evidence, not an update\n\nFeedback events — accepted unchanged · accepted after modification · rejected as incorrect ·\nrejected as irrelevant · rejected on timing · rejected for missing context · ignored · reversed\nafter execution · marked harmful.\n\nA thumbs-up alone is insufficient; request a structured reason where feasible. Weighting may\nconsider role, domain authority, whether a correction was supplied, outcome measurability, and\nconflicting feedback between actors.\n\n**Feedback never causes direct online modification of production behaviour.** It aggregates\ninto evaluation evidence and moves through the rule lifecycle.\n\n---\n\n## 10. Learning storage\n\nA single `brain.learnings` table cannot support auditable learning. Seven concerns, seven\ntables: observations · pattern candidates · rules · rule versions · evaluations · feedback ·\nexecution audit. All tenant-scoped with RLS. Full DDL: [Appendix A](#appendix-a--learning-schema).\n\n### 10.1 The learning schema straddles the repo boundary — split it\n\nThe repository split ([Repository Topology](../deployment/REPOSITORY_TOPOLOGY_AND_MIGRATION_SPEC.md))\nassigns operational state to Product and governed records to Platform. The learning tables fall\non both sides, and the boundary is **the approval record**:\n\n| Table                             | Repo         | Why                                                                           |\n| --------------------------------- | ------------ | ----------------------------------------------------------------------------- |\n| `learning.observations`           | **Product**  | Operational, high-churn evidence                                              |\n| `learning.pattern_candidates`     | **Product**  | Derived evidence; no authority                                                |\n| `learning.rules`, `rule_versions` | **Platform** | Carry `approved_by` / `approved_at` — governance records                      |\n| `learning.rule_evaluations`       | **Platform** | The evidence that justifies approval; an auditor needs it beside the approval |\n| `learning.feedback_events`        | **Platform** | Same — evidence supporting a governance decision                              |\n| `learning.rule_execution_audit`   | **Platform** | Immutable execution record; belongs with the gate                             |\n\nThe rule: **evidence generation is Product; the approval and everything an auditor needs to\nreconstruct it is Platform.** Observations reach Platform through the same outbox path as any\nother promotion — never by direct cross-repo write.\n\n---\n\n## 11. Confidence is six things\n\n| Concept               | Question it answers                        |\n| --------------------- | ------------------------------------------ |\n| Pattern confidence    | How consistent is the observed pattern?    |\n| Prediction confidence | How sure is this specific prediction?      |\n| Input completeness    | How much of the required input is present? |\n| Data freshness        | How current is it?                         |\n| Historical precision  | How often has this rule been right?        |\n| Policy eligibility    | Is this permitted at all?                  |\n\n```ts\ninterface DecisionEvidence {\n  predictionConfidence: number;\n  inputCompleteness: number;\n  freshnessConfidence: number;\n  historicalPrecision: number;\n  sampleSize: number;\n  eligibleForProactiveMode: boolean;\n  eligibleForAutonomousMode: boolean;\n}\n```\n\n**A single confidence number must never represent all six.**\n\n### 11.1 Calibration — name the method\n\nConfidence must be calibrated against observed outcomes, and for small samples a conservative\n**lower bound** is used rather than raw accuracy. A rule at 100% over 3 observations is not\nmore reliable than one at 94% over 500.\n\n_Addition to the source proposal:_ the method must be **named, not left to each service**, or\ntwo components will compute \"confidence\" differently — the exact drift this architecture\nexists to prevent. Specify the **Wilson score lower bound at 95%**, with the confidence level\nrecorded on the rule version. Under Wilson, 3/3 yields ≈ 0.44 and 470/500 yields ≈ 0.92 — the\nordering the invariant demands, produced by a stated formula rather than by judgement.\n\n---\n\n## 12. Drift and expiry\n\nDrift signals: declining precision · rising false-positive rate · material input-distribution\nchange · new source schema versions · workspace policy change · changed user correction\nbehaviour · declining outcome effectiveness · persistent abstention · repeated execution\nreversal.\n\n```\napproved_autonomous → suspended → shadow → re-evaluation and re-approval\n```\n\nRules carry approval expiry. Expired rules return to proactive or shadow per policy.\n**Drift must be able to suspend a rule without deploying application code.**\n\n---\n\n## 13. Twin read/write contract\n\n**May read:** canonical Spine entities · relationships · field provenance · projection status ·\npermitted organisational memory · applicable policies · previous proposals and feedback ·\ncurrent rule versions.\n\n**May write:** conversation events · observations · proposals · suggested entity corrections ·\nsuggested memory entries · feedback requests · candidate patterns.\n\n**May never write directly:** canonical entity changes · approved organisational memory ·\nautonomous policies · active rule versions · external system mutations.\n\nThis closes **M3**: agent memory may be _cited_ by the Twin within its own scope, but may not\ninform organisational-level claims until it has passed triage and promotion.\n\n### 13.1 Agent modes\n\n| Mode           | May do                                                         | May not                                      |\n| -------------- | -------------------------------------------------------------- | -------------------------------------------- |\n| **Advisory**   | Explain and analyse                                            | Create executable proposals; mutate anything |\n| **Proactive**  | Detect conditions; create proposals                            | Execute without approval                     |\n| **Autonomous** | Execute a previously approved rule version within its envelope | Create or broaden its own authority          |\n\nAutonomous mode must verify input completeness and freshness before each execution, use\nidempotency keys, record a complete audit, and stop when confidence or policy eligibility falls\nbelow threshold.\n\n---\n\n## 14. Observability\n\n**Pipeline:** ingestion success rate · quarantine rate · normalization error rate · identity\ncollision rate · **outbox lag** · projection lag · dead-letter count · reconciliation backlog.\n\n**Health scoring:** input completeness · stale-input percentage · recalculation latency ·\nformula version distribution · band transition count.\n\n**Learning:** observations processed · candidates created · suggestions accepted/modified/\nrejected/ignored · shadow precision and coverage · autonomous execution count · false-positive\nrate · reversal rate · drift suspensions · rule approval age · rule version distribution.\n\nAll broken out by workspace, connector, rule and pipeline stage.\n\n---\n\n## 15. Acceptance criteria\n\n1. An unknown payload source cannot enter canonical normalization without source resolution\n2. Invalid field values preserve their raw representation\n3. Ambiguous identity candidates never merge automatically\n4. A successful Spine commit survives failure of every downstream projection\n5. Every projection can be rebuilt from canonical events\n6. Missing organisation context returns zero rows and rejects writes\n7. Scores of exactly 50 and 80 resolve deterministically\n8. A health score cannot conceal incomplete or stale inputs\n9. User feedback cannot directly mutate production behaviour\n10. A rule cannot become autonomous without a recorded Governance approval\n11. Every autonomous execution references a fixed rule version and policy envelope\n12. Drift can suspend a rule without deploying application code\n13. Rules and projections are idempotent and replayable\n14. Every learned behaviour is tenant-scoped, versioned, explainable and reversible\n15. The Twin cannot mutate canonical truth or external systems outside governed paths\n16. _(added)_ No component performs a synchronous write to both Spine and a projection\n17. _(added)_ Confidence lower bounds are computed by the single named method (§11.1)\n\n---\n\n## Appendix A — Learning schema\n\n```sql\ncreate schema if not exists learning;\n\ncreate table learning.observations (\n  id uuid primary key,\n  org_id uuid not null,\n  actor_type text not null,\n  actor_id uuid,\n  context_type text not null,\n  context_entity_ids uuid[] not null default '{}',\n  trigger_type text not null,\n  proposed_action jsonb,\n  actual_action jsonb,\n  outcome jsonb,\n  feedback text,\n  reason_code text,\n  source_event_ids uuid[] not null default '{}',\n  occurred_at timestamptz not null,\n  schema_version text not null,\n  created_at timestamptz not null default now()\n);\n\ncreate table learning.pattern_candidates (\n  id uuid primary key,\n  org_id uuid not null,\n  pattern_type text not null,\n  definition jsonb not null,\n  status text not null,\n  support_count integer not null default 0,\n  distinct_entity_count integer not null default 0,\n  first_observed_at timestamptz,\n  last_observed_at timestamptz,\n  confidence numeric,\n  evidence_query_version text not null,\n  created_at timestamptz not null default now(),\n  updated_at timestamptz not null default now()\n);\n\ncreate table learning.rules (\n  id uuid primary key,\n  org_id uuid not null,\n  name text not null,\n  current_version integer not null,\n  lifecycle_state text not null,\n  created_by_type text not null,\n  created_by_id uuid,\n  created_at timestamptz not null default now(),\n  retired_at timestamptz\n);\n\ncreate table learning.rule_versions (\n  id uuid primary key,\n  rule_id uuid not null references learning.rules(id),\n  version integer not null,\n  trigger_definition jsonb not null,\n  condition_definition jsonb not null,\n  action_definition jsonb not null,\n  explanation text not null,\n  source_candidate_ids uuid[] not null default '{}',\n  confidence numeric,\n  confidence_method text not null,          -- §11.1: named, not implied\n  policy_envelope jsonb,\n  approved_by uuid,\n  approved_at timestamptz,\n  approval_expires_at timestamptz,\n  created_at timestamptz not null default now(),\n  unique(rule_id, version)\n);\n\ncreate table learning.rule_evaluations (\n  id uuid primary key,\n  org_id uuid not null,\n  rule_version_id uuid not null references learning.rule_versions(id),\n  opportunity_id uuid not null,\n  prediction jsonb,\n  observed_decision jsonb,\n  observed_outcome jsonb,\n  matched boolean not null,\n  correct boolean,\n  false_positive boolean,\n  false_negative boolean,\n  evaluated_at timestamptz not null,\n  created_at timestamptz not null default now()\n);\n\ncreate table learning.feedback_events (\n  id uuid primary key,\n  org_id uuid not null,\n  rule_version_id uuid references learning.rule_versions(id),\n  proposal_id uuid,\n  actor_id uuid not null,\n  feedback_type text not null,\n  reason_code text,\n  original_output jsonb,\n  corrected_output jsonb,\n  created_at timestamptz not null default now()\n);\n\ncreate table learning.rule_execution_audit (\n  id uuid primary key,\n  org_id uuid not null,\n  rule_version_id uuid not null references learning.rule_versions(id),\n  proposal_id uuid,\n  action_type text not null,\n  request_payload jsonb not null,\n  execution_status text not null,\n  result_payload jsonb,\n  idempotency_key text not null,\n  compensation_status text,\n  executed_at timestamptz,\n  created_at timestamptz not null default now(),\n  unique(org_id, idempotency_key)\n);\n```\n\nEvery table requires workspace-isolated RLS per §8.\n\n---\n\n**Related:** [Architecture §8–§11](../../architecture/INTEGRATEWISE_ARCHITECTURE.md) ·\n[Repository Topology](../deployment/REPOSITORY_TOPOLOGY_AND_MIGRATION_SPEC.md) ·\n[Marketplace Channels and Data Flow](../surfaces/MARKETPLACE_CHANNELS_AND_DATA_FLOW_SPEC.md)\n"}