当 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 下载页面:
实践教程:
Mortise Spec Code Engine TypeScript/React 脚手架GitHub 地址:
Mortise Spec Code Engine(TypeScript/React)GitHub 地址: