Deal Management System (DMS)

The Structure-stage machinery. DealPlan as the typed action DAG two parties sign over; the deal lifecycle (Draft → Proposed → Accepted → Active → Completed); plan compilation to workflows; agreement projection as legal-style markdown; the deal_structurer agent persona; the dms-deals system workspace.

Guides/Structure

Where this sits in the alignment economy. The DMS is the machinery of the Structure stage. Once participants have reasoned, communicated, and negotiated a confirmed Intent, the DMS is what turns that Intent into a typed, hashable, signable DealPlan — and then drives the lifecycle that compiles the plan into an executable workflow, mirrors status back as the workflow runs, and renders the result as a readable agreement.

"Deal structuring" is the work the DMS does. Read the alignment-economy guide for the framing; read agents, knowledge & workspaces for the substrate the DMS runs on.

What the DMS is

The DMS (Deal Management System) is the domain layer that owns:

  • Deal as a first-class domain object (not the same as a payment, a contract, or a swap — a deal is the legal-shaped wrapper around the on-chain artifacts it produces).
  • DealPlan — a typed action DAG that describes what the deal does. Two parties sign over the plan's hash; subsequent rendering, execution, and audit all key off the same canonical object.
  • The deal state machine — Draft → Proposed → Accepted → Active → Completed (with CounterOffered / Rejected / Cancelled / Defaulted / FailedAfterPartialExecution detours).
  • Plan compilation — DealPlan → pipeline WorkflowDefinition, submitted via HTTP to the agents pipeline runner.
  • Status mirroring — workflow events flow back as deal_events, so the deal is the audit-trail unit.
  • Agreement projection — frames + DealPlan + agreement metadata render as a legal-style markdown agreement with recitals, clauses, governing law, and dispute resolution.
  • Frame emission — every state transition writes frames into the deal's KG, so the deal is queryable knowledge alongside documents and conversations.

The current name reflects the scope: a loan is one specific deal kind among many — repos, distributions, structured products, service agreements, escrow arrangements — and the DMS handles the lifecycle uniformly across them.

The Deal lifecycle

The state machine, with the GraphQL mutation that drives each transition:

                              proposeDeal
                                   │
                                   ▼
   ╭──────╮                  ╭──────────╮
   │ DRAFT│ ── propose ───►  │ PROPOSED │
   ╰──────╯                  ╰─────┬────╯
   (wizard                         │
    save state)         ┌──────────┼─────────────┐
                        │          │             │
                  signDeal     rejectDeal   counterOfferDeal
                        │          │             │
                        ▼          ▼             ▼
                  ╭──────────╮ ╭──────────╮ ╭──────────────╮
                  │ ACCEPTED │ │ REJECTED │ │COUNTEROFFERED│
                  ╰────┬─────╯ ╰──────────╯ ╰──────┬───────╯
                       │                          │
                       │       (child deal in PROPOSED;
                  activateDeal  parent in COUNTEROFFERED;
                       │        signing the child completes the cycle)
                       ▼
                  ╭──────────╮
                  │  ACTIVE  │  one-shot pipeline runs;
                  ╰────┬─────╯  periodic processing if applicable
                       │
              ┌────────┼──────────────┬────────────────┐
              │        │              │                │
              ▼        ▼              ▼                ▼
        ╭──────────╮ ╭───────────╮ ╭────────────╮ ╭──────────────────┐
        │COMPLETED │ │ CANCELLED │ │ DEFAULTED  │ │FAILED_AFTER_     │
        │          │ │           │ │            │ │PARTIAL_EXECUTION │
        ╰──────────╯ ╰───────────╯ ╰────────────╯ ╰──────────────────╯
        all phases   either party  periodic       one-shot failed
        clean        cancelled;    processing     mid-execution;
                     cleanup ran   past grace     compensation needs
                                   period         user OnFailure edges

Terminal states: Completed, Cancelled, Rejected, Defaulted, FailedAfterPartialExecution. Active is the in-progress state. Each transition writes a deal_events audit record alongside the state change, so the deal state and audit trail stay aligned.

Caller rules per mutation:

Mutation
proposeDeal
Allowed caller
any authenticated user
Mutation
signDeal
Allowed caller
any party listed in Deal.parties (once each)
Mutation
rejectDeal
Allowed caller
any party
Mutation
counterOfferDeal
Allowed caller
any party (creates a child Proposed deal; parent moves to CounterOffered)
Mutation
cancelDeal (from Proposed)
Allowed caller
the deal's proposer_entity_id
Mutation
cancelDeal (from Accepted)
Allowed caller
any principal (proposer or party)
Mutation
activateDeal
Allowed caller
the deal's proposer_entity_id
Mutation
processDealPeriod (periodic phase)
Allowed caller
any party or a recognised backend scheduler

The DealPlan

