Unclassified

Edge Conditions and Governed Auto-Learning

Status: Proposed
Document type: Specification
Authority: Subordinate to INTEGRATEWISE_ARCHITECTURE.md
Canonical sections implemented: §8 (Truth, memory scopes, promotion) · §9 (Four flows, Normalizer/Triage) · §10 (Capability effect classes, governance) · §11.4 (Response state) · §14 (Tenancy)
Owner: Platform + Data
Version: 1.0
Last verified against: HEAD d941a422
Last verified date: 2026-07-31
Resolves: S0b (org_memory write ambiguity) · M1 (store vs cache) · M3 (agent memory citation)

Adds implementation precision. Does not redefine kernel vocabulary, ownership boundaries,
truth authority, runtime ordering or governance invariants.


1. Invariants

  1. Spine is the system of record. Brain, Twin, Knowledge, Workbench and analytical stores
    are projections or reasoning layers. None of them is a parallel canonical store.
  2. Uncertainty is represented explicitly. unknown · unresolved · insufficient_data
    · conflicted · stale · failed are valid persisted states. The platform never converts
    uncertainty into a guessed canonical value.
  3. Tenant boundaries fail closed. Missing or invalid workspace context returns zero rows
    and rejects writes.
  4. Autonomy is granted by policy, never inferred by an agent. Agents may discover patterns
    and propose rules. Only Governance authorises autonomous execution.
  5. All derived behaviour is versioned and reversible. Rules, score formulas, mappings,
    learned patterns and execution policies require version history, provenance, evaluation
    evidence and rollback.

Invariant 4 is the agent-scoped corollary of the Authority Law (confidence increases
trust; confidence never grants authority
). It is not a ninth law — it is the same law applied
where it is most often violated.


2. No dual-write: the transactional outbox

The system must not perform independent synchronous writes to Spine and to any projection.

Source event
    ↓
┌─ Spine transaction ──────────────────────┐
│  canonical entity mutation                │
│  field lineage mutation                   │
│  transactional outbox event               │   ← same transaction, always
└──────────────────┬───────────────────────┘
                   ↓  asynchronous consumers
     Brain projection · search index · vector index
     signal engine · Workbench projection · org-memory cache

The canonical mutation and the outbox event commit in the same database transaction.
Everything downstream is an asynchronous consumer of that event.

2.1 Delivery semantics

At-least-once delivery · stable event identifiers · idempotent consumers · monotonic entity
versions · retry with exponential backoff and jitter · dead-letter queues · reconciliation
jobs.

The architecture does not claim cross-store exactly-once delivery. Any document that does
is wrong.

2.2 Partial failure

FailureResulting state
Spine transaction failsNo canonical change and no outbox event
Spine succeeds, projection failsCanonical state valid; outbox retries
Search/vector projection failsCanonical valid; that projection is lagging
Retry limit exceededEvent enters the projection repair queue
Projection receives an older versionSuccessful no-op
Projection receives a duplicateSuccessful no-op
interface ProjectionStatus {
  projectionName: string;
  entityId: string;
  canonicalVersion: number;
  projectedVersion: number;
  status: "current" | "lagging" | "failed" | "rebuilding";
  lastSuccessfulAt?: string;
  lastErrorCode?: string;
}

2.3 This resolves S0b and M1

Architecture S0b records org_memory as a documented cache with six direct writers and no
defined reconciliation. The outbox settles it:

Neon memory.org_memory is the store. The D1 org_memory table is a projection.
Exactly one writer — the promotion gate, inside the Spine transaction. The other five
services become read-only consumers of the outbox.

M1 is closed on that basis. The same reasoning retires the "Brain as second write
destination" framing entirely: Brain is a reasoning and learning projection used by the
Twin, not a truth store.
The durable control path is unchanged:

Spine → Twin proposal → Governance → Hermes execution → re-ingestion into Spine

3. Edge Condition Registry

Every pipeline stage registers its known edge conditions:

edge_code · stage · detection_condition · severity · handling_strategy ·
fallback_state · retry_policy · owner · user_visibility · audit_required

3.1 Canonical edge conditions

