MSC Engine 一个 AI 友好的 TypeScript React 脚手架

54 阅读19分钟

当 AI 能写代码后,真正的瓶颈不再是“能不能生成”,而是“能不能长期交付”。很多团队会发现:Demo 很快能跑起来,但一旦进入多人协作、持续迭代、上线与运维,项目就容易失控——改不动、测不全、没人敢动。

MortiseSpecCodeEngine(MSC Engine) 是一套 AI 友好的“代码胶水层”框架,专门解决这件事:把“工程可交付”的隐性经验,转成 AI 也能稳定遵守的显性结构与约束,让 AI 产出的代码从第一天就具备 可组合、可测试、可演进 的工程属性。


1. 什么是 MSC Engine

MSC Engine 是一套面向 Spec Coding 的组件化框架引擎。它通过统一的结构与约束,把前端开发从“页面里堆逻辑”,升级为“可被 AI 自动搭建、自动验证、自动组合”的标准化组件体系,让 AI 生成的代码真正进入长期交付。

它由五个关键层组成:

  • 基础组件(View / Logic) :分层明确,职责单一
  • 组件事件(ActionEvent / StateEvent) :统一交互与状态变更的表达
  • 流程编排(Workflow) :把事件流转从代码里抽出来,集中治理
  • 业务配置(DSL) :用配置装配业务模块,实现可复现、可热替换
  • 结果可验(Spec 指令) :把验证、回归、回写机制写进交付流程

一句话概括:

把 React 页面从“代码堆”升级为“可组装的业务模块”,让 AI 写出来的东西从第一天就能维护、能扩展、能协作。


2. 基础组件:边界清晰,有限泛化(View / Logic)

MSC Engine 的基础组件遵循一个原则:边界要清晰,泛化要可控。

因此我们将基础组件拆为两类:View 组件Logic 组件

|- msc-engine-component                         // 组件根目录
    |- action                                   // 发送事件(Action)相关目录
        |- MscEngineComponentActionKeys.ts      // 定义组件可发送事件的 key 枚举
        |- MscEngineComponentActionModel.ts     // 定义组件发送事件的数据结构
    |- state                                    // 接收事件(State)相关目录
        |- MscEngineComponentStateKeys.ts       // 定义组件可接收事件的 key 枚举
        |- MscEngineComponentStateModel.ts      // 定义组件接收事件的数据结构
    |- data                                     // 组件自定义数据目录
        |- MscEngineComponentCustomData.ts      // 定义组件自定义数据结构与类型
    // View 视图组件结构
    |- view                                     // 组件自定义 Brick/Layer/Virtual View 目录
        |- MscEngineComponentCustomView.tsx     // 组件自定义 Brick/Layer/Virtual View 实现代码
    |- MscEngineComponentBrick.tsx              // MSC Engine View 组件核心类(Brick/Layer/Virtual)
    |- MscEngineComponentView.tsx               // MSC Engine View 组件关联的 React 代码
    // Loigc 逻辑组件结构
    |- MscEngineComponentLogic.ts               // MSC Engine Logic 组件核心类

2.1 View:只负责 UI 结构与展示

View 的目标是把 GUI 拆成可组合单元,让布局与渲染结构 稳定、可复用、可替换

在 MSC Engine 中,View 进一步分为三类:

① Layer(层布局组件)

用于组织空间结构与分区,基于 FlexLayout 组装与管理子组件。

  • 解决:页面框架怎么搭
  • 价值:让布局成为“结构”,而不是散落在业务代码里的样式与拼接

② Virtual(虚拟布局组件)

自身不渲染实体 UI,负责管理与调度子组件(Dialog/浮层/动态插拔等)。

  • 解决:多层叠加导致层级过深、维护困难与性能问题
  • 价值:把“弹窗/浮层/动态区域”抽成独立调度能力,结构更轻、更可控

③ Brick(最小视图单元)

Brick 是最小视图组件,遵循三条硬规则:

  • 功能独立、边界清晰:一个 Brick 聚焦一个视图需求
  • 低耦合、可独立开发:可替换、可升级,不牵连全局
  • 标准接口通信:通过事件/协议通信,不直接依赖外部组件

Brick 的价值在于:把“页面”拆成“积木”,让它天然具备 可单测、可复用、可组合 的属性。