A DealPlan is the immutable artefact that defines what the deal does. It's a typed directed acyclic graph (the only allowed cycles are explicit retry edges, bounded by a max_loop_iterations validator). Each node is a typed action referencing a descriptor in the DealActionRegistry by task_name. The plan is the legal envelope. Two parties signing the deal sign over plan_hash, not over any party's particular view of the plan. Once locked at proposeDeal, the plan cannot be modified — only superseded via counterOfferDeal, which creates a new child deal with a new plan and a new hash.

Reference resolution inside the plan

Action inputs use placeholder references that get resolved at execution time:

Placeholder
$step.X.Y
Resolves to
Output Y of an earlier step X in the plan DAG
Placeholder
$cashflow.X
Resolves to
A field from the deal's cashflow config
Placeholder
$period.X
Resolves to
A field from the current period (for periodic-phase steps)

The validator type-checks references at proposal time — a deal that asks for a field that doesn't exist or has the wrong type is rejected before any party can sign.

Validation at proposal time

proposeDeal runs strict validation before locking the plan:

  • All party entity_ids resolve and are distinct.
  • The proposer is not also a party (no self-deals).
  • The plan's action DAG is well-formed (no cycles except via retry edges, all step_ids unique, all references typed).
  • All declared cashflows are wired into at least one step's input.
  • agreement_metadata.platform_terms_version is set (the universal master terms must be pinned at proposal time so platform amendments can't retroactively alter signed agreements).

Drafts (saved via saveDealDraft) skip this validation so partial authoring works. Strict validation only fires when the user clicks Propose at the end of the wizard.

From Intent to executed Deal

The full Stage 3 (Negotiate) → Stage 4 (Structure) → Stage 5 (Execute) flow:

   Stage 3                                                Stage 4                                  Stage 5
   ────────                                               ────────                                 ────────

   Confirmed Intent
   (with proposed terms,           ──── deal_structurer agent composes DealPlan ──►
    parties, cashflow refs)             saveDealDraft (iterative)
                                        validate                  Draft
                                        ▼                          │
                                   proposeDeal                     │
                                        ▼                          ▼
                                   PROPOSED  ── signDeal ──►   ACCEPTED  ── activateDeal ──►   pipeline submitted
                                        │                                                       to agents-api
                                  optional:                                                     ▼
                                  counterOfferDeal                                              workflow runs
                                  (loop)                                                        ▼
                                                                                                pipeline_status_mirror
                                                                                                writes deal_events
                                                                                                ▼
                                                                                              ACTIVE / COMPLETED /
                                                                                              DEFAULTED / …
                                                                                                ▼
                                                                                              on-chain artifacts settled

proposeDeal — locking the plan

mutation ProposeBondTrancheA {
  dealFlow {
    proposeDeal(input: {
      name: "Tranche A — 2027 Q1 receivables",
      parties: [{ entityId: "ENT-investor-12" }],
      plan: { /* canonical-JSON DealPlan */ },
      cashflowRef: "cashflow:tranche-A-2027-Q1",
      idempotencyKey: "tranche-A-2027-Q1-propose-v1"
    }) {
      success
      deal { id status planHash parties { entityId } }
    }
  }
}

Effect: a new row in deals with status Proposed, the proposer's signature minted implicitly, plan locked, audit event emitted. The counterparties receive an IntentProposed-style event (and a thread notification in the workspace where the deal was raised — typically the dms-deals system workspace or a regular WG).

signDeal — counterparty assent

mutation { dealFlow {
  signDeal(input: { dealId: "DEAL-xxx" }) { success deal { status } }
}}

Each non-proposer party calls signDeal once. When the last outstanding party signs, status flips Proposed → Accepted and accepted_at is stamped. The plan_hash the party signs over is the same canonical SHA-256 the proposer locked at proposeDeal — both parties end up with cryptographically-identical evidence of what they agreed to.

activateDeal — compile + submit

mutation { dealFlow {
  activateDeal(input: { dealId: "DEAL-xxx" }) { success deal { status } }
}}

This is where structuring crosses into execution. activateDeal:

  1. Compiles the DealPlan's action DAG into a WorkflowDefinition (the shape the agents pipeline runner consumes).
  2. Submits the workflow to the agents pipeline runner.
  3. Marks the deal Active.
  4. The submitted workflow runs the one-shot phase first — typically the contract / swap / payment mutations against the federated GraphQL gateway that actualise the deal's terms on-chain.
  5. As workflow events fire, the platform mirrors them into deal_events so the deal is the single audit-trail unit.

Periodic processing

Deals with a periodic_phase (loans with monthly coupons, repo roll schedules, ABS waterfall distributions) keep running after the one-shot phase. processDealPeriod(period_index) advances one period at a time:

mutation { dealFlow {
  processDealPeriod(input: { dealId: "DEAL-xxx", periodIndex: 3 }) {
    success
  }
}}

Idempotent on (deal_id, period_index) — a status check in deal_periods short-circuits already-processed periods, so retries are safe. Either party or a recognised backend scheduler can call this; periodic-phase failures past the grace period transition the deal to Defaulted with default-branch cleanup actions run.

Agreement projection — the deal as a readable agreement

A signed deal is more than a workflow spec — it's a legal-shaped agreement that humans need to read, audit, and reference. Deal events emit frames into the deal's KG, and the agreement projector loads those frames into a typed view plus rendered markdown.

The output is a structured markdown document with:

  • Title (from agreement_metadata.title_override, falling back to the matched Concept's display name).
  • Recitals — the "WHEREAS" preamble paragraphs the proposer authored, each numbered automatically.
  • Definitions — defined terms extracted from frames and the manifest.
  • Operative clauses — one per step in the DealPlan, rendered from descriptor ClauseTemplates with author overrides applied.
  • Master terms incorporation by reference — pinned at platform_terms_version.
  • Boilerplate pack expansion — e.g. "services_agreement_uk_2024", "consumer_loan_uk_fca_2024".
  • Governing law + dispute resolution — from agreement_metadata or, when absent, fallback to the matched Concept's manifest properties.
  • Signatories block with each party's signature timestamp.

GraphQL exposure: dealAgreement query returns the rendered markdown; dealAgreementGraph returns the typed view for programmatic consumers (MCP tools, structured display).

Plan-hash invariance

AgreementMetadata and ClauseMetadata are entirely optional — plans authored before this module existed serialise byte-identically because Option::None is filtered by the canonical-JSON hash machinery. Adding agreement metadata to a new plan changes its hash; populating it after signing is not possible. This is the right safety property: the rendered agreement and the signed plan_hash are always for the same canonical object.

The dms-deals working group

The agents binary auto-creates a system-owned working group with category = "dms-deals", one per economy. This is where deal workflows execute when the user-side workspace doesn't otherwise have a home for them. The group id is installed at boot via set_default_deal_workflow_group_id; pipeline_submission reads it as a fallback when DMS_DEAL_WORKFLOW_GROUP_ID env var is unset.

Implications for application authors:

  • Deal-related workflow threads (pipeline_main, pipeline_task) for dms-deals-routed deals land in the system workspace, not the user's primary workspace.
  • If you want deal workflows to surface in a specific workspace's chat stream, pass that workspace's id at proposeDeal time via the appropriate input field (or set the env var per deployment).
  • The dms-deals workspace is system-owned — members are auto-added based on the deal's parties; you don't manage it manually.

The deal_structurer seeded agent

The platform ships a seeded agent persona with agent_key = "deal_structurer" whose specific role is to compose DealPlans from confirmed intents. It reads:

  • The confirmed Intent (proposed terms, parties, asset identifiers).
  • The relevant KG (asset frames, party history, prior deals).
  • The applicable economy manifest (which Concepts are available, their defaults, their action descriptors).

…and produces a candidate DealPlan with:

  • A typed action DAG sized to the deal's complexity.
  • A draft AgreementMetadata block — recitals composed from the conversation history, governing-law selection from the parties' jurisdictions, boilerplate-pack matched against the deal Concept.
  • Wired-up cashflow references and period schedule (for multi-period deals).

The Structurer's output is itself a Pipeline Draft — it materialises as an Intent the proposer reviews and confirms before saveDealDraft + proposeDeal actually land. The Structurer proposes; the human proposer commits. Same safety boundary as every other agentic action.

Domain owners can fork and specialise the Structurer for their deal types (a lending Structurer biases toward loan templates; a treasury Structurer biases toward payment-approval templates).

Frame emission — the deal as queryable knowledge

Every state transition writes frames into the deal's KG:

  • proposeDeal writes frames for the proposer, each party, the cashflow config, the plan's structural roles.
  • signDeal updates the relevant party's AgreementState from "proposed" to "signed"; clause renderers pick this up and flip the lifecycle markers in the rendered markdown.
  • activateDeal writes the workflow id + initial period state.
  • Periodic-phase advancement writes per-period frames.
  • Terminal transitions (Completed, Cancelled, Defaulted) write closing frames + summary statistics.

Once frames are in the deal's KG, every other tool — RAG queries, the agreement projector, conversation analyzers — can read the deal's state alongside any other knowledge in the workspace. The deal is not a black box. Its state, its history, its parties, and its cashflows are all first-class knowledge that downstream agents can reason over.

Common deal shapes

The DMS doesn't enforce specific instrument types — any DealPlan that compiles validates. But four shapes are common enough to ship as plan-template factories:

Shape
Bundle
What it produces
A single deal wrapping multiple contract / payment / swap actions that must commit or fail together. The "service agreement with monthly billing" template.
Shape
Issuance
What it produces
One party (issuer) creates a composed contract; the deal binds the issuer to honour the contract's obligations as periodic phases unfold.
Shape
Issuance + swap
What it produces
An issuance plus an atomic swap of the issued contract for cash from the counterparty. The canonical securitisation tranche shape.
Shape
Repo / collateralisation
What it produces
An atomic swap with collateral and a repurchase obligation. See Collateralisation for the structural details — the DMS sits on top, adding the legal envelope (AgreementMetadata) and the lifecycle (propose → sign → activate).

Custom DealPlans for novel deal kinds are first-class — domain authors compose them in YAML/JSON and submit via saveDealDraft + proposeDeal.

Connection to other systems

The DMS sits between Intent and chain:

   ╭───────────╮     ╭───────────╮     ╭────────╮     ╭───────────╮     ╭────────╮
   │  Intent   │ ──► │   DMS     │ ──► │ Agents │ ──► │  Payments │ ──► │ Chain  │
   │  (Stage 3 │     │ (Stage 4) │     │pipeline│     │   (MQ)    │     │        │
   │ Negotiate)│     │           │     │ runner │     │           │     │        │
   ╰───────────╯     ╰─────┬─────╯     ╰───┬────╯     ╰───────────╯     ╰────────╯
                           │               │
                           │               ▼
                           │            workflow status events
                           │               │
                           └─◄──────────────┘
                              pipeline_status_mirror
                              writes deal_events
  • Upstream (Intent): the confirmed Intent carries the parameters the DMS needs to build a DealPlan — proposer, parties, cashflow refs, agreement metadata.
  • Downstream (agents pipeline runner): the DMS compiles the DealPlan to a WorkflowDefinition and submits via HTTP. The runner advances steps using all the normal workflow primitives (human_task, agent_task, signature_gate, automated).
  • Downstream (payments MQ): workflow steps that need on-chain effect submit messages — createSwap, createObligation, instant send, etc. The MQ pipeline runs validation + execution; on-chain state ratchets.
  • Status feedback loop: the pipeline status mirror watches workflow events and writes them as deal_events, so the deal is the single audit-trail unit across the whole chain. Querying deal.events returns the propose, sign, activate, every workflow step, every period transition, and the terminal state.

Querying deals

query MyDeals {
  dealFlow {
    dealsByProposer(status: PROPOSED) {
      id
      name
      status
      planHash
      parties { entityId }
      events { timestamp eventType payload }
    }
    dealsAsParty(status: ACCEPTED) {
      id
      name
      status
      agreementMarkdown   # rendered agreement document
      cashflowSummary { totalIn totalOut periods {} }
    }
  }
}

Filter by status, by role (proposer vs party), or by deal kind (via dealKind: "BUNDLE", etc.). The dealAgreement query returns the rendered markdown; dealAgreementGraph returns the typed view for programmatic consumers.

For periodic-phase deals, deal.periods returns each period's status, scheduled date, and processing event log.

Pitfalls

Pitfall
Treating a Proposed deal as binding
Symptom
Trying to act on a deal before it's Accepted
Right approach
A Proposed deal is one-sided; only the proposer has signed. Wait for signDeal from every party. Status is the discriminator.
Pitfall
Modifying the plan after signing
Symptom
Plan hash drift; signatures no longer valid
Right approach
The plan is immutable post-propose. To change terms, use counterOfferDeal (creates a child deal in Proposed status).
Pitfall
Acting on Active without checking phases
Symptom
Failing on a deal whose one-shot phase hasn't completed
Right approach
Active covers both one-shot-running and periodic-running. Check the workflow status via deal.workflowStatus before acting on the deal's products.
Pitfall
Forgetting periodic processing
Symptom
Deals silently going to Defaulted after grace period
Right approach
If your deal has a periodic_phase, your application or backend scheduler must call processDealPeriod per period. The platform doesn't auto-advance.
Pitfall
Cross-economy plans
Symptom
A plan referencing actions / cashflows from a different economy
Right approach
Plans validate against the proposer's economy. Cross-economy deals need explicit federation; consider a single shared economy for the deal's lifetime instead.
Pitfall
Skipping the agent translation
Symptom
Asking a human to write a DealPlan from scratch
Right approach
The deal_structurer agent exists for this reason. Drive plan composition from a conversation, let the Structurer propose a candidate, let the human review + edit + propose. Pure-manual plan authoring is a power-user path.

Reference

You need
Deal lifecycle operations
Use
dealFlow.* namespace via the Apollo Router gateway
You need
Rendered agreement
Use
dealAgreement query
You need
Programmatic agreement graph
Use
dealAgreementGraph query
You need
Underlying primitives

See also

YieldFabric docs(317)