CodeConditionRequired handlingFallback
SOURCE_UNKNOWNSource system unidentifiableDeterministic fingerprinting; classifier recommendation onlyQuarantine for triage
SOURCE_CLASSIFICATION_AMBIGUOUSMultiple source candidates within marginDo not assign automaticallyQuarantine with candidate list
SCHEMA_VERSION_UNKNOWNPayload schema unsupportedPreserve raw; request mappingDead-letter
FIELD_COERCION_FAILEDValue not convertible to canonical typeStore raw; emit field-level errorCanonical field null
REQUIRED_FIELD_MISSINGRequired canonical field absentConfigured derivation onlyIncomplete provisional record
IDENTITY_COLLISIONMultiple candidates exceed thresholdRequire disambiguation; do not mergeUnresolved identity queue
IDENTITY_LOW_CONFIDENCENo candidate meets thresholdProvisional identity only where policy permitsQuarantine
IDENTITY_SPLIT_SUSPECTEDEntity appears to represent multiple real entitiesSuspend automatic updates to conflicting fieldsIdentity-review case
IDENTITY_MERGE_CONFLICTMerge would overwrite incompatible valuesField-level merge proposalLeave entities separate
STALE_EVENTEvent older than stored watermarkRecord; do not overwrite current stateHistorical event store
OUT_OF_ORDER_EVENTSequence gap or reorderBuffer where possible; else reconciliation markerReconciliation queue
DUPLICATE_EVENTIdempotency key already seenSuccessful no-opAudit duplicate
SPINE_COMMIT_FAILEDCanonical transaction failsRetry transient onlyDead-letter and alert
PROJECTION_UPDATE_FAILEDProjection consumer errorAsync retry from outboxProjection repair queue
HEALTH_INPUT_INCOMPLETERequired dimensions absentCalculate only where permitted; expose completenessinsufficient_data
HEALTH_INPUT_STALEInputs exceed freshness policyReduce confidence or suppressstale
RLS_CONTEXT_MISSINGWorkspace context unsetReject; return no dataSecurity audit event
AUTOMATION_POLICY_MISSINGNo applicable approval policyBlock executionGovernance queue
ACTION_PARTIAL_FAILUREMulti-step action partly completedStop dependents; compensate where definedManual recovery case
LEARNING_DRIFT_DETECTEDRule performance degradesSuspend autonomous modeReturn rule to shadow

4. Source identity

Resolution order — deterministic evidence first, classifier last but one:

  1. Signed connector or integration identifier
  2. Registered ingress endpoint
  3. OAuth installation or connection identifier
  4. Source-specific event signature
  5. Registered payload fingerprint
  6. Classifier recommendation
  7. Manual triage

An AI classification result is never authoritative source identity by itself.

interface SourceClassification {
  candidates: Array<{ provider: string; confidence: number; evidence: string[] }>;
  modelVersion: string;
  classifiedAt: string;
}

Automatic assignment requires all three:

top_confidence >= configured_minimum
AND  top_confidence − second_confidence >= configured_margin
AND  no deterministic evidence contradicts the result

Otherwise: quarantine.

This is the same shape as the capability effect classes (§10.0): a classifier is a derive
capability, and a derive capability produces candidates, never facts.


5. Normalization and coercion

Normalization never silently discards or mutates an invalid source value.

interface CanonicalFieldValue<T> {
  value: T | null;
  rawValue?: unknown;
  sourceSystem: string;
  sourceRecordId: string;
  observedAt: string;
  normalizedAt: string;
  normalizationStatus: "valid" | "coerced" | "invalid" | "missing" | "conflicted";
  normalizationRuleVersion: string;
  confidence?: number;
}
Input conditionBehaviour
Safe, lossless conversionConvert; mark coerced
Locale-dependent valueUse source or workspace locale
Ambiguous date or currencyReject automatic conversion
Numeric field contains units or textParse only through a registered rule
Value exceeds canonical rangePreserve raw; mark invalid
Enumerated value unknownMap to unknown — never the nearest known value
Null vs blank vs absentPreserve the distinction
{
  "field": "arr",
  "value": null,
  "rawValue": "around 2 lakh",
  "normalizationStatus": "invalid",
  "errorCode": "FIELD_COERCION_FAILED"
}

6. Identity resolution

Deterministic identifiers before probabilistic similarity:

  1. Verified external system identifier
  2. Verified email or domain identifier
  3. Exact normalized identifier match
  4. Composite deterministic match
  5. Probabilistic candidate generation
  6. Human disambiguation

Threshold alone is insufficient — separation is required:

automatic_match allowed when:
  top_score >= match_threshold
  AND (top_score − second_score) >= ambiguity_margin
  AND no immutable identifier conflicts

When several candidates clear the threshold without separation: do not pick the highest, do
not merge.
Open an identity-resolution case preserving all candidate scores and evidence.

{
  "resolution_status": "unresolved",
  "canonical_status": "provisional",
  "eligible_for_automation": false
}

7. Health score semantics

Three independent outputs. One number cannot carry all three.