2.2 Logic:只负责数据处理与业务封装

Logic 组件承载数据处理与业务能力(数据库访问、网络请求、三方 SDK 调用等),遵循 RPD(需求驱动) 原则:可独立开发、迭代、测试,并沉淀为可复用业务模块。

一句话:View 不直接访问数据源(请求/存储/SDK),Logic 不触达 UI 渲染(不写组件/样式)。

这对 AI Coding 非常关键:边界一旦清楚,AI 就不容易把所有东西揉在一起,显著降低耦合带来的“屎山化”风险,让多人多智能体协作与长期迭代变得可控。


3. 事件机制:组件间协作的统一语言

组件化之后,核心问题变成:组件之间如何协作,而不是互相硬调用

MSC Engine 用两类事件统一协作方式:

  • ActionEvent(发起事件) :组件声明“我发生了什么 / 我想触发什么”
/**
 * MscEngineComponentActionKeys 组件发送事件
 * 1.继承发送事件父类 MscMortiseActionKeys
 * 2.使用标签 AnnMscActionKeysClass,表明是该文件是发送事件代码,MSC Engine 容器启动后,会通过 @AnnMscActionKeysClass 标签自动对组件事件进行注册
 **/
import {AnnMscActionKey, AnnMscActionKeysClass, MscMortiseActionKeys} from "@mortiseai/mai_msc_engine_ts_module";

@AnnMscActionKeysClass()
export class MscEngineComponentActionKeys extends MscMortiseActionKeys {

    /**
     * @AnnMscActionKey 标签
     * 参数1 acceptable: boolean ,必要参数,表示该事件是否可以通过 MSC 引擎实例让引擎外部进行调用,主要用于与老系统兼容
     * 参数2 action: string ,必要参数 ,事件名称,命名规则为全小写字符,蛇形命名
     * 参数3 model: string ,非必要参数 ,对应的发送事件数据对象文件名,主要用于数据校验和自动化测试
     * 参数4 mock: string ,非必要参数 ,对应的发送事件模拟数据对象文件名,主要用于自动化测试
     **/
    @AnnMscActionKey(false, "msc_engine_module_action_event", "MscEngineComponentActionModel", "MscEngineComponentActionMockModel")
    static MSC_ENGINE_MODULE_ACTION_EVENT = "msc_engine_module_action_event"

}
  • StateEvent(响应事件) :组件声明“我能响应什么 / 我关心哪些变化”
/**
 * MscEngineComponentStateKeys 组件接收事件
 * 1.继承接收事件父类 MscMortiseStateKeys
 * 2.使用标签 AnnMscStateKeysClass,表明是该文件是接收事件代码,MSC Engine 容器启动后,会通过 @AnnMscStateKeysClass 标签自动对组件事件进行注册
 **/
import {AnnMscStateKey, AnnMscStateKeysClass, MscMortiseStateKeys} from "@mortiseai/mai_msc_engine_ts_module";

@AnnMscStateKeysClass()
export class MscEngineComponentStateKeys extends MscMortiseStateKeys {

    /**
     * @AnnMscStateKey 标签
     * 参数1 acceptable: boolean ,必要参数,表示该事件是否可以通过 MSC 引擎实例让引擎外部进行监听,主要用于与老系统兼容
     * 参数2 operable: boolean ,必要参数,表示该事件是否可以通过 MSC 引擎实例让引擎外部进行调用,主要用于与老系统兼容
     * 参数3 state: string ,必要参数 ,事件名称,命名规则为全小写字符,蛇形命名
     * 参数4 model: string ,非必要参数 ,对应的发送事件数据对象文件名,主要用于数据校验和自动化测试
     * 参数5 mock: string ,非必要参数 ,对应的发送事件模拟数据对象文件名,主要用于自动化测试
     **/
    @AnnMscStateKey(false, false, "msc_engine_module_state_event", "MscEngineComponentStateModel")
    static MSC_ENGINE_MODULE_STATE_EVENT = "msc_engine_module_state_event"

}

好处是:组件不需要知道对方是谁,只要遵守事件协议,就能 解耦协作、可插拔组合,也更利于 AI 自动组装与自动回归验证。

兼容老系统:用事件做新老系统混合编程

