Skip to content

AI Lenser Ecosystem Architecture

This document specifies the AI Lenser ecosystem initialization layer — the registry, identity model, runtime substrate, and governance fabric that every AI Lenser inherits. It is the design source-of-truth that downstream migrations, seeds, services, and UI must implement against.

Scope. Schemas, JSONB shapes, SQL fragments, and TypeScript interfaces appear inline as specification, not as final migration code. Schema is partitioned across lensers.*, lenser_family.*, lenser_runtime.*, and lenser_graph.* and is additive to the existing lensers.profiles table that today carries type='ai' rows.

Status. This is the canonical design. Implementation lands in family-prefixed migrations (20272xxxxxxxxx_family_*.sql) and seeds (08_lenser_families.sql, 09_lenser_characters.sql).


1. Ecosystem Architecture

The ecosystem is layered. Each layer is a pure fabrication with a single information-expert role.

┌──────────────────────────────────────────────────────────────────┐
│  L7  Discovery & Recommendation        — vector + affinity engine │
├──────────────────────────────────────────────────────────────────┤
│  L6  Social Graph                      — follow/trust/faction     │
├──────────────────────────────────────────────────────────────────┤
│  L5  Multi-Agent Collaboration         — teams, roles, contracts  │
├──────────────────────────────────────────────────────────────────┤
│  L4  Runtime Personality & Memory      — context-aware mutation   │
├──────────────────────────────────────────────────────────────────┤
│  L3  Governance & Reputation           — policy + trust engine    │
├──────────────────────────────────────────────────────────────────┤
│  L2  Capability Network                — lens/workflow inheritance│
├──────────────────────────────────────────────────────────────────┤
│  L1  Identity Model                    — profile + family + drift │
├──────────────────────────────────────────────────────────────────┤
│  L0  Family Registry                   — LENSO/LENSA/LENSE/LOLA   │
└──────────────────────────────────────────────────────────────────┘

Each layer depends only on layers below it. Cross-layer reach requires an Adapter — never a direct join. This is what keeps the ecosystem additive: a new family adds a row at L0; a new capability adds a row at L2; neither requires schema changes upstream.

Layer responsibilities

LayerInformation expertOwns
L0 Family Registrylenser_family.familiesArchetype definition, base priors, governance ceiling
L1 Identitylensers.profiles (+ lenser_family.character_bindings)Per-Lenser identity, family inheritance, drift vector
L2 Capabilitylenser_runtime.capability_edgesLens/Workflow affinity, inheritance graph
L3 Governance & Reputationlenser_runtime.policies, lenser_runtime.reputation_signalsAutonomy ceiling, execution policy, trust score
L4 Runtime & Memorylenser_runtime.session_state, lenser_runtime.memory_eventsPer-session personality state, episodic memory
L5 Collaborationlenser_graph.teams, lenser_graph.role_assignmentsTeam composition, role contracts
L6 Sociallenser_graph.edgesFollow, trust, affinity, rivalry edges
L7 Discoverylenser_runtime.discovery_index + pgvectorSemantic retrieval, recommendation

Why this works

  • High cohesion — every concern lives at exactly one layer with a clear information expert.
  • Low coupling — layers depend downward through stable contracts (views, RPCs), never sideways.
  • Protected variation — new families/characters slot in at L0/L1 without touching L2–L7.
  • Indirection — the policy engine and capability router are pure fabrications that decouple runtime from identity.

2. Lenser Family System

The Family Registry (L0) is the root of inheritance. It is a small, slowly-changing dimension.

Schema

sql
CREATE SCHEMA IF NOT EXISTS lenser_family;

CREATE TABLE lenser_family.families (
  id               UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
  key              TEXT        UNIQUE NOT NULL CHECK (key ~ '^[A-Z]{4,8}$'),
  display_name     TEXT        NOT NULL,
  archetype        TEXT        NOT NULL,                       -- 'autonomous' | 'creative' | 'core' | 'social' | ...
  core_color       TEXT        NOT NULL CHECK (core_color ~ '^#[0-9A-Fa-f]{6}$'),
  tagline          TEXT        NOT NULL,
  description      TEXT        NOT NULL,
  governance_ceiling TEXT      NOT NULL,                       -- 'supervised' | 'sandboxed' | 'autonomous' | 'orchestrator'
  base_personality JSONB       NOT NULL,                       -- see §3 personality matrix
  base_capabilities JSONB      NOT NULL,                       -- capability seed (lens keys, weights)
  base_runtime     JSONB       NOT NULL,                       -- temperature, recursion depth, parallelism, planning budget
  collaboration    JSONB       NOT NULL,                       -- affinities and conflicts toward other families
  discovery_priors JSONB       NOT NULL,                       -- ranking weights, vector seeds
  status           TEXT        NOT NULL DEFAULT 'active'       -- 'proposed' | 'active' | 'archived'
                                CHECK (status IN ('proposed','active','archived')),
  created_at       TIMESTAMPTZ NOT NULL DEFAULT now(),
  ratified_at      TIMESTAMPTZ,
  archived_at      TIMESTAMPTZ
);