interface HealthScore {
  score: number | null;
  band: "critical" | "at_risk" | "healthy" | "insufficient_data" | "stale";
  completeness: number;
  confidence: number;
  calculatedAt: string;
  formulaVersion: string;
  missingDimensions: string[];
  staleDimensions: string[];
}

Bands are half-open, so boundaries are deterministic:

BandDefinition
Critical0 <= score < 50
At Risk50 <= score < 80
Healthy80 <= score <= 100

Exactly 50 is At Risk. Exactly 80 is Healthy.

Partial calculation is permitted only when available_weight >= minimum_weight_coverage and
required dimensions are present; otherwise score: null, band: "insufficient_data".

A partial score is never displayed without its completeness indicator. A stale dimension is
either excluded or decayed by a versioned function — and which one is explicit in the formula
version.


8. Row-level security fails closed

When app.current_org_id is absent, empty, malformed or unauthorised: reads return zero rows,
writes are rejected, a security-policy violation is recorded. No default organisation is
inferred, and no organisation is selected from request data alone.

create or replace function security.current_org_id()
returns uuid language plpgsql stable security invoker as $$
declare value text;
begin
  value := current_setting('app.current_org_id', true);
  if value is null or btrim(value) = '' then return null; end if;
  begin return value::uuid;
  exception when invalid_text_representation then return null; end;
end; $$;

create policy workspace_isolation on spine.entities
  using      (org_id = security.current_org_id())
  with check (org_id = security.current_org_id());

A privileged service role may bypass RLS only inside an isolated system worker that
receives an explicit organisation identifier, verifies connector ownership, writes the
organisation identifier into every mutation, produces an immutable audit record, and does not
process records from multiple organisations in one transaction unless explicitly designed for
administrative operations.


9. Governed auto-learning

Learning is a governed evidence pipeline, not direct mutation of agent behaviour.

observation → candidate → suggested → shadow → approval review → active
                                                      ↓
                            remain active · return to shadow · suspend · retire
StateMeaningExecution permission
observationIndividual event or outcomeNone
candidateStatistically recurring patternNone
suggestedHuman-readable proposed behaviourNone
shadowEvaluated against live opportunities, no actionNone
approved_proactiveMay present recommendationsUser action required
approved_autonomousMay execute within a policy envelopeGoverned execution
suspendedDisabled by drift, error or policy changeNone
retiredPermanently inactive, retained for auditNone

An agent may move a pattern from observation to candidate or suggested. An agent may
never promote a rule to approved_autonomous.

This extends architecture §8.2c-i: Triage classifies, the Promotion Gate governs. Here the
same separation applies to behaviour rather than memory.

9.1 Candidate eligibility

Minimum observations · distinct entities · distinct users or workflows · observation period ·
maximum single-entity concentration · outcome consistency · no unresolved policy violation ·
no prohibited sensitive-data dependency.

minimum_observations: 10
minimum_distinct_entities: 3
minimum_observation_days: 7
maximum_single_entity_concentration: 0.50
minimum_outcome_consistency: 0.75

Initial operating defaults — not permanent constants.

9.2 Bounded pattern types (first release)

Trigger → recommended action · trigger → classification · trigger → routing destination ·
repeated approval decision · repeated field correction · repeated workflow sequence ·
repeated risk or opportunity signal.

Free-form policy generation is not permitted in the first release.

9.3 Graduation

Suggested → shadow requires a machine-testable trigger and output, available and
sufficiently complete data, explicitly scoped entities and actions, no unresolved security or
compliance issue, and a rollback path.

Shadow evaluation measures support · precision · recall where measurable · false-positive
rate · false-negative rate · abstention rate · coverage · outcome impact · performance by
segment · performance over time.

interface RuleEvaluation {
  ruleVersionId: string;
  opportunityId: string;
  predictedOutcome: unknown;
  observedDecision?: unknown;
  observedOutcome?: unknown;
  matched: boolean;
  correct?: boolean;
  falsePositive?: boolean;
  falseNegative?: boolean;
  evaluatedAt: string;
}

Proactive approval requires minimum evidence volume, precision above threshold, no critical
policy violations, an explanation the approver understands, and a recorded Governance approval.
Proactive rules may create suggestions and proposals. They may not perform external writes.

Autonomous approval requires successful shadow and proactive operation, false-positive
rate within policy, every possible action explicitly enumerated, an action budget, a rollback
or compensation strategy, authorisation by required approvers of that rule version, and a
Governance-specified envelope:

interface AutonomousPolicyEnvelope {
  allowedActionTypes: string[];
  allowedEntityTypes: string[];
  maximumActionsPerHour: number;
  maximumValueAtRisk?: number;
  allowedTimeWindows?: string[];
  requireFreshDataWithinMinutes: number;
  minimumRuleConfidence: number;
  minimumInputCompleteness: number;
  approvalExpiresAt: string;
  emergencyDisableSupported: true;
}