在不可避免的老系统嵌入场景下,MSC Engine 通过事件规则声明与外部系统做混合编程,让老系统可以无缝兼容 Agent Coding 的能力:

  • acceptable:是否允许引擎外监听该事件
  • operable:是否允许引擎外调用/触发该能力

一句话: 构建可声明、可治理的事件边界,同时再不破坏老系统情况下,保证新能力接入后依然可控、可演进。


4. Workflow:把事件流转从“耦合”变成“编排”

组件化之后,真正容易失控的往往不是组件本身,而是 事件在组件之间怎么流转

/**
 * MscEngineComponentWorkflow 工作流组件
 * 1. 继承工作流组件基类 MscWorkflow。
 * 2. 实现 handleActionEvent(event: MscActionEvent) 方法,统一接收通过 DSL 配置的 View 和 Logic 组件的发送事件。
 * 3. 根据业务别名和事件类型,将收到的事件自动转换为目标组件的接收事件(支持无参和有参两种转换方式)。
 */
import {MscActionEvent, MscStateEvent, MscWorkflow} from "@mortiseai/mai_msc_engine_ts_module";

export class MscEngineComponentWorkflow extends MscWorkflow {

    // 统一处理 View/Logic 组件发送的业务事件
    handleActionEvent(event: MscActionEvent) {
        switch (event.getSender()) {
            // 监听业务别名
            case "MscEngineCaseBrick":
                this.handleMscEngineCaseBrick(event)
                break
            default:
                break
        }
    }

    // 将 View/Logic 组件的发送事件转换为对应接收事件,支持无参和有参事件
    handleMscEngineCaseBrick(event: MscActionEvent){
        switch (event.getMessage()) {
            // 无参事件转换
            case MscEngineCaseBrickActionKeys.MSC_ENGINE_BRICK_CASE_ACTION_ONE:
                const event1 = MscStateEvent.obtain()
                    .setSender(event.getSender())
                    .setReceiver("MscEngineCaseLogic")
                    .setMessage(MscEngineCaseLogicStateKeys.MSC_ENGINE_LOGIC_CASE_STATE_ONE)
                    .build()
                this.sendStateEventObj(event1)
                break
            // 有参事件转换
            case MscEngineCaseBrickActionKeys.MSC_ENGINE_BRICK_CASE_ACTION_TWO:
                const model2 = new MscEngineCaseLogicStateModel()
                model2.params1 = event.getModel().params1
                model2.params2 = [...event.getModel().params2]
                model2.params3 = {...event.getModel().params3}
                const event2 = MscStateEvent.obtain()
                    .setSender(event.getSender())
                    .setReceiver("MscEngineCaseLogic")
                    .setMessage(MscEngineCaseLogicStateKeys.MSC_ENGINE_LOGIC_CASE_STATE_TWO)
                    .setModel(model2)
                    .build()
                this.sendStateEventObj(event2)
                break
            default:
                break
        }
    }

}

Workflow 是 MSC Engine 的关键一环:它不参与 UI 渲染,也不承载业务逻辑,只专注做一件事:

监听 View/Logic 发出的 ActionEvent,并将其编排为目标组件可响应的 StateEvent。

因此带来两个结果:

  • View / Logic 只需要关心“发什么、收什么”,无需关心调用链与依赖关系
  • 事件流程集中在 Workflow 中统一治理:可视、可测、可修改、可回归

这让系统具备一种对 AI 更友好的工作方式:事件驱动 + 可编排。AI 调整的不再是“互相调用的代码”,而是“可治理的流程”。


5. DSL:用配置组装业务模块

最后一层,是把组件体系进一步产品化——让它从“可复用组件集合”,升级为可复现、可装配的业务模块

在 MSC Engine 中,规则很清晰:

每个 DSL 实例唯一对应一个业务模块(Module),两者一一映射。

模块不再依赖“某个人写出来的一堆文件”,而是由一份可读、可版本化、可生成的 DSL 来定义与复现。

DSL 分为四部分:

  • 初始化:bizId / bizName / 宽高 / layout / logic / workflow / ext …
/**
 * Mortise DSL 配置初始化
 * 
 * @function MscProvider.registryMscDsl(key: string, value: any)
 * @param {string} key - DSL 配置名称,与当前 DSL 文件名一致,与 MscEnv 环境文件下的 _dsl 数据保持一致。
 * @param {object} value - DSL 初始化配置对象。
 */
