Maat — Portfolio Intelligence and Business Management Platform
This document specifies what is implemented in the Maat domain. It is
grounded in the source under libs/maat/*, apps/maat/*, and
libs/contracts/maat/. Every entity, field, enum value, event, endpoint,
permission, and count below was read from source:
libs/maat/core/src/ {organization,event-bus,redis-namespace}.ts,
libs/maat/*/src/index.ts, libs/maat/finance/src/v2-persistence-ledger.ts,
libs/maat/strategy/src/v2-fighting-game-live-ops-calendar.ts,
libs/maat/dashboard/src/v2-balance-dashboard.ts,
libs/maat/negotiation-intelligence/src/procurement-program.ts,
libs/maat/knowledge/src/knowledge-graph-neo4j-schema.ts,
libs/maat/sdk/src/typed-client.ts,
apps/maat/api-gateway/src/{app,openapi/spec,middleware/api-versioning, realtime/websocket-server}.ts,
and the apps/maat/* route trees. Where a capability is present only as a
scaffold or is named in a phase backlog, it is labelled accordingly.
1. Domain Object Model (@maat/core)#
The foundational types live in libs/maat/core/src/organization.ts. This single
file defines the entire shared domain vocabulary for the Maat platform: branded
ID types that prevent accidental cross-type assignment, frozen enum value
arrays, Zod schemas for every entity, and a complete set of constructor,
serializer, and type guard functions. There is no ORM — all entities are
in-memory value objects that can be validated, constructed, and serialized
without a database.
For each entity, organization.ts follows a consistent pattern:
- A
*Draftinterface accepts loosely typed inputs (strings, partial objects). create<Entity>(draft)validates and normalises the draft, throwing on invalid input.create<Entity>WithDefaults(overrides?)deep-merges aDeepPartialover a built-in default object, making test setup concise.serialize<Entity>(value)/deserialize<Entity>(serialized)provide JSON round-trip helpers.is<Entity>(value)is anunknown-narrowing type guard.
1.1 Branded Identifier Types#
To prevent accidental cross-type mixing at compile time (passing an AgentId
where an OrganizationId is expected), every entity identifier is a branded
string type. Each type enforces a known prefix pattern and has a dedicated
constructor function. The table below lists the 13 entity ID types and their
regex patterns, all case-insensitive.
organization.ts defines a Brand<T, B> helper and these branded string IDs,
each with a regex pattern and a create*Id constructor:
| Type | Pattern (case-insensitive) |
|---|---|
OrganizationId |
org_[a-z0-9][a-z0-9_-]{2,62} |
OperatingCompanyId |
opco_[a-z0-9][a-z0-9_-]{2,62} |
BusinessUnitId |
bu_[a-z0-9][a-z0-9_-]{2,62} |
MarketId |
market_[a-z0-9][a-z0-9_-]{2,62} |
CompetitorId |
competitor_[a-z0-9][a-z0-9_-]{2,62} |
ProjectId |
project_[a-z0-9][a-z0-9_-]{2,62} |
KPIId |
kpi_[a-z0-9][a-z0-9_-]{2,62} |
RiskId |
risk_[a-z0-9][a-z0-9_-]{2,62} |
AgentId |
agent_[a-z0-9][a-z0-9_-]{2,62} |
ScenarioId |
scenario_[a-z0-9][a-z0-9_-]{2,62} |
DashboardId |
dashboard_[a-z0-9][a-z0-9_-]{2,62} |
AlertId |
alert_[a-z0-9][a-z0-9_-]{2,62} |
AuditEventId |
audit_[a-z0-9][a-z0-9_-]{2,62} |
ISODateString (YYYY-MM-DD) and ISODateTimeString (UTC ISO-8601) are also
branded; isISODateString, createISODateString, isISODateTimeString, and
createISODateTimeString validate and construct them.
1.2 Enums and Literal-Union Value Arrays#
Rather than TypeScript enum declarations, every enumerated set is expressed as
a frozen *_VALUES as const array with a derived union type. This pattern
makes the full value set available at runtime (for validation, UI dropdowns, and
API responses) while also providing compile-time type narrowing.
The following table lists all value arrays and their members.
| Value array | Members |
|---|---|
LEGAL_ENTITY_TYPE_VALUES |
HOLDING_COMPANY, CONGLOMERATE, PRIVATE_LIMITED, PUBLIC_LIMITED, PARTNERSHIP, COOPERATIVE, NON_PROFIT, STATE_OWNED |
LEGAL_STRUCTURE_MODEL_VALUES |
SINGLE_ENTITY, HOLDING_WITH_SUBSIDIARIES, MULTI_HOLDING, FEDERATED |
OWNERSHIP_MODEL_VALUES |
PRIVATELY_HELD, PUBLICLY_TRADED, STATE_OWNED, MIXED |
OPERATING_COMPANY_INDUSTRY_SECTOR_VALUES |
AGRICULTURE, MANUFACTURING, ENERGY, LOGISTICS, FINANCIAL_SERVICES, TECHNOLOGY, HEALTHCARE, CONSUMER_GOODS, REAL_ESTATE, PROFESSIONAL_SERVICES |
OPERATING_COMPANY_BOARD_ROLE_VALUES |
CHAIRPERSON, EXECUTIVE_DIRECTOR, NON_EXECUTIVE_DIRECTOR, INDEPENDENT_DIRECTOR |
MARKET_GEOGRAPHIC_REGION_VALUES |
GHANA, WEST_AFRICA, PAN_AFRICAN, GLOBAL |
COMPETITOR_THREAT_LEVEL_VALUES |
LOW, MEDIUM, HIGH, CRITICAL |
PROJECT_STATUS_VALUES |
PROPOSED, APPROVED, IN_PROGRESS, COMPLETED, CANCELLED |
KPI_METRIC_TYPE_VALUES |
FINANCIAL, OPERATIONAL, STRATEGIC, ESG |
KPI_MEASUREMENT_FREQUENCY_VALUES |
REAL_TIME, DAILY, WEEKLY, MONTHLY, QUARTERLY, ANNUALLY |
KPI_TREND_DIRECTION_VALUES |
INCREASING, DECREASING, STABLE |
KPI_THRESHOLD_COMPARATOR_VALUES |
GT, GTE, LT, LTE |
KPI_THRESHOLD_SEVERITY_VALUES |
INFO, WARNING, CRITICAL |
REGULATION_JURISDICTION_VALUES |
GHANA, ECOWAS, AFCFTA, INTERNATIONAL |
RISK_CATEGORY_VALUES |
MARKET, CREDIT, OPERATIONAL, REGULATORY, POLITICAL, ENVIRONMENTAL, SUPPLY_CHAIN |
RISK_REVIEW_CADENCE_VALUES |
WEEKLY, MONTHLY, QUARTERLY, SEMI_ANNUALLY, ANNUALLY |
AGENT_CLASS_VALUES |
STRATEGY, ENGINEERING, FINANCE, OPERATIONS, RESEARCH, COMPLIANCE |
AGENT_TASK_PRIORITY_VALUES |
LOW, MEDIUM, HIGH, URGENT |
AGENT_TASK_STATUS_VALUES |
QUEUED, IN_PROGRESS, BLOCKED |
SCENARIO_CATEGORY_VALUES |
BASE_CASE, BEST_CASE, WORST_CASE, CUSTOM |
DASHBOARD_OWNER_TYPE_VALUES |
USER, AGENT |
DASHBOARD_LAYOUT_TYPE_VALUES |
GRID, FREEFORM |
DASHBOARD_DATA_SOURCE_MODE_VALUES |
PULL, PUSH |
DASHBOARD_PERMISSION_SUBJECT_TYPE_VALUES |
USER, ROLE, AGENT |
DASHBOARD_PERMISSION_LEVEL_VALUES |
VIEWER, EDITOR, OWNER |
ALERT_SEVERITY_VALUES |
INFO, WARNING, CRITICAL, EMERGENCY |
ALERT_ACTION_PRIORITY_VALUES |
LOW, MEDIUM, HIGH, URGENT |
ALERT_ESCALATION_CHANNEL_VALUES |
EMAIL, SMS, SLACK, PAGER |
ALERT_ACKNOWLEDGMENT_STATUS_VALUES |
UNACKNOWLEDGED, ACKNOWLEDGED |
AUDIT_EVENT_TYPE_VALUES |
CREATE, UPDATE, DELETE, APPROVE, REJECT, ASSIGN, ESCALATE, ACKNOWLEDGE, RESOLVE |
AUDIT_EVENT_ACTOR_TYPE_VALUES |
USER, AGENT |
AUDIT_COMPLIANCE_CLASSIFICATION_VALUES |
PUBLIC, INTERNAL, CONFIDENTIAL, RESTRICTED |
SYNERGY_CATEGORY_VALUES |
REVENUE, COST, KNOWLEDGE, OPERATIONAL |
1.3 Core Entities#
The 15 root entities are Organization, OperatingCompany, BusinessUnit,
Market, Competitor, Project, KPI, Regulation, Risk, Agent,
Scenario, Dashboard, Alert, AuditEvent, and Synergy. Each has a
matching *Draft interface (loosely typed inputs accepted by the create*
constructor) and an exported Zod schema. The detailed field specifications
follow.
Organization#
The top-level entity representing the holding group that owns the portfolio companies.
| Field | Type | Meaning |
|---|---|---|
id |
OrganizationId |
Branded organization identifier |
name |
string |
Non-empty trimmed name |
legalEntityStructure |
LegalEntityStructure |
Entity type, structure model, ownership model, and registrations (≥ 1, exactly one isPrimary) |
incorporationJurisdiction |
IncorporationJurisdiction |
countryCode, optional stateOrRegion/city, legalRegime, regulator |
foundingDate |
ISODateString |
Incorporation date |
missionStatement |
string |
Non-empty mission text |
operatingCompanies |
OrganizationOperatingCompany[] |
≥ 1 references: companyId, name, strategicRole, ownershipPercent |
consolidatedFinancialSummary |
ConsolidatedFinancialSummary |
currencyCode, fiscalYear, revenue, grossProfit, ebitda, netIncome, totalAssets, totalLiabilities, shareholdersEquity, operatingCashFlow, freeCashFlow, debtToEquityRatio |
OperatingCompany#
Represents one of the seven portfolio companies. Captures Ghana-specific regulatory identifiers (GRA TIN, SSNIT employer number) alongside standard company metadata, reflecting the platform's Ghana-first compliance posture.
| Field | Type | Meaning |
|---|---|---|
id |
OperatingCompanyId |
Branded company identifier |
name |
string |
Company name |
industrySector |
OperatingCompanyIndustrySector |
Sector classification |
isicClassificationCode |
string |
ISIC code (section letter + 2–4 digits) |
ghanaRegistrationNumber |
string |
Registrar of Companies number (prefix C/CS/BN/CG/RGD + 3–12 digits) |
graTaxIdentificationNumber |
string |
GRA TIN (leading letter + 8–12 digits) |
ssnitEmployerNumber |
string |
SSNIT employer number (5–12 digits) |
headquartersLocation |
OperatingCompanyHeadquartersLocation |
countryCode, optional region, city, optional address/postal code |
subsidiaries |
OperatingCompanySubsidiaryReference[] |
subsidiaryCompanyId, name, ownershipPercent |
ceo |
OperatingCompanyExecutiveReference |
executiveId, fullName, title, appointedAt |
boardMembers |
OperatingCompanyBoardMember[] |
memberId, fullName, role, appointedAt, optional termEndAt |
annualRevenueRange |
OperatingCompanyAnnualRevenueRange |
currencyCode, fiscalYear, minimum, maximum |
BusinessUnit#
A division within an operating company, with its own product lines, factory locations, P&L summary, and workforce count.
Fields: id, parentCompanyId, name, graTaxIdentificationNumber,
ssnitEmployerNumber, productLines[] (name, category,
annualOutputUnits), factoryLocations[] (siteCode, countryCode, region,
city, primaryProducts[]), employeeCount, capacityUtilizationPercent, and
profitAndLossSummary (currencyCode, fiscalYear, revenue,
costOfGoodsSold, grossProfit, operatingExpenses, operatingProfit,
netProfit).
Market#
Describes a market segment that portfolio companies operate in or are evaluating for entry, including size estimates (TAM/SAM/SOM) and a regulatory complexity index.
Fields: id, name, geographicRegion, sectorClassification,
sizeEstimates (currencyCode, referenceYear, tam, sam, som),
annualGrowthRatePercent, competitiveIntensityScore,
regulatoryComplexityIndex.
Competitor#
Tracks a named competitor with its market share, strengths, weaknesses, a timestamped timeline of recent moves, a threat level, and an intelligence freshness timestamp so stale competitive data is always visible.
Fields: id, name, marketSharePercent, strengths[], weaknesses[],
recentMovesTimeline[] (happenedAt, headline, summary), threatLevel,
intelligenceFreshnessTimestamp.
Project#
A tracked initiative owned by a business unit, with budget allocation, actual spend, milestones, assigned agents, and a reference to the risk register.
Fields: id, owningBusinessUnitId, name, description, status,
budgetAllocation (currencyCode, amount), actualSpend,
timelineMilestones[] (milestoneId, name, targetDate, optional
completionDate), assignedAgents[] (agentId, role, allocationPercent),
riskRegisterReference.
KPI#
A Key Performance Indicator with a target value, actual value, measurement frequency, trend direction, and a set of threshold alerts that fire when the metric crosses configured boundaries.
Fields: id, name, description, metricType, targetValue (value,
unit), actualValue, measurementFrequency, trendDirection,
thresholdAlerts[] (alertId, thresholdValue, comparator, severity,
message), responsibleBusinessUnitId.
Regulation#
A regulatory requirement from a specific body, in a specific jurisdiction, with
compliance requirements, associated penalties, and a list of affected business
units. Note that Regulation has no branded ID — it is identified by
jurisdictionCode + regulationIdentifier.
Fields: jurisdiction, regulatoryBodyName, regulationIdentifier,
effectiveDate, complianceRequirements[] (requirementId, description,
isMandatory, optional dueDate), penaltyStructure (optional currencyCode,
optional min/max monetary penalty, nonMonetaryPenalties[]),
affectedBusinessUnitIds[].
Risk#
A risk entry in a risk register, with a composite risk score derived from likelihood and impact scores, mitigation strategies, an owner reference, and a review schedule with a next-review date.
Fields: id, category, likelihoodScore, impactScore,
compositeRiskScore, mitigationStrategies[], ownerReference (ownerId,
ownerName, ownerRole), reviewSchedule (cadence, nextReviewDate,
optional lastReviewedDate).
Agent#
An AI agent registration record. Each agent has a class (strategy, engineering,
finance, operations, research, or compliance), a capability list validated
against the AGENT_CAPABILITY_MATRIX, assigned tools, a memory context window
size, a performance score, and a current task queue.
Fields: id, agentClass, name, capabilities[], assignedTools[],
memoryContextWindow, performanceScore, currentTaskQueue[]
(AgentTaskQueueItem: taskId, title, priority, status). The constructor
checks each capability against AGENT_CAPABILITY_MATRIX, a per-AgentClass map
of allowed capability strings.
Scenario#
A named simulation scenario with probabilistic weighting, outcome projections (a record of metric names to numeric values), and sensitivity parameters that bound the key assumptions.
Fields: id, name, description, scenarioCategory, assumptions[],
timeHorizonMonths, probabilityWeight, simulatedOutcomes (record of
string → number), sensitivityParameters[] (parameterName, baseValue,
minimumValue, maximumValue).
Dashboard#
A configurable dashboard entity owned by a user or agent, with a grid or freeform layout, widget definitions with pixel positions, data-source bindings, refresh interval, and per-subject access permissions.
Fields: id, ownerReference (ownerId, ownerType, displayName),
layoutConfiguration (layoutType, columns, rowHeight),
widgetDefinitions[] (widgetId, widgetType, title, position
{x,y,width,height}), dataSourceBindings[] (bindingId, widgetId,
sourceKey, mode), refreshIntervalSeconds, accessPermissions[]
(subjectType, subjectId, permissionLevel), lastViewedTimestamp.
Alert#
A system alert representing a condition requiring attention, with an escalation chain that automatically escalates to successively higher-level targets after configurable timeout windows.
Fields: id, severity, category, title, description, sourceSystem,
affectedEntities[] (entityType, entityId), recommendedActions[]
(actionId, description, priority, optional ownerId),
acknowledgmentStatus, escalationChain[] (level, target, channel,
triggerAfterMinutes).
AuditEvent#
An immutable audit record capturing who did what to which entity, with before/after state snapshots, a compliance classification, and a correlation ID for distributed tracing.
Fields: id, eventType, actor (actorType, actorId, optional
actorDisplayName), targetEntity (entityType, entityId, optional
entityName), actionPerformed, previousStateSnapshot, newStateSnapshot,
timestamp, correlationId, complianceClassification.
Synergy#
A quantified inter-company synergy opportunity, linking a source business unit
to a target business unit with an estimated annual value, a realization
timeline, a probability score, and prerequisite dependencies. Note that
Synergy has no branded ID.
Fields: sourceBusinessUnitId, targetBusinessUnitId, category,
estimatedAnnualValue (currencyCode, amount, referenceFiscalYear),
realizationTimeline (plannedStartDate, targetRealizationDate),
probabilityScore, dependencyPrerequisites[] (code, description).
1.4 Constructors, Serialization, and Type Guards#
For each of the 15 entities, organization.ts exports the following — all must
be present when adding a new entity:
create<Entity>(draft)— validates and normalises a*Draft, throwing on invalid input;create<Entity>WithDefaults(overrides?)— deep-merges aDeepPartialover a built-in default object;serialize<Entity>(value)/deserialize<Entity>(serialized)— JSON round-trip helpers;is<Entity>(value)—unknown-narrowing type guard.
The Ghana-identifier schemas GhanaCompanyRegistrationNumberSchema,
GRATaxIdentificationNumberSchema, and SSNITEmployerNumberSchema are exported
for reuse by any library that needs to validate these identifiers independently.
2. Domain Event System (@maat/core)#
Defined in libs/maat/core/src/event-bus.ts. There is no Redis-backed bus;
the implementation is a typed in-process bus. Events are the mechanism by which
domain activities (market signals, agent completions, strategy decisions,
compliance alerts, simulation progress) notify other parts of the system without
creating direct library dependencies between them.
2.1 Event Types#
All domain events conform to exactly five types, each with its own Zod payload
schema. MaatDomainEventTypeSchema is a Zod enum of these five values:
| Event type | Payload schema |
|---|---|
MarketIntelligenceEvent |
MarketIntelligenceEventPayloadSchema |
AgentTaskEvent |
AgentTaskEventPayloadSchema |
StrategyDecisionEvent |
StrategyDecisionEventPayloadSchema |
ComplianceAlertEvent |
ComplianceAlertEventPayloadSchema |
SimulationStateEvent |
SimulationStateEventPayloadSchema |
2.2 Base Envelope#
Every event extends a shared base envelope, ensuring consistent tracing metadata
regardless of event type. BaseMaatEventSchema requires: eventId (UUID),
eventType, schemaVersion (literal 1), organizationId, source,
correlationId, optional causationId, optional traceId, and occurredAt
(ISO-8601 with offset). MaatDomainEventSchema is a discriminated union on
eventType.
2.3 Event Payloads#
The five event payload schemas carry the following domain-specific fields. Note the bounded numeric fields (confidence, impact scores) and the discriminated status enums — these enforce domain invariants at the schema level.
| Event | Payload fields |
|---|---|
MarketIntelligenceEvent |
marketId, signalType (PRICE_MOVEMENT / DEMAND_SHIFT / SUPPLY_DISRUPTION / COMPETITOR_ACTIVITY / REGULATORY_CHANGE), severity (INFO/WARNING/CRITICAL), confidence (0–1), summary, affectedSectors[], metrics (record of string → number) |
AgentTaskEvent |
taskId, agentId, taskType (INTELLIGENCE_SCAN / STRATEGY_SIMULATION / FINANCIAL_ANALYSIS / SUPPLY_CHAIN_OPTIMIZATION / COMPLIANCE_REVIEW), status (QUEUED/ASSIGNED/IN_PROGRESS/BLOCKED/COMPLETED/FAILED), priority (LOW/MEDIUM/HIGH/URGENT), optional dueAt, optional details |
StrategyDecisionEvent |
decisionId, strategyId, decisionType (INVESTMENT / DIVESTMENT / EXPANSION / PARTNERSHIP / RESTRUCTURE / RISK_MITIGATION), rationale, confidence (0–1), expectedImpactScore (−100…100), approvedBy[] (≥ 1) |
ComplianceAlertEvent |
alertId, jurisdiction (GHANA/ECOWAS/AFCFTA/INTERNATIONAL), regulationCode, severity (WARNING/CRITICAL/EMERGENCY), title, description, optional dueAt, impactedBusinessUnits[] |
SimulationStateEvent |
simulationId, scenarioId, runId, state (INITIALIZING/RUNNING/PAUSED/COMPLETED/FAILED), progressPercent (0–100), horizonMonths (positive int), summary |
2.4 Bus API#
The InMemoryMaatEventBus class and its supporting utilities form the complete
bus API:
MAAT_EVENT_SCHEMAS— record mapping each event type to its Zod schema.parseMaatDomainEvent(event)— validates an unknown payload, routing byeventTypeto the matching schema.createMaatEvent(draft)— fillseventId(randomUUID()),schemaVersion(1), andoccurredAt(new Date().toISOString()) defaults, then validates.InMemoryMaatEventBus— class withpublish(input)(parses then dispatches),subscribe(eventType, handler),subscribeAll(handler),getSubscriberCount(eventType?), andclearSubscriptions(). Eachsubscribe*call returns an unsubscribe function. Handlers run viaPromise.allSettled; if any reject, dispatch throws anAggregateError.
3. Redis Namespace Builder (@maat/core)#
libs/maat/core/src/redis-namespace.ts provides a pure key-string builder; it
does not open a Redis connection. Its purpose is to centralize the key-prefix
convention so that all services are guaranteed to use consistent, non-colliding
namespace strings when a Redis integration is eventually wired in.
createMaatRedisNamespacePrefixes(options?) accepts basePrefix (default
maat), environment, and separator (default :), and returns
MaatRedisNamespacePrefixes. When an environment is provided, the root prefix
becomes maat:<environment> — keeping dev, staging, and production namespaces
separate on a shared Redis instance.
| Prefix field | Default value |
|---|---|
root |
maat (or maat:<environment> when an environment is given) |
agentState |
maat:agent:state |
intelligenceCache |
maat:intelligence:cache |
strategyResults |
maat:strategy:results |
dashboardState |
maat:dashboard:state |
rateLimiting |
maat:rate:limit |
createMaatRedisKeyBuilder(prefixes?) returns a MaatRedisKeyBuilder with
agentState, intelligenceCache, strategyResult, dashboardState, and
rateLimit key-composing functions. assertUniqueNamespacePrefixes(prefixes)
throws if any two prefixes collide. MAAT_REDIS_NAMESPACES and
MAAT_REDIS_KEYS are the default-configured singletons.
4. Library Surface#
libs/maat/ contains 19 libraries, all published as @maat/<name>. Each
library's src/index.ts exports a <NAME>_LIBRARY package-name constant plus
the per-feature modules below. The implementation modules are pure TypeScript;
no library imports a database driver (pg, drizzle, neo4j-driver,
redis/ioredis). The only external runtime SDK is @anthropic-ai/sdk,
imported by @maat/intelligence in world-model-simulator.ts and
report-generation-pipeline.ts.
The table below shows status and module counts as read from each library's
src/index.ts. A "scaffold" status means the library exists with a
project.json and package.json but its source files contain minimal stubs — it
is a placeholder for planned work, not a reduced-size implementation.
| Library | Status | Module count (index.ts) |
|---|---|---|
@maat/core |
implemented | 3 modules (event-bus, organization, redis-namespace) |
@maat/agents |
implemented | 28 modules |
@maat/capital |
implemented | 15 modules |
@maat/compliance |
implemented | 20 modules |
@maat/digital-twin |
implemented | 20 modules |
@maat/finance |
implemented | 21 modules |
@maat/integrations |
implemented | 7 modules (one per portfolio company) |
@maat/intelligence |
implemented | 30 modules |
@maat/knowledge |
implemented | 21 modules |
@maat/projects |
implemented | 15 modules |
@maat/reporting |
implemented | 15 modules |
@maat/risk |
implemented | 15 modules |
@maat/sdk |
implemented | 10 modules |
@maat/strategy |
implemented | 21 modules |
@maat/supply-chain |
implemented | 20 modules |
@maat/workforce |
implemented | 15 modules |
@maat/dashboard |
scaffold | 1 module — only the V2 balance-dashboard contract (§7) |
@maat/negotiation-intelligence |
scaffold | 1 module — procurement-program (§4.1) |
Note:
@maat/dashboardis not a general dashboard aggregation layer. Its only source file beyond the V2 contract is a.gitkeep. Dashboard entity types (Dashboard, widgets, layouts, permissions) live in@maat/core.
4.1 @maat/negotiation-intelligence — Procurement Program (Phase 179.7.1)#
Defined in negotiation-intelligence/src/procurement-program.ts. This module
seeds the Concordia shared bargaining substrate with Maat's procurement-program
configuration: the tracks Maat negotiates under, the levers it uses, and the
approval thresholds that gate high-value decisions. The agent mediation
behaviour itself is still planned for a later phase.
It exports:
ProcurementTrackSchema— enum of procurement tracks.ProcurementLeverSchema— enum of negotiation levers.ApprovalThresholdSchema— object with an approval threshold definition.ProcurementProgramSchema— the full program object.findApprovalThreshold(...)— selects the threshold applicable to a value.toMaatExtensionDefaults(program)— projects a program into Concordia extension defaults.
5. API Gateway (apps/maat/api-gateway)#
apps/maat/api-gateway is a Hono application (not Fastify). It is a
JWT-authenticated reverse-dispatch gateway that sits in front of per-domain
upstream services, providing a single authenticated entry point for all Maat API
consumers. Rather than each downstream service managing its own auth and
organization access enforcement, the gateway handles both centrally. Source:
apps/maat/api-gateway/src/app.ts.
5.1 Authentication and Authorization#
createAuthMiddlewareverifies aBearerJWT withHS256usingMAAT_JWT_SECRET(falls back to a development secret).MaatJwtClaimsSchemarequiressub,orgId,roles[],permissions[], and optionalexp/iat.MaatPermissionSchemaenumerates 23 permission strings:gateway:adminplusread/writepairs forintelligence,strategy,agents,finance,supply-chain,digital-twin,compliance,knowledge,dashboard,simulation, andworker.requirePermission(permission)allows the request if the caller holds the permission or theplatform-adminrole.ensureOrganizationAccessrejects cross-org access unless the caller is aplatform-admin.
5.2 Routes#
The gateway exposes a set of infrastructure routes directly, plus domain-service proxy routes for each of the 11 registered services. The infrastructure routes are:
| Method | Path | Description |
|---|---|---|
GET |
/health |
Gateway liveness |
GET |
/ready |
Readiness; reports routesRegistered count |
GET |
/api/v1/services |
List registered domain services (gateway:admin) |
GET |
/api/v1/audit/requests |
Query the request audit trail (compliance:read) |
GET |
/api/v1/realtime/rooms |
WebSocket room snapshot (gateway:admin) |
POST |
/api/v1/realtime/dashboard/update |
Broadcast a dashboard update (dashboard:write) |
POST |
/api/v1/realtime/agents/status |
Broadcast agent status (agents:write) |
POST |
/api/v1/realtime/market/alert |
Broadcast a market alert (intelligence:write) |
POST |
/api/v1/realtime/simulation/progress |
Broadcast simulation progress (simulation:write) |
GET |
/api/docs |
Swagger UI |
GET |
/api/docs/openapi.json |
Generated OpenAPI 3.1 spec |
Each of the 11 domain services additionally mounts a router under its base path
(/api/v1/intelligence, /api/v1/strategy, /api/v1/agents,
/api/v1/finance, /api/v1/supply-chain, /api/v1/digital-twin,
/api/v1/compliance, /api/v1/knowledge, /api/v1/dashboard,
/api/v1/simulation, /api/v1/worker) exposing:
GET /health— service registration status (requires the read permission);POST /dispatch— forwards anorganizationId/path/method/query/bodyrequest to the upstream service URL (requires the write permission). Upstream URLs come fromMAAT_<SERVICE>_SERVICE_URLenv vars, defaulting tohttp://maat-<service>:3000.
5.3 Middleware#
apps/maat/api-gateway/src/middleware/ provides three middleware modules:
api-versioning.ts—MAAT_API_VERSIONS(currently onlyv1,CURRENT_API_VERSION = 'v1'); attachesX-Api-Version*headers, emitsDeprecation/Sunsetheaders for deprecated versions, and rejects unknown versions whenenforceKnownVersionsis set.rate-limit.ts—createMaatRateLimitMiddlewarefor/api/v1/*.request-response-logging.ts— logging middleware plusInMemoryMaatRequestAuditTrail, an in-memory audit store with a filterablelist({ limit, organizationId, outcome }).
5.4 Realtime WebSocket Server#
apps/maat/api-gateway/src/realtime/websocket-server.ts defines
MaatRealtimeWebSocketServer. This server handles live data delivery for the
five Next.js web apps — pushing dashboard updates, agent status changes, market
alerts, and simulation progress without requiring each app to poll.
RealtimeChannelSchema enumerates four channels: dashboard, agents,
market-alerts, simulation. Clients send
subscribe/unsubscribe/heartbeat commands; rooms are keyed
channel:org[:entity]. Org-scoped access is enforced per room. Broadcast
methods: broadcastDashboardUpdate, broadcastAgentStatus,
broadcastMarketAlert, broadcastSimulationProgress.
6. SDK (@maat/sdk)#
libs/maat/sdk/src/typed-client.ts provides MaatClient, a typed client whose
transport is a pluggable HttpAdapter. The SDK is designed to work in tests and
integration contexts without a live server: when no adapter is supplied the
client falls back to MockHttpAdapter, giving consumers type-safe
request/response shapes even before the real backend is wired.
MaatClientConfig—baseUrl,apiKey,environment(production/staging/development/local), optionaldefaultCurrency(defaultGHS),defaultTimezone(defaultAfrica/Accra),timeoutMs(default30000),maxRetries(default3),modules, anddebug.- Sub-clients on
MaatClient:companies(CompanyClient),finance(FinanceClient),risks(RiskClient),scenarios(ScenarioClient),reporting(ReportingClient). forCompany(companyId)returns aScopedCompanyClient;ping()probes/api/v1/health.- Response envelopes:
ApiResponse<T>(data+meta) andPaginatedResponse<T>(addspagination).FilterClausesupports operatorseq,neq,gt,gte,lt,lte,in,contains,startsWith.
The other SDK modules are cli-tool, react-components, webhook-manager,
api-key-manager, batch-operations, sse-streaming, plugin-architecture,
offline-mode, and sdk-docs-generator.
7. V2 Cross-Domain Contracts#
Maat owns three contracts that serve the separate V2 (fighting-game) program. They are implemented inside the Maat libraries because Maat provides the business authority — financial transaction governance, season calendar, and balance analytics — even in the context of a competitive game. A key invariant of all three contracts is that rollback frames must never call Maat services: real-time combat simulation is deterministic and must not depend on external service availability.
7.1 Finance Persistence Ledger — @maat/finance/v2-persistence-ledger#
This subsection defines the V2 Persistence Ledger Contract: Maat finance is
the single source of truth for every V2 currency mutation, and the engine holds
only a reconciling cache. The TypeScript authority lives in
libs/maat/finance/src/v2-persistence-ledger.ts, the V2 service wrapper is
published as @v2/maat-finance-ledger (depending on @maat/finance at
workspace:*), and the in-engine policy struct is FV2MaatFinanceLedgerPolicy
in V2/ue/Source/V2Persistence/Public/V2PersistenceTypes.h.
v2-persistence-ledger.ts exports V2_MAAT_FINANCE_LEDGER_CONTRACT_ID
(v2.persistence.maat-finance-ledger.v1) and
V2_MAAT_FINANCE_OFFLINE_CACHE_MODE
(v2-engine-ledger-cache-reconcile-on-sync).
Transaction types are grant, spend, refund, adjust, migration.
Decision reasons include approved-online-authority, approved-offline-cache,
invalid-transaction-shape, region-price-mismatch, regulatory-cap-exceeded,
refund-reference-required, and refund-window-expired.
V2MaatFinanceRegionalPricingPolicy carries region-aware PPP pricing and
regulatory transaction/daily-spend caps.
Required behaviour:
- Spend decisions verify region-aware PPP pricing.
- Spend and refund decisions check regulatory caps and refund windows before the entry is mirrored.
- Approved offline entries carry a Maat decision id and reconcile on next online sync.
- Rollback frames never call Maat finance services.
The Unreal side mirrors this in FV2MaatFinanceLedgerPolicy, whose
SourceOfTruthPackageName defaults to @maat/finance and whose
bRequiresOnlineReconciliation flag marks cached offline entries that must be
re-validated against Maat on the next online sync. ApplyMaatFinanceTransaction
consumes the policy to apply region-priced spend/refund deltas to the engine
currency ledger; it never participates in deterministic combat and never
influences rollback. The reciprocal @v2/maat-finance-ledger service composes
@maat/finance as its source of truth
(sourceOfTruthPackageName: MAAT_FINANCE_PACKAGE_NAME), flags
inEngineLedgerCache: true and reconcileOnNextOnlineSync: true, and pins
mayInfluenceRollback: false.
7.2 Live-Ops Calendar — @maat/strategy/v2-fighting-game-live-ops-calendar#
This subsection defines the V2 Fighting-Game Live-Ops Calendar Contract,
served to the V2 program through the @v2/maat-live-service-calendar package
(depending on @maat/strategy at workspace:*). Maat strategy is authoritative
for the season calendar and Crown Points economy; the V2 wrapper marks
standaloneV2LiveCalendarReplaced: true, asserting that there is no separate V2
calendar schema, and exposes the engine read hooks Rank.GetCrownPoints and
Esports.QualifyForFinals.
libs/maat/strategy/src/v2-fighting-game-live-ops-calendar.ts exports
MAAT_V2_LIVE_OPS_CALENDAR_SOURCE_OF_TRUTH_ID, the Pro Circuit tier values
(MAAT_V2_PRO_CIRCUIT_TIER_VALUES), and the live-ops event kinds
(MAAT_V2_LIVE_OPS_EVENT_KIND_VALUES). Types include MaatV2LiveOpsSeason,
MaatV2ProCircuitEvent, MaatV2AnniversaryEvent, MaatV2CharityEvent,
MaatV2BalanceRampWindow, and MaatV2CrownPointsRule. The functions
calculateMaatV2CrownPoints(input) and
buildMaatV2FightingGameLiveOpsCalendar(...) compute Crown Points and assemble
the calendar. Maat owns seasons, ranked-season windows, Crown Points pool sizing
and tier scoring, the Pro Circuit calendar, and anniversary/charity/balance-ramp
windows. Rollback frames never call the calendar.
7.3 Balance Dashboard — @maat/dashboard/v2-balance-dashboard#
This subsection defines the V2 Balance Dashboard Contract, exposed to the V2
program as @v2/maat-balance-dashboard. That service composes three Maat
libraries — @maat/intelligence (trend and anomaly detection),
@maat/dashboard (panel/manifest governance), and @maat/reporting (scorecards
and operational KPI charts) — each pinned at workspace:*; it deliberately does
not depend on @maat/analytics. The wrapper sets
standaloneV2DashboardReplaced: true, confirming there is no standalone V2
balance dashboard, and pins mayInfluenceRollback: false.
libs/maat/dashboard/src/v2-balance-dashboard.ts exports
MAAT_V2_BALANCE_DASHBOARD_ID (maat.v2.balance-dashboard) and
buildMaatV2BalanceDashboardSurface(input). The contract declares the companion
@maat/reporting and @maat/intelligence package names by reference; it does
not import them. MAAT_V2_BALANCE_DASHBOARD_EVENT_TOPICS lists eight topics:
v2.match.ended, v2.match.combat.move.used, v2.match.combat.damage.applied,
v2.match.combat.attack.blocked, v2.match.combat.frame-data.drift,
v2.match.balance.ab-result.published, v2.match.balance.ptb-signal.recorded,
and v2.match.performance.frame-budget-exceeded. Panel kinds: pick-rate,
win-rate, win-rate-on-block, frame-data-drift, hot-cold-heatmap,
ab-publication, release-gate. The output is a dashboard/reporting artifact;
it is off rollback and must not influence deterministic combat simulation.
8. Knowledge Graph Schema (@maat/knowledge)#
libs/maat/knowledge/src/knowledge-graph-neo4j-schema.ts defines an
in-memory graph schema (TypeScript records and Zod validation; no
neo4j-driver dependency). The schema is designed for a future Neo4j
projection: every node record carries a neo4jLabel field, and relationship
types are modelled to map directly onto Neo4j relationship labels.
The vocabulary is:
KG_NODE_TYPE_VALUES:ORGANIZATION,PERSON,PRODUCT,MARKET,REGULATION,TECHNOLOGY,LOCATION,EVENT,CONCEPT.KG_RELATIONSHIP_TYPE_VALUES:COMPETES_WITH,SUPPLIES_TO,REGULATED_BY,LOCATED_IN,INVENTED_BY,PART_OF.KnowledgeGraphNodeRecord(nodeId,nodeType,neo4jLabel,properties,createdAt,updatedAt,version) andKnowledgeGraphRelationshipRecordmodel nodes and edges.
9. Frontend Applications#
apps/maat/ contains 11 application roots: 5 implemented Next.js web apps,
1 implemented Hono service app, and 4 bootstrap scaffold service apps.
9.1 Implemented Next.js Apps (5)#
Each app targets a distinct user persona: portfolio-level executives use the
dashboard app, operations teams use agent-console, analysts use intel, investors
use the investor portal, and strategists use the war room. Each runs on its own
Next.js port (e.g. dashboard on 3055) and has build/start/lint/
typecheck/test Nx targets.
| App | Package | Routes (route groups) |
|---|---|---|
dashboard |
maat-dashboard-app |
(auth)/login, (dashboard)/portfolio, (dashboard)/company, (dashboard)/company/[companyId], (dashboard)/alerts, (dashboard)/layout-builder; API routes /api/auth/login, /api/auth/logout |
agent-console |
@maat/agent-console |
(console) — tasks, approvals, fleet, conversations, analytics |
intel |
@maat/intel |
(intel) — landscape, news, pricing, regulatory, sizing |
investor |
@maat/investor |
(portal) — dashboard, portfolio, explore, simulate, onboard |
war-room |
@maat/war-room |
(war-room) — scenario, market-entry, strategy-canvas, synergies, capital |
9.2 Implemented Service App (1)#
api-gateway (maat-api-gateway-app) — the Hono gateway described in §5.
9.3 Scaffold Service Apps (4)#
agents (maat-agents-app), intelligence (maat-intelligence-app),
simulation (maat-simulation-app), and worker (maat-worker-app) each
contain only a bootstrap<name>() function returning an "<name> initialized"
string. They are placeholders for future deployable services.
10. Technology Stack#
The table below summarizes the technology choices for the domain. The most important entry for new contributors is the last row: there is no persistence layer wired in. All domain state lives in-memory. The Redis namespace and Neo4j schema modules are forward-looking contracts, not active connections.
| Layer | Technology |
|---|---|
| Language | TypeScript (Node.js) |
| Validation | Zod |
| Gateway framework | Hono (@hono/zod-validator, @hono/swagger-ui) |
| Gateway auth | jsonwebtoken (HS256) |
| Frontend | Next.js (App Router) |
| LLM SDK | @anthropic-ai/sdk (used only in @maat/intelligence) |
| Build | Nx with @nx/js:tsc (libs / gateway) and next build (web apps) |
| Testing | Vitest (@nx/vite:test) |
| Persistence | None — all domain state is in-memory value objects |
There is no PostgreSQL, Drizzle, Neo4j driver, or Redis client wired into the domain. The "Redis namespace" and "Neo4j schema" modules produce keys and record shapes for a future datastore integration but do not connect to one.
11. Validation Rules#
The @maat/core Zod schemas enforce a set of domain invariants at construction
time. These rules apply to all entities and must be respected in any code that
creates or modifies Maat domain objects.
Entity-level rules:
- Strict objects — every entity schema uses
.strict(), rejecting unknown keys. - ID patterns — IDs must match their prefixed regex (§1.1).
- Currency —
currencyCodemust be a 3-letter ISO 4217 code; country codes must be 2-letter ISO codes. - Percent fields —
0 ≤ value ≤ 100; probability/score fields —0 ≤ value ≤ 1; fiscal years — integer1900 ≤ year ≤ 9999. - Ghana GH jurisdiction — a
LegalEntityRegistrationwithjurisdictionCode === 'GH'must use a Ghana company registration number. - Exactly one primary registration —
LegalEntityStructure.registrationsmust contain exactly one entry withisPrimary === true. - Board terms — a board member's
termEndAtcannot precedeappointedAt. - Non-empty collections —
Organization.operatingCompaniesandLegalEntityStructure.registrationsrequire at least one entry. - Agent capabilities —
createAgentrejects capabilities not listed for the agent's class inAGENT_CAPABILITY_MATRIX.
Event validation rules (enforced by event-bus.ts): eventId must be a UUID,
schemaVersion must be the literal 1, occurredAt must be ISO-8601 with
offset, confidence values are bounded 0–1, expectedImpactScore is bounded
−100…100, and StrategyDecisionEvent.approvedBy requires at least one
approver.
12. Acceptance Criteria#
A Maat change is acceptable when all of the following conditions hold:
- New or changed
@maat/coreentities ship the full set — interface,*Draft,*_VALUESarrays for any new enum, Zod schema,create*,create*WithDefaults,serialize*/deserialize*, andis*— and are re-exported fromindex.ts. - Every schema is
.strict()and every constructor throws on invalid input. npx tsc --noEmitandnpx vitest runpass in the affected library directory (Nx may be bypassed per repository policy).- New API gateway routes declare a
MaatPermission, enforceensureOrganizationAccessfor org-scoped data, and are reflected in the generated OpenAPI spec. - New domain events extend
BaseMaatEventSchema, are added toMaatDomainEventTypeSchemaandMAAT_EVENT_SCHEMAS, and validate throughparseMaatDomainEvent. - V2 contract changes preserve the rollback-exclusion and source-of-truth-ownership rules in §7.