Archived
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:
co-authored by
Claude Opus 5
parent
7cd664af3a
commit
14d4fa046a
@@ -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();
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user