MscProvider.registryMscDsl("mai_dsl_name", {

    /** 
     * 通用基础功能
     **/
    // 业务 ID,必要,如 "10001"
    bizId: string | undefined,
    // 业务名称,必要,如 "mai_dsl_name",一般与当前 DSL 文件名一致
    bizName: string | undefined,
    // 页面宽度,非必要默认 1280
    scaleW: number = 1280,
    // 页面高度,非必要默认 720
    scaleH: number = 720,
    // 视图 DSL 配置,必要,不可重复,如:"mai_dsl_layout" 
    layout: string = "",
    // 逻辑 DSL 配置,必要,不可重复,如:"mai_dsl_logic"
    logic: string = "",
    // 工作流 DSL 配置,必要,不可重复,如:"mai_dsl_workflow"
    workflow: string = "",
    // 附加扩展数据,非必要
    ext: Map<string, any> | undefined,

    /** 
     * 高级扩展功能(Future)
     **/
    // 动态配置,设定 DSL 动态配置,非必要,默认 false
    modify: boolean = false,
    // 事件边车,非必要,不可重复,如:"mai_dsl_sidecar"
    sidecar: string = "",
    // 业务分析,非必要,不可重复,如:"mai_dsl_analysis"
    analysis: string = "",
    // 自动埋点,非必要,不可重复,如:"mai_dsl_trace"
    trace: string = "",
    // 智能监控,非必要,不可重复,如:"mai_dsl_monitor"
    monitor: string = "",
    // 开发调试,非必要,不可重复,如: "mai_dsl_develop"
    develop: string = ""

});
  • View Layout DSL:基于 Layer / Virtual / Brick 描述 Flex 布局与层级
/**
 * Mortise View Layout DSL 配置
 * 
 * @function MscProvider.registryMscDsl(key: string, value: any)
 * @param {string} key -  View Layout DSL 配置名称,与 Mortise DSL 初始化配置中的 layout 保持一致
 * @param {object} value - View Layout DSL 配置对象。
 */
MscProvider.registryMscDsl("mai_dsl_layout", [
    {
        // View 组件类型。可选:"layer"(层布局)、"virtual"(虚拟布局)、"brick"(基础视图组件)
        type: string,
        // View 组件在业务内的唯一别名。用于组件间事件流转。不可重复。
        name: string,
        // View 组件对应的文件名。需与 Mortise MscEvn 的 View 配置一致。
        view: string,
        // View 组件唯一业务ID。10000 ≤ soleId ≤ 99999。
        soleId: number,
        // View 组件初始展现形式。0:展示;1:隐藏;2:懒加载。
        status: number,
        // View 组件初始布局与位置信息
        attrs: {
            // 宽度设置。-1:100%;-2:自适应子组件宽度;其他:具体像素值。
            width: number,
            // 高度设置。-1:100%;-2:自适应子组件高度;其他:具体像素值。
            height: number,
            // 上间距,像素,默认 0。
            top: number,
            // 下间距,像素,默认 0。
            bottom: number,
            // 左间距,像素,默认 0。
            left: number,
            // 右间距,像素,默认 0。
            right: number,
            // View 组件类型为 “layer”(层布局)时,布局样式类型支持以下选项:"flex":FlexLayout 布局 ,"inline-block":行内块布局(可并排排列,且可设置宽高),"none":默认值,表示不设置特殊布局
            type: string,
            // FlexLayout 布局参数(type 为 "flex" 时启用)
            flex?: {
                "flex-direction"?: string,     // 主轴方向
                "flex-wrap"?: string,          // 换行方式
                "justify-content"?: string,    // 主轴对齐
                "align-items"?: string,        // 交叉轴对齐
                "align-content"?: string       // 多行对齐
            },
            // Z 轴深度,决定视图堆叠顺序。默认与 soleId 相同。
            zIndex?: number
        },
        // Layer 层或 Virtual 虚拟布局下的子组件数组,按渲染顺序排列。每个子项为完整的 View 组件对象。
        child?: Array<any>,
        // View 组件初始化数据。可选。
        data?: Map<string, any>,
        // View 组件附加数据。可选。
        ext?: Map<string, any>
    },
    ...
])
  • Logic DSL:逻辑组件注册与依赖声明