9.4 Feedback is evidence, not an update

Feedback events — accepted unchanged · accepted after modification · rejected as incorrect ·
rejected as irrelevant · rejected on timing · rejected for missing context · ignored · reversed
after execution · marked harmful.

A thumbs-up alone is insufficient; request a structured reason where feasible. Weighting may
consider role, domain authority, whether a correction was supplied, outcome measurability, and
conflicting feedback between actors.

Feedback never causes direct online modification of production behaviour. It aggregates
into evaluation evidence and moves through the rule lifecycle.


10. Learning storage

A single brain.learnings table cannot support auditable learning. Seven concerns, seven
tables: observations · pattern candidates · rules · rule versions · evaluations · feedback ·
execution audit. All tenant-scoped with RLS. Full DDL: Appendix A.

10.1 The learning schema straddles the repo boundary — split it

The repository split (Repository Topology)
assigns operational state to Product and governed records to Platform. The learning tables fall
on both sides, and the boundary is the approval record:

TableRepoWhy
learning.observationsProductOperational, high-churn evidence
learning.pattern_candidatesProductDerived evidence; no authority
learning.rules, rule_versionsPlatformCarry approved_by / approved_at — governance records
learning.rule_evaluationsPlatformThe evidence that justifies approval; an auditor needs it beside the approval
learning.feedback_eventsPlatformSame — evidence supporting a governance decision
learning.rule_execution_auditPlatformImmutable execution record; belongs with the gate

The rule: evidence generation is Product; the approval and everything an auditor needs to
reconstruct it is Platform.
Observations reach Platform through the same outbox path as any
other promotion — never by direct cross-repo write.


11. Confidence is six things

ConceptQuestion it answers
Pattern confidenceHow consistent is the observed pattern?
Prediction confidenceHow sure is this specific prediction?
Input completenessHow much of the required input is present?
Data freshnessHow current is it?
Historical precisionHow often has this rule been right?
Policy eligibilityIs this permitted at all?
interface DecisionEvidence {
  predictionConfidence: number;
  inputCompleteness: number;
  freshnessConfidence: number;
  historicalPrecision: number;
  sampleSize: number;
  eligibleForProactiveMode: boolean;
  eligibleForAutonomousMode: boolean;
}

A single confidence number must never represent all six.

11.1 Calibration — name the method

Confidence must be calibrated against observed outcomes, and for small samples a conservative
lower bound is used rather than raw accuracy. A rule at 100% over 3 observations is not
more reliable than one at 94% over 500.

Addition to the source proposal: the method must be named, not left to each service, or
two components will compute "confidence" differently — the exact drift this architecture
exists to prevent. Specify the Wilson score lower bound at 95%, with the confidence level
recorded on the rule version. Under Wilson, 3/3 yields ≈ 0.44 and 470/500 yields ≈ 0.92 — the
ordering the invariant demands, produced by a stated formula rather than by judgement.


12. Drift and expiry

Drift signals: declining precision · rising false-positive rate · material input-distribution
change · new source schema versions · workspace policy change · changed user correction
behaviour · declining outcome effectiveness · persistent abstention · repeated execution
reversal.

approved_autonomous → suspended → shadow → re-evaluation and re-approval

Rules carry approval expiry. Expired rules return to proactive or shadow per policy.
Drift must be able to suspend a rule without deploying application code.


13. Twin read/write contract

May read: canonical Spine entities · relationships · field provenance · projection status ·
permitted organisational memory · applicable policies · previous proposals and feedback ·
current rule versions.

May write: conversation events · observations · proposals · suggested entity corrections ·
suggested memory entries · feedback requests · candidate patterns.

May never write directly: canonical entity changes · approved organisational memory ·
autonomous policies · active rule versions · external system mutations.

This closes M3: agent memory may be cited by the Twin within its own scope, but may not
inform organisational-level claims until it has passed triage and promotion.

13.1 Agent modes

ModeMay doMay not
AdvisoryExplain and analyseCreate executable proposals; mutate anything
ProactiveDetect conditions; create proposalsExecute without approval
AutonomousExecute a previously approved rule version within its envelopeCreate or broaden its own authority

Autonomous mode must verify input completeness and freshness before each execution, use
idempotency keys, record a complete audit, and stop when confidence or policy eligibility falls
below threshold.


14. Observability

Pipeline: ingestion success rate · quarantine rate · normalization error rate · identity
collision rate · outbox lag · projection lag · dead-letter count · reconciliation backlog.