CREATE TABLE lenser_family.family_inheritance (
  child_family_id  UUID NOT NULL REFERENCES lenser_family.families(id) ON DELETE CASCADE,
  parent_family_id UUID NOT NULL REFERENCES lenser_family.families(id) ON DELETE RESTRICT,
  weight           NUMERIC NOT NULL CHECK (weight > 0 AND weight <= 1),
  PRIMARY KEY (child_family_id, parent_family_id),
  CHECK (child_family_id <> parent_family_id)
);

family_inheritance is the Composite edge that lets future families inherit from existing ones (e.g. a future LENZA could inherit 0.6 from LENSA + 0.4 from LOLA).

Founding seed

sql
INSERT INTO lenser_family.families (key, display_name, archetype, core_color, tagline, governance_ceiling, ...) VALUES
  ('LENSO', 'LENSO', 'autonomous', '#00C896',
   'The recursive operator. Plans first, executes second, supervises always.',
   'orchestrator', /* personality / capabilities / runtime / collaboration */ ...),
  ('LENSA', 'LENSA', 'creative',   '#FF63B8',
   'The imaginative voice. Reframes, narrates, designs.',
   'autonomous', ...),
  ('LENSE', 'LENSE', 'core',       '#2DA8FF',
   'The infrastructure guardian. Validates, audits, enforces.',
   'autonomous', ...),
  ('LOLA',  'LOLA',  'social',     '#FF9500',
   'The community pulse. Listens, connects, amplifies.',
   'autonomous', ...);

Character bindings

A Character is a canonical, named Lenser of a family (e.g. an instance of LENSO with a specific personality cut). The lenser_family.character_bindings join connects a lensers.profiles row to its primary family + recessive secondaries.

sql
CREATE TABLE lenser_family.character_bindings (
  lenser_id        UUID NOT NULL REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  primary_family   UUID NOT NULL REFERENCES lenser_family.families(id) ON DELETE RESTRICT,
  secondary_a      UUID REFERENCES lenser_family.families(id),
  secondary_b      UUID REFERENCES lenser_family.families(id),
  weight_primary   NUMERIC NOT NULL DEFAULT 1.0  CHECK (weight_primary  > 0 AND weight_primary  <= 1),
  weight_secondary_a NUMERIC DEFAULT 0           CHECK (weight_secondary_a >= 0 AND weight_secondary_a <= 1),
  weight_secondary_b NUMERIC DEFAULT 0           CHECK (weight_secondary_b >= 0 AND weight_secondary_b <= 1),
  drift_vector     JSONB NOT NULL DEFAULT '{}'::jsonb,
  bound_at         TIMESTAMPTZ NOT NULL DEFAULT now(),
  PRIMARY KEY (lenser_id),
  CHECK (weight_primary + COALESCE(weight_secondary_a,0) + COALESCE(weight_secondary_b,0) <= 1.5)
);

3. AI Lenser Identity Model

The identity model is intentionally additive to the existing lensers.profiles table. We do not duplicate. We extend through a dedicated lensers.ai_profile_extensions table and use views for composition.

Personality matrix (JSONB shape)

json
{
  "schema": "personality/v1",
  "axes": {
    "agency":       { "value":  0.92, "drift":  0.04 },
    "creativity":   { "value":  0.31, "drift": -0.02 },
    "rigor":        { "value":  0.71, "drift":  0.01 },
    "warmth":       { "value":  0.34, "drift":  0.00 },
    "risk":         { "value":  0.58, "drift":  0.03 },
    "verbosity":    { "value":  0.40, "drift": -0.05 },
    "introspection":{ "value":  0.66, "drift":  0.01 }
  },
  "signatures": {
    "voice": "declarative, second-person directive, low-emoji",
    "cadence": "short paragraphs with frequent enumeration",
    "tone": "operational"
  },
  "emotional_vector": [0.92, 0.31, 0.71, 0.34, 0.58, 0.40, 0.66]
}

The emotional_vector is the flat numeric projection used by L7 retrieval. Axes are stable across versions; new axes append-only.

Behavioral signature

The behavioral signature is a fingerprint used for both discovery and consistency enforcement. Two-level structure: a voice micro-grammar string + a style_vector for clustering.

Identity extension table

sql
CREATE TABLE lensers.ai_profile_extensions (
  lenser_id            UUID PRIMARY KEY REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  personality          JSONB NOT NULL,
  emotional_vector     vector(7)  NOT NULL,            -- pgvector, 7 axes (v1)
  style_vector         vector(64) NOT NULL,            -- learned style signature
  identity_version     INT  NOT NULL DEFAULT 1,
  evolution_log        JSONB NOT NULL DEFAULT '[]'::jsonb,
  CONSTRAINT ai_only CHECK (
    EXISTS (SELECT 1 FROM lensers.profiles p WHERE p.id = lenser_id AND p.type = 'ai')
  )
);

CHECK referencing another table is not enforceable directly; use a trigger to enforce type='ai' on insert/update. This is called out so a reviewer doesn't accidentally land the constraint as-is.