/**
 * Mortise Logic DSL 配置
 * 
 * @function MscProvider.registryMscDsl(key: string, value: any)
 * @param {string} key - Logic DSL 配置名称,与 Mortise DSL 初始化配置中的 logic 保持一致
 * @param {object} value - Logic DSL 配置对象。
 */
MscProvider.registryMscDsl("mai_dsl_logic", [
    {
        // 逻辑组件在业务中别名,不可重复,用于 MSC Engine 中组件与组件之间的事件发送与接收,一般与逻辑组件名称相同,如:"MaiDslLogic"
        name: string = "",
        // 逻辑组件文件名,与 Mortise MscEvn 中的 Logic 保持一致,如:"MaiDslLogic"
        logic: string = "",
        // 逻辑组件初始化数据,非必要
        data: Map<string, string> | undefined,
        // 逻辑组件附加数据,非必要
        ext: Map<string, string> | undefined
    },
    ...
])
  • Workflow DSL:事件监听、条件与流转编排(ActionEvent → StateEvent)
/**
 * Mortise Workflow DSL 配置
 * 
 * @function MscProvider.registryMscDsl(key: string, value: any)
 * @param {string} key - Workflow DSL 配置名称,与 Mortise DSL 初始化配置中的 workflow 保持一致
 * @param {object} value - Workflow DSL 配置对象。
 */
MscProvider.registryMscDsl("mai_main_dsl_workflow", [
    {
        // 工作流在业务中别名,不可重复,用于 MSC Engine 中组件与组件之间的事件流转
        name: string = "",
        // 工作流文件名,与 Mortise MscEvn 中的 Workflow 保持一致
        workflow: string = "",
        // 工作流监听的组件发送事件
        events: Array<MscWorkflow> = [
            {
                // 组件名称,视图 View 组件和逻辑 Logic 组件配置的业务别名,如:"MaiDslBrick" 或者 "MaiDslLogic"
                sender: string = "",
                // 组件发送事件名,如 ["mai_dsl_brick_action_event"]
                action: Array<string> | undefined   
            }
            ...
        ]
    },
    ...
])

带来的工程收益:

  • 结构可复现:同一份 DSL 在任何环境都能还原同一个模块
  • 模块可装配:支持动态加载、热替换、快速组装与灰度发布
  • AI 生成方式升级:AI 不再主要“写文件”,而是 生产 DSL + 组件,再由引擎组装成可交付模块

一句话: 用 DSL 把“写页面”变成“组模块”,让业务能力可复现、可装配、可演进。


6. 工程落地:Environment / Listener / Container / Page

为了让 MSC Engine 真正“能接、能跑、能维护”,我们提供了一套标准化集成方式,把能力落到工程结构里:

  • Environment(MscEnv) :统一注册与声明,注册 View / Logic / Workflow / DSL,并集中管理 ActionKeys / StateKeys 等协议与枚举,让模块边界与能力清单可追溯、可治理。
/**
 * MSC Engine 环境
 * 
 * 1.实现环境接口 IMscEnv
 * 2.MscBrick/MscLayer/MscVirtual 名称固定,为兜底默认 View 组件,当 MSC Engine 为找到对应 View 组件时,会自动加载兜底默认 View 组件进行占位,防止部署错乱,可以自定义兜底样式
 * 
 **/ 
import {IMscEnv} from "@mortiseai/mai_msc_engine_ts_module";
import {MscBrick} from "../common/view/MscBrick";
import {MscLayer} from "../common/view/MscLayer";
import {MscVirtual} from "../common/view/MscVirtual";
import {mai_case_dsl} from "../../project/mai-case-module/dsl/mai_case_dsl";
import {MaiCaseBrick} from "../../project/mai-case-module/view/mai-case-brick/MaiCaseBrick";
import {MaiCaseBrickActionKeys} from "../../project/mai-case-module/view/mai-case-brick/action/MaiCaseBrickActionKeys";
import {MaiCaseBrickStateKeys} from "../../project/mai-case-module/view/mai-case-brick/action/MaiCaseBrickStateKeys";
import {MaiCaseLogic} from "../../project/mai-case-module/logic/mai-case-logic/MaiCaseLogic";
import {MaiCaseLogicActionKeys} from "../../project/mai-case-module/logic/mai-case-logic/action/MaiCaseLogicActionKeys";
import {MaiCaseLogicStateKeys} from "../../project/mai-case-module/logic/mai-case-logic/action/MaiCaseLogicStateKeys";
import {MaiCaseWorkflow} from "../../project/mai-case-module/workflow/MaiCaseWorkflow";