Health scoring: input completeness · stale-input percentage · recalculation latency ·
formula version distribution · band transition count.

Learning: observations processed · candidates created · suggestions accepted/modified/
rejected/ignored · shadow precision and coverage · autonomous execution count · false-positive
rate · reversal rate · drift suspensions · rule approval age · rule version distribution.

All broken out by workspace, connector, rule and pipeline stage.


15. Acceptance criteria

  1. An unknown payload source cannot enter canonical normalization without source resolution
  2. Invalid field values preserve their raw representation
  3. Ambiguous identity candidates never merge automatically
  4. A successful Spine commit survives failure of every downstream projection
  5. Every projection can be rebuilt from canonical events
  6. Missing organisation context returns zero rows and rejects writes
  7. Scores of exactly 50 and 80 resolve deterministically
  8. A health score cannot conceal incomplete or stale inputs
  9. User feedback cannot directly mutate production behaviour
  10. A rule cannot become autonomous without a recorded Governance approval
  11. Every autonomous execution references a fixed rule version and policy envelope
  12. Drift can suspend a rule without deploying application code
  13. Rules and projections are idempotent and replayable
  14. Every learned behaviour is tenant-scoped, versioned, explainable and reversible
  15. The Twin cannot mutate canonical truth or external systems outside governed paths
  16. (added) No component performs a synchronous write to both Spine and a projection
  17. (added) Confidence lower bounds are computed by the single named method (§11.1)

Appendix A — Learning schema

create schema if not exists learning;

create table learning.observations (
  id uuid primary key,
  org_id uuid not null,
  actor_type text not null,
  actor_id uuid,
  context_type text not null,
  context_entity_ids uuid[] not null default '{}',
  trigger_type text not null,
  proposed_action jsonb,
  actual_action jsonb,
  outcome jsonb,
  feedback text,
  reason_code text,
  source_event_ids uuid[] not null default '{}',
  occurred_at timestamptz not null,
  schema_version text not null,
  created_at timestamptz not null default now()
);

create table learning.pattern_candidates (
  id uuid primary key,
  org_id uuid not null,
  pattern_type text not null,
  definition jsonb not null,
  status text not null,
  support_count integer not null default 0,
  distinct_entity_count integer not null default 0,
  first_observed_at timestamptz,
  last_observed_at timestamptz,
  confidence numeric,
  evidence_query_version text not null,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

create table learning.rules (
  id uuid primary key,
  org_id uuid not null,
  name text not null,
  current_version integer not null,
  lifecycle_state text not null,
  created_by_type text not null,
  created_by_id uuid,
  created_at timestamptz not null default now(),
  retired_at timestamptz
);

create table learning.rule_versions (
  id uuid primary key,
  rule_id uuid not null references learning.rules(id),
  version integer not null,
  trigger_definition jsonb not null,
  condition_definition jsonb not null,
  action_definition jsonb not null,
  explanation text not null,
  source_candidate_ids uuid[] not null default '{}',
  confidence numeric,
  confidence_method text not null,          -- §11.1: named, not implied
  policy_envelope jsonb,
  approved_by uuid,
  approved_at timestamptz,
  approval_expires_at timestamptz,
  created_at timestamptz not null default now(),
  unique(rule_id, version)
);

create table learning.rule_evaluations (
  id uuid primary key,
  org_id uuid not null,
  rule_version_id uuid not null references learning.rule_versions(id),
  opportunity_id uuid not null,
  prediction jsonb,
  observed_decision jsonb,
  observed_outcome jsonb,
  matched boolean not null,
  correct boolean,
  false_positive boolean,
  false_negative boolean,
  evaluated_at timestamptz not null,
  created_at timestamptz not null default now()
);

create table learning.feedback_events (
  id uuid primary key,
  org_id uuid not null,
  rule_version_id uuid references learning.rule_versions(id),
  proposal_id uuid,
  actor_id uuid not null,
  feedback_type text not null,
  reason_code text,
  original_output jsonb,
  corrected_output jsonb,
  created_at timestamptz not null default now()
);

create table learning.rule_execution_audit (
  id uuid primary key,
  org_id uuid not null,
  rule_version_id uuid not null references learning.rule_versions(id),
  proposal_id uuid,
  action_type text not null,
  request_payload jsonb not null,
  execution_status text not null,
  result_payload jsonb,
  idempotency_key text not null,
  compensation_status text,
  executed_at timestamptz,
  created_at timestamptz not null default now(),
  unique(org_id, idempotency_key)
);

Every table requires workspace-isolated RLS per §8.


Related: Architecture §8–§11 ·
Repository Topology ·
Marketplace Channels and Data Flow