M1: 底座 —— @contracts 首版 + event / ledger / org / llm

@contracts 首版(§8.1 九个文件):
- id 品牌 ID / terminal 终态五名与归侧四标签 / envelope 信封六字段与四类事件
- card 工单两模式 + 对话卡族(形状对齐 2026-07-26 原型定稿)
- contract7 契约七件 / ledger 三级寻址与写三分 / process-package 流程包 DSL 与触发器三类
- manifest 活动包清单六区(含 CMMN·DMN·步序声明的资产形状)/ org / api 路由契约

模块(L1):
- event   信封存储、归属解析三级短路、投递五诺、监听注册表、三级消费裁定
- ledger  schema 自生长(最大化合并/最宽类型/只增不破坏)、任务视图叠加、写三分、冲突检查
- org     岗位授权(客体三类×动作三种)、停用前置检查、会话、种子播种
- llm     四元绑定、ModelGateway 三模式(live/record/replay)—— 两缝之一

app 装配:Hono 服务 + 鉴权中间件 + 治理台/工作台路由;drizzle 迁移;AES-256-GCM 凭证密封

ADR-007 工具链与门禁 · ADR-008 投递队列 · ADR-009 凭证加密
(ADR 编号自 007 续:编码规范 §14 已用到 006,README 写「从 005 续」是笔误)

门禁五道全绿;96 个测试通过,含对真实 PG 的 ledger 集成测试

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
JCAI Main Agent
2026-07-27 00:59:14 +08:00
co-authored by Claude Opus 5
parent 7cd664af3a
commit 14d4fa046a
120 changed files with 15236 additions and 78 deletions
+137
View File
@@ -0,0 +1,137 @@
/**
* DI 装配 —— `app/` 是**唯一组装层**(编码规范 §5.2)。
* 只有这里能同时认识多个模块:new 出 adapter、注入 port、把用例接起来。
*
* 跨层协作一律经端口注入:`ledger` 要发数据事件 → 注入 `event` 的出口;
* `event` 的三级消费下游(trigger L7 / message L2)在其**上**,同样经端口注入。
*/
import { Pool } from 'pg';
import { drizzle } from 'drizzle-orm/node-postgres';
import type { Db } from '@shared/db.ts';
import type { EventService, MessageFallback, TriggerMatcher } from '@modules/event';
import { createEventService, createEventStore } from '@modules/event';
import type { LedgerService } from '@modules/ledger';
import { createDraftStore, createLedgerService, createLedgerStore, DRAFT_DDL } from '@modules/ledger';
import type { OrgService, InFlightWorkProbe } from '@modules/org';
import {
createGrantRepository,
createOrgService,
createPositionRepository,
createStaffRepository,
} from '@modules/org';
import type { LlmService, ModelGateway } from '@modules/llm';
import {
createLlmInstanceRepository,
createLlmService,
createModelGateway,
createProviderInvoker,
createReplayStore,
} from '@modules/llm';
import type { AppConfig } from './config.ts';
import { createIdGenerator, systemClock } from './runtime.ts';
import { createSecretBox, loadSecretKey } from './secret-box.ts';
export type Container = {
readonly config: AppConfig;
readonly pool: Pool;
readonly db: Db;
readonly org: OrgService;
readonly ledger: LedgerService;
readonly event: EventService;
readonly llm: LlmService;
readonly modelGateway: ModelGateway;
close(): Promise<void>;
};
/**
* M1 的占位实现 —— 在途工作探针要问的是 task(L6)与 human-tool(L3),二者随 M3/M4 落地。
* 占位返回空集,停用检查因此在 M1 一律放行;M4 起接真实用例。
*/
const emptyInFlightProbe: InFlightWorkProbe = {
openTasksOf: async () => [],
openCardsOf: async () => [],
openApprovalsOf: async () => [],
};
/** M1 的占位实现 —— 触发器(L7)随 M5 落地,当下恒不命中 */
const noTriggerMatcher: TriggerMatcher = { match: async () => [] };
/** M1 的占位实现 —— 消息(L2)随 M2 落地;在此之前无主事件只落日志,不静默丢弃 */
function createConsoleMessageFallback(): MessageFallback {
return {
async deadLetter(envelope, error) {
process.stderr.write(`[死信] event=${envelope.id} type=${envelope.type} error=${error}\n`);
},
async unowned(envelope, fallbackPosition) {
process.stderr.write(
`[无主兜底] event=${envelope.id} type=${envelope.type} 认领岗位=${fallbackPosition ?? '未配置'}\n`,
);
},
};
}
/** S-FEEL 求值 —— @sfeel 随 M3 落地;M1 的条件监听一律按「无条件」处理 */
const passthroughEvaluator = (): boolean => true;
export async function createContainer(config: AppConfig): Promise<Container> {
const pool = new Pool({ connectionString: config.databaseUrl, max: 10 });
const db = drizzle(pool);
const ids = createIdGenerator();
const secrets = createSecretBox(loadSecretKey());
// 草稿层的建表:ledger schema 的唯一写者是 ledger adapter,DDL 在其出口给出
await pool.query(DRAFT_DDL);
const event = createEventService({
store: createEventStore(db, ids),
clock: systemClock,
ids,
evaluate: passthroughEvaluator,
triggers: noTriggerMatcher,
messages: createConsoleMessageFallback(),
});
const ledger = createLedgerService({
store: createLedgerStore(pool),
drafts: createDraftStore(pool),
events: { publish: async (envelope) => void (await event.publish(envelope)) },
clock: systemClock,
ids,
});
const org = createOrgService({
positions: createPositionRepository(db),
staff: createStaffRepository(db),
grants: createGrantRepository(db),
inFlight: emptyInFlightProbe,
ids,
});
const instances = createLlmInstanceRepository(db);
const provider = createProviderInvoker();
const llm = createLlmService({ instances, provider, secrets, clock: systemClock, ids });
const modelGateway = createModelGateway({
instances,
provider,
replay: createReplayStore(db),
secrets,
clock: systemClock,
mode: 'live',
});
return {
config,
pool,
db,
org,
ledger,
event,
llm,
modelGateway,
async close() {
await pool.end();
},
};
}