export class MscEvn implements IMscEnv {

	private _dsl: any = {
		/** case **/
		 mai_case_dsl,
	}

	private _view: any = {
		/** common **/
        MscBrick,
        MscLayer,
        MscVirtual,
		/** case **/
		MaiCaseBrick,
	}

	private _logic: any = {
		/** case **/
		MaiCaseLogic,
	}

	private _workflow: any = {
		/** case **/
		MaiCaseWorkflow,
	}

	private _actionKeys: any = {
		/** case **/
		MaiCaseBrickActionKeys,
		MaiCaseLogicActionKeys,	
	}

	private _stateKeys: any = {
		/** case **/
		MaiCaseBrickStateKeys,
		MaiCaseLogicStateKeys,
	}

	// 业务应用ID
    appId(): string {
        return "";
    }

	// 通过视图 View 组件文件名获取 View 组件,对应视图 View 组件 DSL 配置的 view 名称
    view(key: string): any {
        return this._view[key]
    }

	// 通过逻辑 Logic 组件文件名获取 Logic 组件,对应逻辑 Logic 组件 DSL 配置的 logic 名称
    logic(key: string): any {
        return this._logic[key]
    }

	// 通过工作流 Workflow 组件文件名获取 Workflow 组件,对应工作流组件 Workflow 设置 DSL 配置的 workflow 名称
    workflow(key: string): any {
        return this._workflow[key]
    }
    
    // 通过 DSL 文件名获取 DSL 数据对象,对应 Mortise DSL 初始化配置的 DSL 配置名称
    dsl(key: string): any {
        return this._dsl[key]
    }

	// 通过 View 或者 Logic 组件发送事件文件名,获取组件发送事件数据
    actionKeys(key: string): any {
        return this._actionKeys[key]
    }

	// 通过 View 或者 Logic 组件接收事件文件名,获取组件接收事件数据
    stateKeys(key: string): any {
        return this._stateKeys[key]
    }

    sidecarClass(key: string): any {
    }

}
  • Listener:对外可观测与可接入,捕获 Action / State / 生命周期 / 错误等事件,用于埋点、日志、监控、联动与兼容扩展。
import {MscActionEvent, MscStateEvent, MscLifecycleEvent, MscErrorEvent} from "@mortiseai/mai_msc_engine_ts_module";

/**
 * MSC Engine 监听
 */
export interface MscEngineListener {

    /**
     * MSC View/Logic/Workflow 发送 Action 事件通知,默认容器外不可接收,需设置 AnnMscActionKey 注解参数 acceptable == true,
     * @param event
     */
    onActionEvent(event: MscActionEvent): void

    /**
     * MSC View/Logic/Workflow 接收 State 事件通知,默认容器外不可接收,需设置 AnnMscStateKey 注解参数 acceptable == true,
     * @param event
     */
    onStateEvent(event: MscStateEvent): void

    /**
     * MSC 容器声明周期,默认可接收,参考 MscLifecycleConstants 常量
     * 1.MSC容器 开始加载 MSC_LIFECYCLE_DLC_CONTAINER_WILL_MOUNT : msc_container_will_mount
     * 2.MSC容器 加载完成 MSC_LIFECYCLE_DLC_CONTAINER_DID_MOUNT : msc_container_did_mount
     * 3.MSC容器 开始卸载 MSC_LIFECYCLE_DLC_CONTAINER_WILL_UNMOUNT : msc_container_will_unmount
     * 4.MSC容器 卸载完成 MSC_LIFECYCLE_DLC_CONTAINER_DID_UNMOUNT : msc_container_did_unmount
     * 5.MSC容器 切换后台 MSC_LIFECYCLE_DLC_CONTAINER_BACK : msc_container_back
     * 6.MSC容器 恢复前台 MSC_LIFECYCLE_DLC_CONTAINER_FRONT : msc_container_front
     * @param event
     */
    onLifecycleEvent(event: MscLifecycleEvent): void