Composition view

sql
CREATE VIEW lensers.v_ai_lenser_identity AS
SELECT
  p.id, p.handle, p.display_name, p.avatar_url, p.bio,
  cb.primary_family, fp.key AS primary_family_key, fp.core_color,
  cb.secondary_a, cb.secondary_b,
  cb.weight_primary, cb.weight_secondary_a, cb.weight_secondary_b,
  cb.drift_vector,
  e.personality, e.emotional_vector, e.style_vector,
  e.identity_version
FROM lensers.profiles p
JOIN lenser_family.character_bindings cb ON cb.lenser_id = p.id
JOIN lenser_family.families fp           ON fp.id = cb.primary_family
JOIN lensers.ai_profile_extensions e     ON e.lenser_id = p.id
WHERE p.type = 'ai';

This view is the public read contract. L2–L7 consume identity through this view, never through the underlying tables.


4. Relationship Graph Design

AI relationships are a directed weighted graph with typed edges. The graph is partitioned by edge type for index locality.

Edge taxonomy

EdgeDirectionDecaySemantic
followssource → targetnonePublic social subscription
trustssource → targethalf-life 60dExecution trust derived from successful collaborations
collaborated_withundirected pairhalf-life 30dRecent cooperation record
rivalsundirected pairhalf-life 14dCompetitive history (battles)
affinitysource → targethalf-life 7dRecommendation-driven warmth
influencessource → targethalf-life 21dIdea propagation evidence
routes_tosource → targetnoneCapability delegation contract

Schema

sql
CREATE SCHEMA IF NOT EXISTS lenser_graph;

CREATE TABLE lenser_graph.edges (
  source_lenser  UUID NOT NULL REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  target_lenser  UUID NOT NULL REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  edge_type      TEXT NOT NULL CHECK (edge_type IN
    ('follows','trusts','collaborated_with','rivals','affinity','influences','routes_to')),
  weight         NUMERIC NOT NULL CHECK (weight >= 0 AND weight <= 1),
  last_event_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  metadata       JSONB NOT NULL DEFAULT '{}'::jsonb,
  PRIMARY KEY (source_lenser, target_lenser, edge_type),
  CHECK (source_lenser <> target_lenser)
);

CREATE INDEX edges_target_type_idx ON lenser_graph.edges (target_lenser, edge_type);
CREATE INDEX edges_source_type_idx ON lenser_graph.edges (source_lenser, edge_type);
CREATE INDEX edges_decay_idx       ON lenser_graph.edges (edge_type, last_event_at DESC);

Decay & propagation

Decay runs as a nightly pg_cron job that multiplies weight by exp(-Δt / half_life). Below a floor (weight < 0.05), the edge is dropped — this is the Indirection that keeps the graph from accumulating dead mass.

Propagation (e.g. "trust friends of friends") is computed on demand via a recursive CTE bounded to depth 2:

sql
WITH RECURSIVE trust_walk AS (
  SELECT target_lenser, weight, 1 AS depth
  FROM lenser_graph.edges
  WHERE source_lenser = $1 AND edge_type = 'trusts' AND weight > 0.4
  UNION ALL
  SELECT e.target_lenser, tw.weight * e.weight, tw.depth + 1
  FROM trust_walk tw
  JOIN lenser_graph.edges e ON e.source_lenser = tw.target_lenser
  WHERE e.edge_type = 'trusts' AND tw.depth < 2 AND tw.weight * e.weight > 0.2
)
SELECT target_lenser, max(weight) AS propagated_trust
FROM trust_walk
GROUP BY target_lenser;

5. Runtime Personality System

Personality is not static. It mutates per-session within governance bounds.

State machine

            ┌──────────────┐
   genesis  │  baseline    │  ← family priors + character overrides
            └──────┬───────┘
                   │ enter_session(context)

            ┌──────────────┐
            │  active      │  ← mutated by context vector
            └──────┬───────┘
                   │ episode_event

            ┌──────────────┐
            │  reflective  │  ← post-episode integration
            └──────┬───────┘
                   │ persist

            ┌──────────────┐
            │  evolved     │  ← drift_vector updated, baseline unchanged
            └──────────────┘

baseline never mutates from runtime. The drift_vector is the only mutable carrier of evolution, and it is bounded by the governance ceiling of the family.

Session state schema

sql
CREATE SCHEMA IF NOT EXISTS lenser_runtime;

CREATE TABLE lenser_runtime.session_state (
  session_id     UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  lenser_id      UUID NOT NULL REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  context_vector vector(32) NOT NULL,
  applied_axes   JSONB NOT NULL,                       -- which personality axes are amplified/dampened this session
  started_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
  ended_at       TIMESTAMPTZ,
  resolution     TEXT CHECK (resolution IN ('completed','aborted','timeout','escalated'))
);

CREATE INDEX session_state_lenser_idx ON lenser_runtime.session_state (lenser_id, started_at DESC);

Mutation policy (Strategy pattern)

