Real Asset Issuance Wizard Spec
Status: DRAFT
Date: 2026-04-20
Scope: Spec/design batch only
Audit Basis: code-verified repository inspection
1. Executive Summary
This specification proposes a governance-first Real Asset Token Issuance Wizard that fits the strongest existing product patterns in the repo instead of introducing a naive "mint token" form.
The design is grounded in these confirmed repo realities:
- The strongest durable write pattern today is declaration-backed draft -> readiness -> ready -> declare with transactional event logging and registry projection updates.
- Parcel and inspection surfaces already use gate-oriented projections with
currentGate,missingRequirements, and prefilled payloads instead of optimistic direct publishing. - Output allocations already look more like reusable registry/inventory records than narrow auction-only fields, but their final ownership is intentionally unresolved between auction, settlement/financial, and shared-registry directions.
- The financial layer is explicitly shadow-only/advisory until integrated upstream, and settlement/anchoring are proof/finality layers rather than approval engines.
- The repo already carries finance-side stakeholder roles such as insurance, reinsurance, escrow, and payment providers, which makes governance-first insurance/reserve/financing seams more appropriate than direct chain semantics.
The result is a wizard that should:
- start from verified source asset truth
- pass through eligibility, structuring, reserve, insurance, and financing gates
- end in an issuance-ready declared structure
- keep blockchain binding out of scope for this batch
- preserve deterministic readiness, approval, and immutability rules
2. Why a Governed Issuance Wizard Is Needed Instead of Direct Token Minting
A direct mint form would be wrong for this repo for six reasons:
- It would bypass source-asset truth. The repo is moving toward parcel, declaration, and inspection-backed registry truth, not free-form asset creation.
- It would ignore remaining-capacity logic. Issuance must respect approved limits, already declared quantities, reserve locks, and remaining eligible basis.
- It would collapse governance into UI fields. Current durable patterns separate draft authoring, readiness checks, mark-ready, and declaration/approval.
- It would treat settlement or blockchain as authority. Repo governance docs consistently position settlement and anchoring as proof/finality only, not approval logic.
- It would hide real structuring risk. Reserve floor, haircut, insurance, and financing cannot be optional side notes if the issuance is supposed to represent a governed claim.
- It would feel bolted on. The current product trajectory favors admin declaration flows, explicit blockers, event logs, and registry projections, not public "action buttons" pretending to be durable truth.
In short: the correct product is not "create token." The correct product is "structure an issuance against a verified asset basis and declare it only after governance gates are satisfied."
3. Proposed Wizard Steps
3.0 Layer Framing
The wizard should be modeled in three layers:
- Source Asset Layer: parcel, output allocation, declaration, inspection, receivable, or later inventory/warehouse basis.
- Structuring Layer: valuation, eligibility, reserve floor, haircut, insurance, financing/factoring, and claim design.
- Issuance Layer: token class, unit economics, maturity/redemption, transfer policy, readiness freeze, approval, and issue declaration.
3.1 Step 1: Asset Selection
- Name:
asset_selection - Purpose: Select the canonical real-asset basis and freeze the initial source snapshot.
- Data collected:
- source asset type
- primary registry/reference id
- owner/originator stakeholder
- jurisdiction and region
- productive asset or receivable linkage
- quantity/capacity snapshot
- source evidence references
- Required fields:
sourceAssetTypeprimaryRefIdownerStakeholderIdrightsBasisregistryTruthStatusjurisdictioncapacitySnapshot.remainingEligibleQuantity
- Optional fields:
parcelIdallocationIddeclarationIdinspectionIdreceivableIdinventoryLotIdwarehouseReceiptIdseasonlinked map/spatial reference
- Validations:
- source must resolve to a known internal or explicitly attested external reference
- owner/originator must be consistent with the source record
- requested basis cannot exceed remaining eligible quantity
- rights basis must support issuance structuring
- Hard blockers:
- source record not found
- ownership/right basis unresolved
- remaining eligible quantity is zero or negative
- source record already suspended/withdrawn/cancelled
- Warnings:
- source comes from mixed/fallback read surface
- inspection or declaration linkage not yet attached
- external reference attested but not fully registry-native
- Outputs to next step:
SourceAssetReference- locked capacity snapshot
- initial evidence catalog
- Readiness conditions:
- source resolved
- owner/right basis consistent
- remaining eligible capacity present
- Next-step dependencies:
- eligibility policy needs a resolved source asset snapshot
3.2 Step 2: Asset Eligibility + Checklist
- Name:
eligibility_checklist - Purpose: Decide whether the selected source asset is eligible for issuance under governance policy.
- Data collected:
- checklist items
- inspection requirement status
- declaration/evidence status
- policy version
- waiver flags if policy allows them
- Required fields:
policyVersion- checklist items for ownership, registry truth, capacity, evidence, and inspection where applicable
decision
- Optional fields:
- waiver request note
- inspection expiry date
- declaration linkage note
- evidence coverage note
- Validations:
- required checklist items must be satisfied
- inspection status must satisfy policy for the chosen source type
- declaration/remaining-capacity math must remain coherent
- mandatory evidence groups must exist
- Hard blockers:
- ownership not verified
- source registry truth unresolved or disputed
- inspection required but missing, failed, or expired under policy
- mandatory evidence missing
- approved limit exceeded
- Warnings:
- inspection near expiry
- evidence attached without strong hash/verification metadata
- source asset uses external attestations not yet mirrored into registry truth
- Outputs to next step:
EligibilityChecklist- step-scoped readiness findings
- eligible basis quantity/amount
- Readiness conditions:
- no blocking checklist item remains unsatisfied
- eligibility decision is
ELIGIBLEor policy-allowedCONDITIONALLY_ELIGIBLE
- Next-step dependencies:
- structuring step uses the eligible basis and policy decision
3.3 Step 3: Asset Structuring
- Name:
asset_structuring - Purpose: Convert the eligible source asset into a deterministic issuance basis.
- Data collected:
- valuation basis
- valuation amount/currency/date
- requested issuance amount or quantity
- haircut/risk buffer assumptions
- claim basis
- servicing party
- Required fields:
- valuation method
- valuation amount
- valuation currency
- valuation date
- requested issuance amount
- haircut percent
- claim basis
maxIssuableAmount
- Optional fields:
- risk buffer percent
- overcollateralization target
- pricing memo reference
- servicing notes
- concentration limit note
- Validations:
- requested issuance must be less than or equal to max issuable amount after haircut/buffers
- valuation date must be within policy freshness window
- claim basis must match source rights basis
- servicing party must be identified when servicing is required
- Hard blockers:
- valuation basis absent or stale beyond hard policy limit
- issuance amount exceeds eligible capacity after haircut
- claim rights conflict with source rights
- Warnings:
- manual valuation override
- high concentration or thin collateral margin
- servicing party not yet operationally onboarded
- Outputs to next step:
- structuring snapshot
- derived issuance ceiling
- collateral/risk baseline for reserves
- Readiness conditions:
- valuation and issuance sizing are internally consistent
- claim basis is legally and operationally coherent
- Next-step dependencies:
- reserve and insurance calculations depend on structuring outputs
3.4 Step 4: Risk and Reserve Configuration
- Name:
risk_reserve_configuration - Purpose: Define the deterministic buffer that protects the issuance against loss, underperformance, or enforcement delay.
- Data collected:
- reserve requirement level
- reserve type
- minimum reserve ratio
- configured reserve ratio
- haircut and overcollateralization settings
- replenishment/breach policy
- reserve evidence references
- Required fields:
requirementLevelreserveTypeminimumReserveRatioconfiguredReserveRatiohaircutPercent- breach action
- Optional fields:
- reserve asset kind
- reserve holder
- replenishment policy
- top-up evidence
- Validations:
- configured reserve ratio must meet or exceed minimum policy ratio
- reserve type must align with structuring method
- funded reserve evidence must exist when reserve is policy-required before issue
- Hard blockers:
- reserve ratio below minimum
- missing reserve evidence when policy requires funded reserve
- contradictory buffer configuration
- Warnings:
- reserve exists only by off-platform attestation
- reserve evidence nearing expiry or review date
- low cushion above minimum
- Outputs to next step:
ReserveConfiguration- reserve-related readiness findings
- Readiness conditions:
- reserve configuration satisfies minimum policy
- reserve evidence present where required
- Next-step dependencies:
- insurance and financing steps need the finalized reserve baseline
3.5 Step 5: Insurance Layer
- Name:
insurance_layer - Purpose: Record whether risk transfer to insurer/reinsurer is part of the issuance support package.
- Data collected:
- insurance requirement level
- provider and reinsurance references
- policy/binder reference
- coverage type and insured value
- deductible/exclusion note
- coverage period
- claim beneficiary
- evidence references
- Required fields:
requirementLevel- if required or selected: provider, coverage status, policy reference, coverage dates, claim beneficiary
- Optional fields:
- reinsurer
- deductible
- exclusions
- renewal note
- Validations:
- coverage period must include issuance risk window
- insured value should not be materially below supported exposure without warning
- claim beneficiary must align with issuance waterfall
- Hard blockers:
- insurance required but missing
- policy inactive or expired
- policy beneficiary conflicts with issuance claim structure
- Warnings:
- coverage optional and missing
- insured value below full exposure
- reinsurance claimed but not evidenced
- Outputs to next step:
InsuranceConfiguration- coverage-linked readiness findings
- Readiness conditions:
- if policy requires insurance, an active evidenced configuration is present
- Next-step dependencies:
- financing and issuance terms depend on whether insurance proceeds support the claim waterfall
3.6 Step 6: Factoring / Financing Layer
- Name:
factoring_financing_layer - Purpose: Record whether the issuance is unlevered or linked to receivable factoring, inventory finance, or prepayment advance support.
- Data collected:
- financing mode
- financier
- facility reference
- advance rate
- maximum financed amount
- recourse type
- priority/waterfall position
- maturity alignment
- assignment evidence references
- Required fields:
mode- if mode is not
NONE: financier, facility reference, advance rate, maximum finance amount, recourse type, priority rank, assignment evidence
- Optional fields:
- fallback financier
- covenant summary
- discount rate note
- settlement profile reference
- Validations:
- financing amount cannot exceed policy or structuring ceiling
- receivable assignment/right transfer must be evidenced
- maturity must align with issuance/redemption profile
- waterfall priority must be explicit
- Hard blockers:
- financing selected but terms incomplete
- advance rate exceeds policy
- assignment proof missing
- financing maturity conflicts with issuance maturity
- Warnings:
- financier profile exists but settlement profile is not yet configured
- facility pending signature
- recourse terms materially weaken investor claim quality
- Outputs to next step:
FactoringConfiguration- financing-linked readiness findings
- Readiness conditions:
- selected financing mode is fully specified and evidence-backed
- Next-step dependencies:
- issuance terms must reflect financing priority and claim waterfall
3.7 Step 7: Issuance Terms
- Name:
issuance_terms - Purpose: Define the tokenized asset class in chain-neutral, governance-safe terms.
- Data collected:
- issuance class
- unit economics
- total supply
- claim rights
- issuance date
- maturity/redemption model
- transfer restrictions
- publication mode
- Required fields:
issuanceClassunitNameunitCurrencyfaceValuePerUnitunitCounttotalIssuanceAmountclaimRightsredemptionModelissuanceDate- transfer restrictions
- publication mode
- Optional fields:
- internal symbol
- maturity date
- redemption window
- servicing notes
- future binding mode
- Validations:
- unit economics must reconcile with total issuance amount
- claim rights must match structuring, reserve, insurance, and financing waterfall
- maturity/redemption model must be explicit
- transfer restrictions must be policy-readable, not implied
- Hard blockers:
- supply math inconsistent
- claim rights ambiguous
- maturity/redemption left undefined where required
- transfer policy undefined
- Warnings:
- transfer restriction depends on future capability not yet implemented
- maturity missing for a nominally fixed-term issuance
- internal symbol selected before class governance approval
- Outputs to next step:
TokenIssuanceTerms- issuance preview summary
- Readiness conditions:
- economics, rights, maturity, and transfer policy are deterministic
- Next-step dependencies:
- review step requires the finalized immutable issuance snapshot
3.8 Step 8: Review and Issue
- Name:
review_and_issue - Purpose: Freeze the draft, run consolidated readiness, obtain approvals, and record the issuance event.
- Data collected:
- final readiness report
- approval decisions
- immutable snapshot hash
- issue execution note
- publication/declaration metadata
- Required fields:
- readiness report with zero hard blockers
- required approval records
- immutable snapshot hash
- issue actor id
- Optional fields:
- issue note
- publication evidence
- downstream settlement or future chain binding reference
- Validations:
- no hard blocker remains
- source asset snapshot has not drifted since readiness freeze
- required approvals satisfy separation of duties
- issue event references the approved immutable snapshot
- Hard blockers:
- any unresolved blocker
- required approval missing
- snapshot drift between approval and issue
- evidence hash mismatch
- Warnings:
- warnings remain but are explicitly accepted by approver
- public publication still pending while internal issue is recorded
- Outputs to next step:
- lifecycle transition to
READY_FOR_REVIEW, thenAPPROVED_FOR_ISSUE, thenISSUED - immutable issued record
- event log entry suitable for future projection/registry publishing
- lifecycle transition to
- Readiness conditions:
- zero hard blockers
- approval complete
- immutable snapshot frozen
- Next-step dependencies:
- issued asset can later feed settlement, servicing, suspension, redemption, and closure flows
4. Canonical DTOs
These DTOs are intentionally chain-neutral. They describe governance, readiness, and issuance structure first, and leave live blockchain binding out of scope.
export type IssuanceWizardStepId =
| "asset_selection"
| "eligibility_checklist"
| "asset_structuring"
| "risk_reserve_configuration"
| "insurance_layer"
| "factoring_financing_layer"
| "issuance_terms"
| "review_and_issue";
export type IssuanceDraftState =
| "DRAFT"
| "STRUCTURING"
| "READY_FOR_REVIEW"
| "APPROVED_FOR_ISSUE"
| "ISSUED"
| "SUSPENDED"
| "MATURED"
| "REDEEMED"
| "CLOSED"
| "CANCELLED";
export type ReadinessSeverity = "BLOCKER" | "WARNING" | "INFO";
export type EvidenceReference = Readonly<{
kind: string;
refId: string;
label?: string;
uri?: string;
sha256?: string;
verified?: boolean;
uploadedAt?: string;
expiresAt?: string;
}>;
export type ReadinessFinding = Readonly<{
code: string;
severity: ReadinessSeverity;
stepId: IssuanceWizardStepId;
message: string;
path?: string;
sourceRefId?: string;
evidenceRefId?: string;
policyRef?: string;
expiresAt?: string;
suggestedAction?: string;
}>;
export type SourceAssetType =
| "PARCEL"
| "OUTPUT_ALLOCATION"
| "DECLARATION"
| "INSPECTION_CERTIFICATE"
| "RECEIVABLE"
| "INVENTORY_LOT";
export type RightsBasis =
| "OWNERSHIP"
| "LEASE"
| "ASSIGNMENT"
| "RECEIVABLE_ASSIGNMENT"
| "WAREHOUSE_CUSTODY";
export type RegistryTruthStatus =
| "VERIFIED"
| "PENDING_REVIEW"
| "DISPUTED"
| "EXTERNAL_ONLY";
export type SourceAssetCapacitySnapshot = Readonly<{
grossQuantity?: number;
unit?: string;
declaredQuantity?: number;
reservedQuantity?: number;
remainingEligibleQuantity?: number;
approvedLimitQuantity?: number;
requestedIssuanceQuantity?: number;
}>;
export type SourceAssetReference = Readonly<{
sourceAssetType: SourceAssetType;
primaryRefId: string;
parcelId?: string;
productiveAssetId?: string;
allocationId?: string;
declarationId?: string;
inspectionId?: string;
receivableId?: string;
inventoryLotId?: string;
ownerStakeholderId: string;
operatorStakeholderId?: string;
rightsBasis: RightsBasis;
registryTruthStatus: RegistryTruthStatus;
jurisdiction: string;
region?: string;
season?: string;
sourceState?: string;
assetSnapshot: Readonly<{
label?: string;
productCategory?: string;
quantity?: number;
unit?: string;
qualityGrade?: string;
originRegion?: string;
warehouseReceiptId?: string;
}>;
capacitySnapshot: SourceAssetCapacitySnapshot;
linkedEvidence: readonly EvidenceReference[];
}>;
export type EligibilityChecklistItem = Readonly<{
code: string;
label: string;
satisfied: boolean;
blocking: boolean;
detail?: string;
sourceDomain?:
| "PARCEL"
| "DECLARATION"
| "INSPECTION"
| "RESERVE"
| "INSURANCE"
| "FINANCING";
expiresAt?: string;
waiverAllowed?: boolean;
}>;
export type EligibilityDecision =
| "ELIGIBLE"
| "CONDITIONALLY_ELIGIBLE"
| "INELIGIBLE";
export type EligibilityChecklist = Readonly<{
policyVersion: string;
evaluatedAt?: string;
evaluatedBy?: string;
decision: EligibilityDecision;
linkedInspectionStatus?:
| "NOT_REQUIRED"
| "CURRENT"
| "EXPIRING"
| "EXPIRED"
| "FAILED";
linkedDeclarationStatus?: string;
evidenceCoverageStatus?: "COMPLETE" | "PARTIAL" | "MISSING";
items: readonly EligibilityChecklistItem[];
}>;
export type AssetStructuringSnapshot = Readonly<{
valuationBasis: Readonly<{
method:
| "REGISTRY_SNAPSHOT"
| "INSPECTION_VERIFIED"
| "RECEIVABLE_FACE_VALUE"
| "MARKET_REFERENCE"
| "INVENTORY_REPORT";
amount: number;
currency: string;
valuationDate: string;
providerStakeholderId?: string;
reportRefId?: string;
}>;
requestedIssuanceAmount: number;
requestedIssuanceCurrency: string;
requestedIssuanceQuantity?: number;
haircutPercent: number;
riskBufferPercent?: number;
overcollateralizationPercent?: number;
maxIssuableAmount: number;
claimBasis:
| "OWNERSHIP_BENEFICIAL_INTEREST"
| "OUTPUT_PROCEEDS"
| "RECEIVABLE_COLLECTION_RIGHT"
| "INVENTORY_RELEASE_RIGHT";
servicingPartyId?: string;
}>;
export type ReserveRequirementLevel = "NONE" | "OPTIONAL" | "REQUIRED";
export type ReserveConfiguration = Readonly<{
requirementLevel: ReserveRequirementLevel;
reserveType:
| "CASH_ESCROW"
| "COLLATERAL_BUFFER"
| "THIRD_PARTY_GUARANTEE"
| "HYBRID";
minimumReserveRatio: number;
configuredReserveRatio: number;
haircutPercent: number;
overcollateralizationPercent?: number;
reserveAssetKind?:
| "FIAT"
| "TOKENIZED_CASH"
| "SOURCE_ASSET_BUFFER"
| "GUARANTEE";
holderPartyId?: string;
evidenceRefs: readonly EvidenceReference[];
replenishmentPolicy?: string;
breachAction?: "BLOCK_ISSUANCE" | "SUSPEND" | "TOP_UP_REQUIRED";
}>;
export type InsuranceConfiguration = Readonly<{
requirementLevel: ReserveRequirementLevel;
coverageStatus:
| "NOT_SELECTED"
| "PENDING_BINDER"
| "ACTIVE"
| "EXPIRED";
providerStakeholderId?: string;
reinsurancePartnerStakeholderId?: string;
policyReference?: string;
coverageType?:
| "CROP"
| "PROPERTY_DAMAGE"
| "TITLE_RISK"
| "CREDIT"
| "PARAMETRIC"
| "MULTI_PERIL";
insuredValue?: number;
currency?: string;
deductiblePercent?: number;
coverageStartAt?: string;
coverageEndAt?: string;
claimBeneficiaryPartyId?: string;
exclusionsNote?: string;
evidenceRefs: readonly EvidenceReference[];
}>;
export type FactoringConfiguration = Readonly<{
mode:
| "NONE"
| "RECEIVABLE_FACTORING"
| "INVENTORY_FINANCE"
| "PREPAYMENT_ADVANCE";
financierStakeholderId?: string;
facilityReference?: string;
advanceRatePercent?: number;
maximumFinanceAmount?: number;
currency?: string;
recourseType?: "WITH_RECOURSE" | "WITHOUT_RECOURSE";
priorityRank?: "SENIOR" | "PARI_PASSU" | "SUBORDINATED";
settlementWaterfall?: readonly string[];
maturityDate?: string;
assignmentEvidenceRefs: readonly EvidenceReference[];
notes?: string;
}>;
export type TransferRestriction = Readonly<{
kind:
| "PERMISSIONED_ONLY"
| "JURISDICTION_LIMITED"
| "LOCKUP"
| "INVESTOR_TYPE_LIMIT";
value?: string;
until?: string;
}>;
export type TokenIssuanceTerms = Readonly<{
issuanceClass: string;
internalSymbol?: string;
unitName: string;
unitCurrency: string;
faceValuePerUnit: number;
unitCount: number;
totalIssuanceAmount: number;
claimRights: readonly (
| "PRINCIPAL"
| "PROCEEDS"
| "RESERVE_ACCESS"
| "INSURANCE_PROCEEDS"
| "RECOVERY_ACTION"
)[];
redemptionModel:
| "AT_MATURITY"
| "AMORTIZING"
| "REVOLVING_POOL"
| "EVENT_DRIVEN";
issuanceDate: string;
maturityDate?: string;
redemptionWindowStart?: string;
redemptionWindowEnd?: string;
transferRestrictions: readonly TransferRestriction[];
publicationMode:
| "INTERNAL_REGISTRY"
| "DECLARED_PUBLICLY"
| "PRIVATE_PLACEMENT";
servicingNotes?: string;
downstreamBindingMode: "DECLARATIVE_ONLY" | "FUTURE_CHAIN_BINDING";
}>;
export type ApprovalRecord = Readonly<{
stage:
| "READINESS_REVIEW"
| "ISSUANCE_APPROVAL"
| "ISSUE_EXECUTION"
| "SUSPENSION"
| "AMENDMENT";
actorId: string;
role: string;
decidedAt: string;
decision: "APPROVED" | "CHANGES_REQUIRED" | "REJECTED" | "EXECUTED";
note?: string;
}>;
export type IssuanceReadinessStepStatus = Readonly<{
stepId: IssuanceWizardStepId;
ready: boolean;
blockerCount: number;
warningCount: number;
infoCount: number;
lastEvaluatedAt?: string;
}>;
export type IssuanceReadinessReport = Readonly<{
draftId: string;
evaluatedAt: string;
evaluatedBy?: string;
policyVersion: string;
overallStatus:
| "NOT_READY"
| "READY_WITH_WARNINGS"
| "READY_FOR_REVIEW"
| "APPROVED_FOR_ISSUE";
hardBlockers: readonly ReadinessFinding[];
warnings: readonly ReadinessFinding[];
infoNotes: readonly ReadinessFinding[];
stepStatuses: readonly IssuanceReadinessStepStatus[];
immutableSnapshotHash?: string;
requiredApprovals: readonly (
| "READINESS_REVIEW"
| "ISSUANCE_APPROVAL"
| "ISSUE_EXECUTION"
)[];
}>;
export type RealAssetIssuanceDraft = Readonly<{
draftId: string;
state: IssuanceDraftState;
currentStep: IssuanceWizardStepId;
sourceAsset?: SourceAssetReference;
eligibility?: EligibilityChecklist;
structuring?: AssetStructuringSnapshot;
reserveConfiguration?: ReserveConfiguration;
insuranceConfiguration?: InsuranceConfiguration;
factoringConfiguration?: FactoringConfiguration;
issuanceTerms?: TokenIssuanceTerms;
readinessReport?: IssuanceReadinessReport;
evidenceCatalog: readonly EvidenceReference[];
governance: Readonly<{
createdBy: string;
lastEditedBy?: string;
rulesVersion: string;
sourceSystem: string;
amendmentOfDraftId?: string;
approvals: readonly ApprovalRecord[];
}>;
createdAt: string;
updatedAt: string;
submittedForReviewAt?: string;
approvedForIssueAt?: string;
issuedAt?: string;
suspendedAt?: string;
closedAt?: string;
}>;
5. Blocker / Warning / Readiness Model
5.1 Severity Model
- Hard blockers:
- prevent transition to
READY_FOR_REVIEW - must be resolved, not merely acknowledged
- represent violations of policy, missing source truth, broken math, or missing required evidence
- prevent transition to
- Warnings:
- do not prevent readiness by default
- must remain visible on review and approval screens
- may require explicit reviewer acknowledgement depending on policy
- Informational notes:
- do not affect transitions
- capture traceability, operational follow-up, or future integration notes
5.2 Default Finding Policy
| Condition | Default severity | Notes | | --- | --- | --- | | Ownership not verified | Hard blocker | Source truth failure | | Parcel/source record not registry-linked | Hard blocker | Unless policy explicitly permits external-only basis | | Remaining eligible quantity exhausted | Hard blocker | Prevents over-issuance | | Inspection expired | Hard blocker if required, warning otherwise | Policy-driven | | Inspection failed | Hard blocker | Eligibility failure | | Mandatory evidence missing | Hard blocker | Includes proof of title/assignment/reserve funding | | Insurance missing | Warning if optional, hard blocker if required | Policy-driven | | Reserve ratio below minimum | Hard blocker | Deterministic numeric gate | | Reserve evidence stale | Warning, escalates to blocker if policy requires current attestation | Policy-driven | | Factoring terms incomplete while financing selected | Hard blocker | Selected structure not fully defined | | Valuation stale | Warning or hard blocker depending on age threshold | Policy-driven | | Transfer restriction not yet implementable | Warning | Allowed only if declaration is still internal | | Snapshot drift after approval | Hard blocker | Must force re-review |
5.3 Readiness Rules
- Step readiness is local. A step is ready when its required fields are complete and it has no step-local hard blockers.
- Draft readiness is global. A draft is ready for review only when every required step is ready and the consolidated report has zero hard blockers.
- Approval readiness is stricter than authoring readiness. A draft with warnings may reach
READY_FOR_REVIEW, but approval must explicitly accept or return those warnings. - Issue readiness is immutable-snapshot based. The issue event must point to the approved snapshot hash, not to mutable live draft fields.
6. Lifecycle Model
6.1 Proposed Lifecycle States
DRAFT: Draft exists, source asset may still be incomplete.STRUCTURING: Source asset chosen and structuring work is underway.READY_FOR_REVIEW: Draft frozen for governance review; no hard blockers remain.APPROVED_FOR_ISSUE: Governance approval granted; economics and claim terms frozen.ISSUED: Issuance event recorded.SUSPENDED: Issuance is paused due to reserve breach, evidence lapse, insurance lapse, or governance hold.MATURED: Economic maturity reached.REDEEMED: Redemption completed.CLOSED: Administrative close-out complete.CANCELLED: Withdrawn before issue.
6.2 Proposed Transitions
| From | To | Trigger | Mandatory constraints |
| --- | --- | --- | --- |
| DRAFT | STRUCTURING | Source asset basis selected | source asset snapshot exists |
| STRUCTURING | READY_FOR_REVIEW | Mark ready for review | zero hard blockers; snapshot frozen |
| READY_FOR_REVIEW | STRUCTURING | Changes requested | reviewer note recorded |
| READY_FOR_REVIEW | APPROVED_FOR_ISSUE | Governance approval | separation of duties satisfied |
| APPROVED_FOR_ISSUE | ISSUED | Issue action executed | immutable snapshot hash matches approved record |
| ISSUED | SUSPENDED | Breach/lapse/policy hold | suspension reason recorded |
| SUSPENDED | ISSUED | Reinstatement approved | amendment/reinstatement evidence recorded |
| ISSUED | MATURED | Maturity reached | maturity rule satisfied |
| MATURED | REDEEMED | Redemption recorded | redemption evidence recorded |
| REDEEMED | CLOSED | Closure completed | archival/close-out record present |
| DRAFT or STRUCTURING or READY_FOR_REVIEW | CANCELLED | Cancellation recorded | actor authority and reason recorded |
6.3 Lifecycle Notes
- There is no direct edit path after
APPROVED_FOR_ISSUEorISSUED. - Post-issue changes must use append-only amendment flow rather than mutable field updates.
SUSPENDED,MATURED,REDEEMED, andCLOSEDbelong to later operational phases, but the states should exist now so issuance terms can be designed against them.
7. Governance / Approval Model
7.1 Who Can Start a Draft
Allowed initiators should be:
LAND_OWNERor other authorized asset originator when the asset basis belongs to them- internal
ISSUANCE_OPERATORacting on behalf of the originator - internal governance-support operator for correction/rescue scenarios
Not allowed:
- insurer, financier, or payment provider as unilateral draft originator for someone else's asset
7.2 Who Can Edit Structuring Fields
Editable by:
ISSUANCE_OPERATORRISK_OFFICERFINANCE_OPERATOR- role-specific contributors for insurance/factoring evidence uploads
Constraints:
- source asset selection and source-right linkage should not be editable by arbitrary finance-only roles
- approval actors must not silently rewrite structuring economics during approval
7.3 Who Can Approve Issuance
Minimum proposed separation of duties:
READINESS_REVIEWERverifies no blockers remainISSUANCE_APPROVERgrants issue approvalISSUE_EXECUTORrecords the issuance event against the approved snapshot
Additional constraint:
- the final approver must not be the same actor who finalized the linked inspection result when the issuance depends on that inspection
- the issue executor may be the issuance operator, but only after approval and without edit authority over frozen economics
7.4 What Must Be Immutable After Issue
After ISSUED, these fields must be immutable:
- source asset identifiers and capacity snapshot
- valuation basis and max issuable amount
- reserve floor and configured reserve ratio
- insurance configuration used in the issuance basis
- financing/factoring priority and assignment basis
- claim rights
- unit economics and total issuance amount
- maturity/redemption model
- transfer restrictions
- rules version, approvals, and snapshot hash
7.5 What Requires Amendment Flow Instead of Direct Edit
These must use amendment flow, never direct edit:
- change in source asset basis
- change in economics or supply
- change in claim rights or priority waterfall
- change in maturity or redemption mechanics
- change in reserve minimums
- change in insurance provider/coverage if it supported the original issue approval
- change in financing seniority or recourse terms
- any post-issue eligibility override
These may be append-only operational events instead of amendments:
- suspension reason
- reinstatement evidence
- redemption record
- close-out note
8. How This Fits Current Repo Direction
8.1 Parcels
The wizard should eventually consume parcel/land-owner truth as a source-asset resolver, not as free-form user input.
Implications:
- only verified/registry-backed parcel or productive-asset records should seed issuance
- parcel review style
missingRequirementsand gate logic should prefilter whether a parcel-origin issuance can even start - map linkage can remain a reference field, but map authoring should not be the primary issuance surface
8.2 Inspections / Checklists
Inspection is not an afterthought. It belongs in eligibility gating.
Implications:
- the wizard should reuse the repo's current checklist/readiness posture
- inspection outcomes should land as structured blockers/warnings, not as plain text notes
- inspection expiry and failure should influence readiness deterministically
8.3 Declarations
This wizard should reuse the declaration-backed mutation pattern already proven in the repo.
That means future implementation should follow:
- create draft
- update draft
- check readiness
- mark ready for review
- approve for issue
- issue/declare
- append event log
- update registry projection
This keeps issuance aligned with the current durable internal/admin write model rather than the repo's weaker public action surfaces.
8.4 Payments / Factoring
Factoring and financing should be declared now but executed later.
Implications:
- factoring configuration should point toward future use of settlement profiles, payment groups, allocation legs, and beneficiary/payer routing
- this batch must not dispatch payments or create settlement events
- the financial layer should remain advisory/shadow-only until upstream integration is intentionally activated
8.5 Reserve / Insurance Evidence
Reserve attestations and insurance binders should follow the same evidence-reference discipline already used in declaration flows:
- typed evidence groups
- hash/reference support
- visible readiness impact
- immutable linkage once approved/issued
8.6 Product Fit
This should launch first as an internal/admin governed wizard, not as a public self-service mint surface.
That matches the repo's strongest current truth:
- internal declaration flows are durable
- public action flows are still mixed in several domains
- issuance is too cross-domain and too risk-sensitive to start as a public action surface
9. What Should Be Implemented First After This Spec
- Add canonical issuance contracts in a core package, preferably
packages/coreor a newpackages/core/issuanceslice, using the DTOs above. - Implement a declaration-style readiness validator that emits
blockingErrors,warnings, andreadinessChecklistequivalents for issuance. - Add durable persistence for draft/event/projection seams, most safely adjacent to existing internal declaration ownership rather than in a brand-new service.
- Build source-asset resolvers for parcel truth, output allocations, declaration references, and inspection projections before building the full UI.
- Add evidence grouping for title/right basis, reserve proof, insurance proof, and financing assignment proof.
- Build the internal wizard shell in
apps/webby reusing current declaration wizard/readiness panel patterns. - Add approval transitions and immutable snapshot freezing before any issue action is exposed.
- Only after the above is stable, connect payment/settlement references and later optional chain-binding seams.
What should not be implemented first:
- blockchain adapters
- smart contract code
- public exchange or transfer flows
- public self-service mint UI
- automated legal/compliance inference
10. Final Verdict
ISSUANCE WIZARD SPEC READY
The spec is ready to guide implementation because the required seams are now explicit:
- source-asset truth
- checklist/readiness policy
- structuring DTOs
- reserve/insurance/financing support layers
- lifecycle and approval boundaries
- immutability and amendment rules
Known implementation risks remain, but they do not block the spec itself:
- parcel registry read/write truth is still not fully canonical across the product
- output allocation ownership is intentionally unresolved
- financing/factoring execution paths are less mature than their future seams
Those are implementation-order constraints, not reasons to revert to a direct mint form.