    /**
     * MSC Error 事件通知
     * @param event
     */
    onErrorEvent(event: MscErrorEvent): void

}
  • Container:模块运行时容器,负责实例化、安装/卸载、事件分发、DSL 动态加载等运行时能力,是模块化交付的核心承载。
import React, {useEffect, useRef, useState} from 'react';
import {MscActionEvent, MscBuilder, MscInstance, MscLifecycleEvent, MscStateEvent, MscProvider, MscErrorEvent} from "@mortiseai/mai_msc_engine_ts_module";
import {MscEvn} from "../env/MscEvn";

/**
 * MSC Engine 容器
 * 可以通过 MscInstance 向引擎内组件发送 MscMortiseStateKeys 事件,实现与已有旧系统兼容,需设置 AnnMscStateKey 注解参数 operable == true
 * MscInstance.sendState(stateEvent: any): void;
   MscInstance.sendState({
                sender: "string|发送组件别名",
                receiver: "string|接收组件别名",
                message: "string|接收事件名称",
                model: "MscMortiseStateModel|接收事件数据模型"
            })
 * @param props
 * @constructor
 */
export function MscEngineContainer(props: any) {

    const [dsl, setDsl] = useState<string>()

    const [debug, seDebug] = useState(false)

    let mscInstance = useRef<MscInstance | null>()

    let mscDom = useRef<any>()

    useEffect(function () {

        if (!window.onerror) {
            window.onerror = function (err) {
                if(err === 'ResizeObserver loop limit exceeded') {
                    console.warn('Ignored: ResizeObserver loop limit exceeded');
                    return true;
                }
            }
        }
        if (!MscProvider.isRegistryMscEnv()) {
            MscProvider.registryMscEnv(new MscEvn())
        }

        if (props.debug) {
            seDebug(props.debug)
        }

        if (dsl != props.dsl) {
            handleDsl(props.dsl)
        }

        return () => {
            if (mscInstance.current) {
                mscInstance.current.uninstall()
                mscInstance.current = null
            }
            if (window.onerror) {
                window.onerror = null
            }
        }

    }, [props])

    const handleDsl = (dls: string) => {
        new Promise<string>(function (resolve) {
            const loadDslFun = MscProvider.MscEnv().dsl(dls)
            if (loadDslFun) {
                loadDslFun()
                resolve(dls)
            }
        }).then(function (dls) {
            setDsl(`${dls}`)
        })
    }

    const handleDom = () => {
        if (!mscInstance.current && dsl) {
            mscInstance.current = new MscBuilder(debug).dls(dsl).observer({
                handleActionEvent(event: MscActionEvent) {
                    props.listener?.onActionEvent(event)
                },
                handleStateEvent(event: MscStateEvent) {
                    props.listener?.onStateEvent(event)
                },
                handleLifecycleEvent(event: MscLifecycleEvent) {
                    props.listener?.onLifecycleEvent(event)
                },
                handleErrorEvent(event: MscErrorEvent) {
                    props.listener?.onErrorEvent(event)
                }
            }).build()
            if (props.onMscInstanceCallBack) {
                props.onMscInstanceCallBack(mscInstance.current)
            }
        }
        if (!mscDom.current) {
            mscDom.current = mscInstance.current?.install()
        }
        return mscDom.current
    }

    return (
        <div style={{width: "100vw", height: "100vh"}}>
            {dsl ? handleDom() : null}
        </div>
    )

}
  • Page:快速启动入口,完成 DSL 传入与监听绑定,让业务模块可即插即用。
import React from 'react';
import ReactDom from 'react-dom';
import {useLocation} from 'react-router-dom'
import {MscEngineContainer} from "./MscEngineContainer";
import {MscEngineListener} from "./MscEngineListener";
import {MscActionEvent, MscErrorEvent, MscLifecycleEvent, MscStateEvent} from "@mortiseai/mai_msc_engine_ts_module";

/**
 * MSC Engine 页面
 * @constructor
 */