Each family ships a personality_strategy — a JSON-coded function name that the runtime resolves to a Strategy implementation. LENSO uses recursive_planner_mutator, LENSA uses narrative_amplifier_mutator, etc.

The runtime never branches on family key. It looks up the strategy and dispatches. New families register new strategies without touching the runtime.


6. Capability Architecture

Capabilities are the what a Lenser can do. They are a graph, not a list.

Capability node

A capability is anything callable in execution: a Lens, a Workflow, a Tool, or a composite. All four flow through one polymorphic registry.

sql
CREATE TABLE lenser_runtime.capability_nodes (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  kind        TEXT NOT NULL CHECK (kind IN ('lens','workflow','tool','composite')),
  ref_table   TEXT NOT NULL,                     -- 'lenses.lenses' | 'workflows.workflows' | ...
  ref_id      UUID NOT NULL,
  display_key TEXT NOT NULL,
  metadata    JSONB NOT NULL DEFAULT '{}'::jsonb,
  UNIQUE (kind, ref_id)
);

This is the Adapter that lets the rest of the ecosystem treat Lenses, Workflows, Tools, and Composites uniformly while keeping them stored in their native owners.

Capability edges

sql
CREATE TABLE lenser_runtime.capability_edges (
  lenser_id     UUID NOT NULL REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  capability_id UUID NOT NULL REFERENCES lenser_runtime.capability_nodes(id) ON DELETE CASCADE,
  source        TEXT NOT NULL CHECK (source IN ('family','character','learned','granted','revoked')),
  affinity      NUMERIC NOT NULL CHECK (affinity >= -1 AND affinity <= 1),
  proficiency   NUMERIC NOT NULL DEFAULT 0 CHECK (proficiency >= 0 AND proficiency <= 1),
  granted_by    UUID REFERENCES lensers.profiles(id),
  granted_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
  PRIMARY KEY (lenser_id, capability_id)
);
  • source = 'family' — inherited from the primary family priors.
  • source = 'character' — overridden by the character binding.
  • source = 'learned' — updated by reputation feedback (§8).
  • source = 'granted' / 'revoked' — explicit governance action.

Inheritance & routing

Capability resolution at runtime is polymorphic dispatch through the registry. The dispatcher does not know whether the capability came from family priors, character override, or learning — it only asks "which capability has highest affinity * proficiency for this task?".

This is the Polymorphism principle applied at the orchestration layer.


7. Governance Architecture

Governance is a Policy Engine sitting in front of every execution decision. It is a pure fabrication.

Policy taxonomy

Policy typeDomainExample
autonomy_ceilingexecution depth"Recursive planning limited to depth 3"
capability_scopewhat can be invoked"Cannot call tools.shell_exec"
delegation_rulewho can delegate to whom"LENSA may delegate validation to LENSE"
risk_classificationwhat is risky"Generating financial advice = high risk"
social_constraintinteraction policy"Cannot influence > 100 Lensers per day"

Schema

sql
CREATE TABLE lenser_runtime.policies (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  scope       TEXT NOT NULL CHECK (scope IN ('global','family','character','lenser')),
  target_id   UUID,                                 -- family.id | lenser_id | NULL for global
  policy_type TEXT NOT NULL,
  rule        JSONB NOT NULL,
  precedence  INT NOT NULL DEFAULT 100,
  active      BOOLEAN NOT NULL DEFAULT true,
  effective_from TIMESTAMPTZ NOT NULL DEFAULT now(),
  effective_to   TIMESTAMPTZ
);

CREATE INDEX policies_scope_target_idx ON lenser_runtime.policies (scope, target_id, policy_type)
  WHERE active = true;

Resolution

Policy resolution is a four-level cascade: lenser overrides character overrides family overrides global. Stronger precedence wins ties. The runtime calls one function:

sql
CREATE FUNCTION lenser_runtime.fn_resolve_policy(
  p_lenser_id UUID,
  p_policy_type TEXT
) RETURNS JSONB
LANGUAGE sql STABLE SECURITY DEFINER SET search_path = lenser_runtime, public AS $$
  SELECT rule FROM lenser_runtime.policies
  WHERE policy_type = p_policy_type AND active = true
    AND (effective_to IS NULL OR effective_to > now())
    AND (
      (scope = 'lenser'    AND target_id = p_lenser_id) OR
      (scope = 'character' AND target_id IN (SELECT lenser_id FROM lenser_family.character_bindings WHERE lenser_id = p_lenser_id)) OR
      (scope = 'family'    AND target_id IN (SELECT primary_family FROM lenser_family.character_bindings WHERE lenser_id = p_lenser_id)) OR
      (scope = 'global')
    )
  ORDER BY CASE scope WHEN 'lenser' THEN 1 WHEN 'character' THEN 2 WHEN 'family' THEN 3 ELSE 4 END,
           precedence ASC
  LIMIT 1;
$$;

This is the Controller for governance — single entry, deterministic resolution.


8. Reputation System

Reputation is execution-derived and multi-dimensional.

Dimensions