function MscEnginePage(props: any) {
	
    const location = useLocation()

    //实例化 MSC Engine 监听,实现与已有旧系统兼容
    const listener: MscEngineListener = {
        onActionEvent(event: MscActionEvent): void {
            console.log("MscEnginePage",`ActionEvent >>> ${JSON.stringify(event)}`)
        },
        onStateEvent(event: MscStateEvent): void {
            console.log("MscEnginePage",`StateEvent >>> ${JSON.stringify(event)}`)
        },
        onLifecycleEvent(event: MscLifecycleEvent): void {
            console.log("MscEnginePage",`LifecycleEvent >>> ${JSON.stringify(event)}`)
        },
        onErrorEvent(event: MscErrorEvent): void {
            console.log("MscEnginePage",`ErrorEvent >>> ${JSON.stringify(event)}`)
        }
    }

    return (
        <MscEngineContainer debug={location.state.debug} dsl={location.state.dsl} listener={listener}/>
    );
    
}

ReactDom.render(
    <MscEnginePage/>,
    document.getElementById('root')
);

覆盖两类落地场景:

  • 新系统:按模块化方式直接构建,组件 → DSL → 模块装配一路贯通
  • 旧系统兼容:以 Container 实例化方式嵌入老系统,不大改架构也能渐进迁移

7. Spec 指令:让结果可验证、过程可追溯

很多 Spec Coding 的失败点,并不在 0–1 能不能写出来,而在 1–100 的长期迭代里:能不能持续用 Spec 文档驱动演进。它体现在两条必经之路上:

  • 从全局开发走向局部迭代(只改一块,不牵一身)

  • 从 Doc → Code 的生成,走向 Code → Doc 的逆向更新

MSC Engine 的 Spec 指令体系,本质是在工程侧把这件事做成“机制”。核心不是教 AI 怎么写,而是构建一套 可执行、可回归、可审计 的交付链路,让每一次产出都能验证、每一次修改都能追溯。

它主要通过两件事实现:

  • 结构先行,支持局部迭代:拆成子 Spec 文档 / 子组件 ,让改动影响压缩在有限空间
  • Doc ↔ Code 对齐,支持双向演进:基于有限泛化的组件体系与映射规则,支持逆向回写、差异对比与一致性校验,确保文档不会写完就过时

换句话说: Spec 指令,就是把“结果可信”写进流程里:AI 不能靠“硬凑能跑”,而必须做到 —— 按结构产出 → 按指令自检 → 按结果回写。


8. 为什么这套脚手架“AI 友好”

所谓 AI 友好,不是让 AI 更会写,而是让它的产出天然处在一个可控、可验证、可演进的工程环境里。MSC Engine 把工程经验从“人脑默认规则”,变成“系统强制结构”,因此更适合 AI 长期稳定工作。

  • 边界清晰,减少歧义:View/Logic 分离,事件驱动协作
  • 结构稳定,生成目标明确:Layer/Virtual/Brick + Workflow,让生成对象更确定
  • 组合优先,避免全局修改:组件 + DSL 装配,局部改动不依赖全局上下文
  • 规则可声明、可治理、可兼容:acceptable/operable 明确边界,新老系统可共存
  • 结果可验证,过程可追溯:按结构产出 → 自检 → 回写,持续对齐 Doc 与 Code

一句话总结:

MSC Engine 的 “AI 友好”,是让 AI 从“写得快”升级为“交付得稳”——结构清晰、流程可编排、产出可验证、演进可追溯。


结语:让 AI 写得快,更要写得久

AI 时代的工程胜负,不在于谁生成得更快,而在于谁能把“可交付”变成默认属性。MSC Engine 的目标,是用 组件化 + 事件机制 + Workflow 编排 + DSL 组装,把 React 工程从“页面代码”升级为“模块化交付”——结构可复现、协作可治理、迭代可回归

让 AI 不止会“写代码”,更能稳定交付模块、持续演进

而 MortiseAI,则基于 Mortise Spec Code Engine,面向 AI Work Manager(用自然语言管理 AI,并将经验沉淀为可复用资产)打造一个 AI 友好的文档驱动开发平台,让交付从一开始就具备可验证、可复用、可演进的工程确定性。

立即体验 MortiseAI

MortiseAI 预览版公测中,免费免激活,仅支持 Mac 和 React 编程语言。

 

MortiseAI 下载页面:

mortiseai.com

实践教程:

mortiseai.com/wiki

Mortise Spec Code Engine TypeScript/React 脚手架GitHub 地址:

github.com/MortiseAI/m…

Mortise Spec Code Engine(TypeScript/React)GitHub 地址:

github.com/MortiseAI/m…