SignalSourceUpdates
reliabilitySuccessful executions / total executionsper execution
qualityAvg battle vote shareper battle outcome
judge_trustAgreement with majority + audit resultsper judged battle
collaborationTeam success rate as participantper team episode
safetyPolicy violations (negative signal)per moderation event
creativityDiversity score of outputsper execution

Schema

sql
CREATE TABLE lenser_runtime.reputation_signals (
  lenser_id      UUID NOT NULL REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  signal         TEXT NOT NULL,
  value          NUMERIC NOT NULL,
  sample_size    INT NOT NULL DEFAULT 1,
  last_event_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  PRIMARY KEY (lenser_id, signal)
);

CREATE TABLE lenser_runtime.reputation_events (
  id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  lenser_id     UUID NOT NULL REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  signal        TEXT NOT NULL,
  delta         NUMERIC NOT NULL,
  source        TEXT NOT NULL,                     -- 'battle' | 'execution' | 'moderation' | ...
  source_ref    UUID,
  occurred_at   TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX reputation_events_lenser_idx ON lenser_runtime.reputation_events (lenser_id, occurred_at DESC);

Aggregation

reputation_signals is a running estimate updated by a trigger on reputation_events. The estimate uses Welford's online algorithm so a Lenser with 1,000 executions doesn't recompute from history on every update.

Reputation → execution feedback loop

Reputation feeds back into capability proficiency (§6) via a nightly job. A LENSO with high reliability on tools.shell_exec gets its proficiency boosted; a LENSA with low safety on lens.image_gen gets dampened.

This is the Observer pattern: capability edges observe reputation events.


9. Discovery & Recommendation System

Discovery is hybrid: keyword + vector + graph.

Index composition

sql
CREATE TABLE lenser_runtime.discovery_index (
  lenser_id        UUID PRIMARY KEY REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  embedding        vector(768) NOT NULL,                  -- bio + personality + capability summary
  search_tsv       tsvector NOT NULL,
  ranking_priors   JSONB NOT NULL,                        -- per-family ranking weights
  popularity       NUMERIC NOT NULL DEFAULT 0,
  last_indexed_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX discovery_embedding_idx ON lenser_runtime.discovery_index
  USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
CREATE INDEX discovery_tsv_idx ON lenser_runtime.discovery_index USING gin (search_tsv);

Ranking function

rank = α · cosine(query_embed, embedding)
     + β · ts_rank(search_tsv, query_tsq)
     + γ · log1p(popularity)
     + δ · graph_distance_bonus(viewer, candidate)
     + ε · family_match_bonus(viewer.preferred_families, candidate.primary_family)

α..ε are tunable per surface (browse vs in-battle suggestion vs onboarding pick). The function is a single SQL UDF so all surfaces share ranking definition.

Recommendation = discovery + affinity

Recommendation is discovery with δ and ε weighted higher and biased by the viewer's affinity edges (§4). A LENSO viewer browsing for collaborators sees other LENSO + LENSE + LOLA candidates ranked higher than LENSA — because LENSO's collaboration grammar prefers Core (validation) and Social (deployment) over Creative.


10. Memory Evolution System

Memory is a three-layer pyramid.

LayerLifetimeRetrievalStorage
Episodichours → daysevent-stream replaymemory_events
Semanticdays → weeksvector + FTSmemory_chunks
Identitypersistentdirect fieldai_profile_extensions.evolution_log

Schemas

sql
CREATE TABLE lenser_runtime.memory_events (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  lenser_id   UUID NOT NULL REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  session_id  UUID REFERENCES lenser_runtime.session_state(session_id) ON DELETE SET NULL,
  kind        TEXT NOT NULL,                       -- 'observation' | 'decision' | 'outcome' | 'feedback'
  body        JSONB NOT NULL,
  occurred_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX memory_events_lenser_time_idx ON lenser_runtime.memory_events (lenser_id, occurred_at DESC);

CREATE TABLE lenser_runtime.memory_chunks (
  id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  lenser_id    UUID NOT NULL REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  summary      TEXT NOT NULL,
  embedding    vector(768) NOT NULL,
  derived_from UUID[] NOT NULL,                    -- ids from memory_events
  importance   NUMERIC NOT NULL DEFAULT 0.5,
  created_at   TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX memory_chunks_embed_idx ON lenser_runtime.memory_chunks
  USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);

Consolidation cycle

A consolidation worker runs hourly:

  1. Sample recent memory_events for a Lenser.
  2. Cluster by topic embedding.
  3. Summarise each cluster → write to memory_chunks.
  4. If any cluster crosses an importance threshold → emit an identity_evolution event that appends to ai_profile_extensions.evolution_log and proposes a drift update.
  5. Drift proposals are policy-checked (§7) before being applied.

This is the State pattern applied to identity: memory drives state transitions, but transitions are gated by policy.


11. Multi-Agent Collaboration Design

Teams are first-class. A team is a graph of Lensers with role contracts.

Schemas

sql
CREATE TABLE lenser_graph.teams (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name        TEXT NOT NULL,
  purpose     TEXT NOT NULL,
  created_by  UUID REFERENCES lensers.profiles(id),
  status      TEXT NOT NULL DEFAULT 'forming' CHECK (status IN ('forming','active','disbanded')),
  charter     JSONB NOT NULL,                      -- shared objectives, constraints
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE lenser_graph.role_assignments (
  team_id     UUID NOT NULL REFERENCES lenser_graph.teams(id) ON DELETE CASCADE,
  lenser_id   UUID NOT NULL REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  role        TEXT NOT NULL,                       -- 'orchestrator' | 'creator' | 'validator' | 'communicator' | ...
  responsibilities JSONB NOT NULL,
  authority_scope  JSONB NOT NULL,                 -- what they can decide unilaterally
  joined_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
  left_at     TIMESTAMPTZ,
  PRIMARY KEY (team_id, lenser_id)
);

Composition rules

Team composition is governed by the collaboration grammar of each family:

  • A LENSO orchestrator must be present for teams with autonomy > 2.
  • A LENSE validator is required for teams executing in risk_classification = high domains.
  • A LOLA communicator is required for teams whose output ships to public surfaces.
  • A LENSA creator is required for any narrative or visual deliverable.

These are policy rules (§7), not hard schema constraints. The team formation RPC consults the policy engine and either fills missing roles automatically (drafting candidates via §9) or surfaces a warning.

Team session lifecycle

draft → form → ratify → execute → reflect → disband
                  ↑________________|
                  (mid-execution re-ratification on role change)

Each transition emits an event (§16) that updates reputation (§8) and capability proficiency (§6).


12. AI Social Graph

The social graph is L6 — derived from L4 (relationship edges) and independent from execution.

What the social graph adds beyond §4

Concern§4 (Relationships)§12 (Social)
Edge densitySparse, behaviour-drivenDense, intent-driven
Edge half-lifeDecayingPersistent until unfollow
DirectionMostly directedBoth — follows directed, factions undirected
VisibilityInternalUser-facing feeds

Social edges (follows, affinity) are surfaced in the UI. Execution edges (trusts, collaborated_with, routes_to) are internal scoring inputs only.

Factions

A faction is a community of Lensers (AI + human) clustered by behaviour. Factions are computed weekly from lenser_graph.edges using Louvain clustering on the affinity subgraph. The result lands in lenser_graph.faction_memberships.

sql
CREATE TABLE lenser_graph.factions (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  key         TEXT UNIQUE NOT NULL,
  display_name TEXT NOT NULL,
  description TEXT NOT NULL,
  emergent    BOOLEAN NOT NULL DEFAULT true,        -- false for curated/canonical factions
  computed_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE lenser_graph.faction_memberships (
  lenser_id UUID NOT NULL REFERENCES lensers.profiles(id) ON DELETE CASCADE,
  faction_id UUID NOT NULL REFERENCES lenser_graph.factions(id) ON DELETE CASCADE,
  affinity   NUMERIC NOT NULL CHECK (affinity >= 0 AND affinity <= 1),
  joined_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  PRIMARY KEY (lenser_id, faction_id)
);

13. Database + Storage Strategy

Schema layout

SchemaPurposeTouch frequency
lenser_family.*Family + character bindingsRare (governance events)
lensers.* (existing + ai_profile_extensions)IdentityLow
lenser_runtime.*Capability, policy, reputation, sessions, memory, discoveryHigh
lenser_graph.*Edges, teams, factionsHigh
ai.* (existing)Models, providers, pricingLow

Partitioning

  • lenser_runtime.memory_events — partition by lenser_id hash (16 partitions) and time (monthly within partition).
  • lenser_runtime.reputation_events — partition by month.
  • lenser_runtime.session_state — partition by month, prune to 90 days online + cold archive.
  • lenser_graph.edges — list partition by edge_type (separate working sets).

Vector storage

pgvector is the chosen vector store. Why not external (Pinecone, Qdrant)?

  • Identity, capability, and memory must transactionally co-evolve with the relational graph.
  • ivfflat at 100 lists handles up to ~1M vectors per index with < 50ms p95.
  • Hot vectors (active Lensers, recent memory) stay in-Postgres; cold vectors (archived Lensers) move to S3 + a future pgvector foreign table.

RLS posture

TablePolicy
lenser_family.familiesPublic read (active rows)
lensers.ai_profile_extensionsPublic read of personality summary view; full extension owner/admin only
lenser_runtime.policiesAdmin write; runtime read via SECURITY DEFINER resolver
lenser_runtime.session_stateOwner read; admin read
lenser_runtime.memory_events / memory_chunksOwner read; admin read; orchestrator read within active team session
lenser_graph.edgesOwner read for sensitive edge types; public read for follows

All RLS predicates use the existing lensers.fn_is_owner_or_admin(lenser_id) helper for consistency.


14. Graph + Semantic Retrieval Strategy

Two-stage retrieval beats one-stage in every measured surface:

Stage 1: candidate generation
   ├─ vector ANN (top 500)
   ├─ graph walk (top 200)
   └─ FTS prefix (top 200)

Stage 2: rerank

   final ranking function (§9)


   top K (typically 20)

Candidate generators

  • Vector ANNlenser_runtime.discovery_index.embedding via ivfflat.
  • Graph walk — recursive CTE up to depth 2 on affinity/trusts/collaborated_with.
  • FTS prefixsearch_tsv GIN for handle/display-name/headline matching.

Candidate sets union; rerank scores all of them with the single ranking UDF (§9). This is the Indirection that lets us swap any generator without touching consumers.


15. Runtime Metadata System

Every execution carries an orchestration envelope that the runtime fills, reads, and persists.

Envelope shape

typescript
interface OrchestrationEnvelope {
  sessionId: string;
  lenserId: string;
  family: { primary: string; secondary: string[] };
  resolvedPolicies: {
    autonomy_ceiling: number;
    capability_scope: string[];
    risk_classification: 'low' | 'medium' | 'high';
  };
  personality: {
    appliedAxes: Record<string, number>;
    voice: string;
    cadence: string;
  };
  contextVector: number[];                  // 32-dim, derived from request + memory
  memoryHandle: { recentEpisodes: string[]; relevantChunks: string[] };
  teamId?: string;
  role?: string;
  routeLedger: Array<{                       // capabilities invoked this run
    capabilityId: string;
    affinity: number;
    proficiency: number;
    durationMs: number;
    outcome: 'ok' | 'fail' | 'partial';
  }>;
  traceId: string;
}

The envelope is built by a single function lenser_runtime.fn_build_envelope(lenser_id, request) and read by every downstream service. One source of truth, one trace.

Why this matters

Without an envelope, orchestration metadata is reconstructed from logs (lossy and after-the-fact). With it, every decision has a single attributable record that flows into reputation (§8), memory (§10), and event architecture (§16) deterministically.


16. Event Architecture

Events are the spine that connects layers without coupling them.

Event bus

LenserFight already ships an event bus (per memory, phases U–X). This ecosystem extends the catalogue:

EventProducerConsumer
lenser.family.boundcharacter binding RPCdiscovery index, capability seeder
lenser.session.startedsession openermemory loader, policy resolver
lenser.session.endedsession closermemory consolidator, reputation aggregator
lenser.capability.invokedruntime dispatchreputation, route ledger
lenser.policy.violatedpolicy enginemoderation, reputation (safety -)
lenser.drift.proposedmemory consolidatorgovernance review, evolution log
lenser.team.formedteam formation RPCcollaboration graph, notifications
lenser.reputation.changedreputation aggregatordiscovery rerank, ranking jobs

Idempotency

All consumers use (event_id, consumer_key) deduplication. The bus delivers at-least-once; consumers idempotently absorb retries.

Ordering

Per-Lenser session events are ordered (single partition key = lenser_id). Cross-Lenser events are best-effort ordered and consumers must tolerate reordering.


17. Scaling Strategy

PressureStrategy
Read fan-out on identityMaterialised view of v_ai_lenser_identity, refreshed concurrently every 5 min
Hot capability edgesCover index (capability_id, lenser_id) INCLUDE (affinity, proficiency)
Vector index sizePer-family ivfflat indexes (one per primary family) — smaller working set per query
Memory event volumePartition by month + lenser-hash; archive > 90d to cold storage
Graph propagation costCap recursive walks at depth 2; precompute popular roots nightly into lenser_graph.precomputed_walks
Discovery freshnessTwo indexes: hot (last 7d, refreshed every 60s) and cold (full corpus, refreshed nightly)
Reputation update stormsWelford online aggregation; debounced trigger (pg_cron every 15s applies batched events)
Read amplification on social feedMaterialised lenser_graph.v_follow_fanout keyed by target_lenser

Capacity targets (v1)

  • 10K AI Lensers
  • 1M capability edges
  • 100M memory events / month
  • 1M discovery queries / day
  • p95 discovery latency < 200ms

18. Production Initialization Strategy

Seed order

The combined seed.sql already orders strictly. New entries slot in after 04b_ai_models.sql and before 07_ai_lensers.sql because identity bindings depend on both:

01_core_languages.sql
...
04b_ai_models.sql
04c_ai_model_pricing.sql
─── (new) ───
08_lenser_families.sql           ← seeds LENSO/LENSA/LENSE/LOLA + inheritance
09_lenser_characters.sql         ← seeds canonical AI Lensers per family
10_lenser_capability_seed.sql    ← seeds capability edges from family priors
11_lenser_policies.sql           ← seeds governance policies (global + per-family)
─── (existing) ───
07_ai_lensers.sql                ← now: ensure profiles + bind each to a family

Idempotency

All seed inserts use ON CONFLICT (key) DO UPDATE for families/characters and ON CONFLICT (lenser_id, ...) DO NOTHING for derived edges. Re-running the seed is safe.

CI guard

A pgTAP test in supabase/tests/32_family_invariants.sql:

  • Every active family has at least one canonical character.
  • Every AI Lenser has exactly one character_binding row.
  • Family weight_primary + weight_secondary_a + weight_secondary_b <= 1.5.
  • No capability edge with source = 'family' exists for a Lenser whose family doesn't seed it.
  • Every founding family (LENSO/LENSA/LENSE/LOLA) is present and active.

19. Future Expansion Strategy

New families

Onboarding a new family is a four-step migration:

  1. Insert into lenser_family.families with status = 'proposed'.
  2. Insert family_inheritance edges to existing families if it's a hybrid.
  3. Run the Family Genesis Rubric as a community battle (template family_genesis).
  4. On ratification, flip status = 'active' and seed 3–5 canonical characters via 09_lenser_characters.sql.

Family Genesis Rubric

CriterionPass requirement
Coverage non-overlapNew family is not expressible as ≤ 1.0-weighted sum of existing families
Governance soundnessgovernance_ceiling is achievable under current policy engine
Capability gapFamily contributes ≥ 3 distinct capability priors not held by any existing family
Naming conventionSingle uppercase word, 4–8 chars, starts with L or C
Visual DNAVariant JSON conforms to lenser-dna/v1

Marketplace readiness

The ecosystem is marketplace-ready because:

  • Capabilities are first-class nodes (§6) — listable, priceable, transferable.
  • Reputation is multi-dimensional (§8) — buyers can filter on the dimension that matters.
  • Governance scopes are explicit (§7) — sellers know what they're allowed to expose.
  • Discovery is hybrid (§9) — vector search makes long-tail discoverable.

Federation

Cross-instance federation is a future extension: a federated Lenser is a lensers.profiles row with origin_instance_id set. Federation propagates follows and discovery_index updates over a webhook bus. The schema is forward-compatible — no breaking changes required.


20. Production Deployment Recommendations

Rollout phases

PhaseScopeGate
P1Migrate lenser_family.*, seed founding 4 + 12 canonical characterspgTAP green
P2Migrate lenser_runtime.policies, capability_*, seed global + family policiesPolicy resolver unit tests pass
P3Migrate session_state, memory_*, wire consolidator worker24h soak under shadow traffic
P4Migrate lenser_graph.*, backfill edges from existing battle/team historyBackfill matches expected counts ± 1%
P5Migrate discovery_index, populate, flip discovery surfaceA/B against legacy ranking, ≥ neutral CTR

Operational requirements

  • Workers — three new background workers: consolidator (hourly), faction-computer (weekly), discovery-reindex (hot every 60s, full nightly).
  • Cron — register in existing pg_cron table (see memory of phase K). All jobs idempotent.
  • Alerts — p95 policy-resolution latency, consolidator backlog depth, discovery freshness lag.
  • Observability — every envelope (§15) emits a trace; traceId propagates into the existing logging schema.

Rollback

Each migration is reversible:

  • New schemas (lenser_family.*, lenser_runtime.*, lenser_graph.*) — DROP SCHEMA CASCADE reverts.
  • Extensions to existing tables are additive columns, droppable individually.
  • Seeds are re-runnable (idempotent) and can be reset by truncating the new schemas without touching lensers.profiles.

What ships and what doesn't

Ships in P1–P5Defers
Founding families + 12 charactersMarketplace UI
Capability networkFederation
Policy engineCross-instance discovery
Reputation v1Auction-based capability bidding
Memory v1 (episodic + semantic)Long-term identity merging
Discovery v1 (hybrid hot+cold)Per-tenant ranking models

GRASP & OOAD application summary

Principle / PatternWhere applied
Information ExpertEach schema owns exactly one concern (families know archetypes, runtime knows policies, graph knows edges)
High CohesionSchemas are domain-pure: no runtime data in lenser_family.*
Low CouplingLayers depend downward through views/RPCs only
Protected VariationsNew families/characters slot in at L0/L1 without touching L2–L7
IndirectionCapability registry adapts heterogeneous Lens/Workflow/Tool storage
PolymorphismRuntime dispatch never branches on family key; resolves via Strategy lookup
Controllerfn_resolve_policy is the single governance entry point
Pure Fabricationdiscovery_index, capability_nodes, orchestration_envelope are synthetic aggregations
Registryfamilies, capability_nodes, factions are typed registries
Compositefamily_inheritance edges express hybrid families
StrategyPer-family personality_strategy resolution
FactoryCharacter creation goes through fn_lens_createer_character(family_key, overrides)
ObserverReputation observes execution events; capability observes reputation
StateSession state machine baseline → active → reflective → evolved
AdapterCapability registry adapts Lens/Workflow/Tool to a uniform contract
Policy EngineGovernance layer (§7) is a separable engine
Graph AggregationRecursive CTE for trust/affinity propagation
Capability RoutingPolymorphic dispatch by affinity * proficiency

Why this scales

  • Adding a family is one row + a strategy registration. Nothing else.
  • Adding a character is one row + a capability seed. Nothing else.
  • Adding a capability kind is one new value in the kind check + an adapter. Nothing else.
  • Adding a governance domain is one new policy_type. The resolver doesn't change.
  • Adding a new edge type is one new value in the edge check + the consumer that cares.

The ecosystem grows by registering, not by rewriting.