@siming-org/core 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1294,24 +1294,26 @@ type TaskRepo = Repo<Task> & {
1294
1294
  declare function createTaskRepo(db: Db): TaskRepo;
1295
1295
 
1296
1296
  /**
1297
- * Issue 相关 Schemas(T202609150002 问题池)
1298
- * 小问题轻量跟进:入池 → 整理(合并/拆分)→ 转任务(多对多)。
1297
+ * Issue 相关 Schemas(T202609150002 问题池;T202609200001 直接标记完成 + 审批原话)
1298
+ * 小问题轻量跟进:入池 → 整理(合并/拆分)→ 转任务(多对多)或直接标记完成。
1299
1299
  *
1300
- * 状态语义(PRD 3.2 四态,终态禁止回转):
1301
- * - pending 待整理 / partial 部分转任务(存在未覆盖遗留)/ converted 已转任务 / cancelled 已取消
1300
+ * 状态语义(PRD 3.2 四态基础上 T202609200001 增 done,终态禁止回转):
1301
+ * - pending 待整理 / partial 部分转任务(存在未覆盖遗留)/ converted 已转任务 / cancelled 已取消 / done 已直接标记完成
1302
+ * - done:无需转任务、在某个任务/改动中顺带解决时的人工收尾终态(taskLinks 保留——已建立关联时进度可读)
1302
1303
  * - 任务后续演进(完成/取消)不触碰问题状态,进度是读时聚合(taskLinks × tasks.status)
1303
1304
  *
1304
1305
  * 关联模型(D1):单一真相源在 Issue 侧(taskLinks 嵌入数组),Task 零改动;
1305
1306
  * 多对多,同项目内组合;取消问题时 taskLinks 终止条目拷入 history(D8 结构化追溯)。
1306
1307
  */
1307
- /** 池内状态(终态 = converted / cancelled,状态机保护见 issue.repo) */
1308
- declare const ISSUE_STATUSES: readonly ["pending", "partial", "converted", "cancelled"];
1308
+ /** 池内状态(终态 = converted / cancelled / done,状态机保护见 issue.repo) */
1309
+ declare const ISSUE_STATUSES: readonly ["pending", "partial", "converted", "cancelled", "done"];
1309
1310
  type IssueStatus = (typeof ISSUE_STATUSES)[number];
1310
1311
  declare const IssueStatusSchema: z.ZodEnum<{
1311
1312
  pending: "pending";
1312
1313
  cancelled: "cancelled";
1313
1314
  partial: "partial";
1314
1315
  converted: "converted";
1316
+ done: "done";
1315
1317
  }>;
1316
1318
  /** 可参与转出/取消的非终态集合(事务内条件更新的并发安全带) */
1317
1319
  declare const ISSUE_ACTIVE_STATUSES: readonly ["pending", "partial"];
@@ -1360,12 +1362,14 @@ declare const IssueHistoryEntrySchema: z.ZodObject<{
1360
1362
  cancelled: "cancelled";
1361
1363
  partial: "partial";
1362
1364
  converted: "converted";
1365
+ done: "done";
1363
1366
  }>>;
1364
1367
  to: z.ZodOptional<z.ZodEnum<{
1365
1368
  pending: "pending";
1366
1369
  cancelled: "cancelled";
1367
1370
  partial: "partial";
1368
1371
  converted: "converted";
1372
+ done: "done";
1369
1373
  }>>;
1370
1374
  taskId: z.ZodOptional<z.ZodString>;
1371
1375
  reason: z.ZodOptional<z.ZodString>;
@@ -1388,7 +1392,9 @@ declare const IssueSchema: z.ZodObject<{
1388
1392
  cancelled: "cancelled";
1389
1393
  partial: "partial";
1390
1394
  converted: "converted";
1395
+ done: "done";
1391
1396
  }>>;
1397
+ approved: z.ZodOptional<z.ZodString>;
1392
1398
  taskLinks: z.ZodDefault<z.ZodArray<z.ZodObject<{
1393
1399
  taskId: z.ZodString;
1394
1400
  mode: z.ZodEnum<{
@@ -1410,12 +1416,14 @@ declare const IssueSchema: z.ZodObject<{
1410
1416
  cancelled: "cancelled";
1411
1417
  partial: "partial";
1412
1418
  converted: "converted";
1419
+ done: "done";
1413
1420
  }>>;
1414
1421
  to: z.ZodOptional<z.ZodEnum<{
1415
1422
  pending: "pending";
1416
1423
  cancelled: "cancelled";
1417
1424
  partial: "partial";
1418
1425
  converted: "converted";
1426
+ done: "done";
1419
1427
  }>>;
1420
1428
  taskId: z.ZodOptional<z.ZodString>;
1421
1429
  reason: z.ZodOptional<z.ZodString>;
@@ -1428,10 +1436,13 @@ type Issue = z.infer<typeof IssueSchema>;
1428
1436
  /**
1429
1437
  * 录入输入:source/projectId 由服务端按通道/项目解析注入,不入客户端 schema
1430
1438
  * (source 是统计信息非权限依据;projectId 缺省按 key=default 解析)。
1439
+ * approved(T202609200001):人类审批原话逐字(1-500 字);AI 通道(mcp/cli)强制在 server 路由层
1440
+ * (source 感知),schema 保持通道中立——Web 录入无此字段。
1431
1441
  */
1432
1442
  declare const IssueCreateSchema: z.ZodObject<{
1433
1443
  title: z.ZodString;
1434
1444
  description: z.ZodOptional<z.ZodString>;
1445
+ approved: z.ZodOptional<z.ZodString>;
1435
1446
  }, z.core.$strip>;
1436
1447
  type IssueCreate = z.infer<typeof IssueCreateSchema>;
1437
1448
  /** 编辑输入(独立 object 不派生——Zod4 UpdateSchema 范式;任意状态可编辑,终态无副作用) */
@@ -1473,8 +1484,8 @@ type TaskIssueView = {
1473
1484
  };
1474
1485
 
1475
1486
  /**
1476
- * Issue 仓储(T202609150002)
1477
- * 状态机保护在此层:终态(converted/cancelled)禁止 cancel/convert 关联写入——
1487
+ * Issue 仓储(T202609150002;T202609200001 直接标记完成 done)
1488
+ * 状态机保护在此层:终态(converted/cancelled/done)禁止 cancel/convert/markDone 写入——
1478
1489
  * 条件更新双保险(filter 携带 status ∈ {pending, partial},命中 0 行即并发终态)。
1479
1490
  * 进度计算两步批查(D2:不用 $lookup——Zod parse 单源 + 一次 $in 往返 + 内存映射)。
1480
1491
  */
@@ -1511,6 +1522,8 @@ type IssueRepo = Repo<Issue> & {
1511
1522
  getByIssueId(issueId: string): Promise<Issue | null>;
1512
1523
  updateIssue(issueId: string, patch: IssueUpdate): Promise<Issue>;
1513
1524
  cancelIssue(issueId: string, reason?: string): Promise<Issue>;
1525
+ /** 直接标记完成(T202609200001):终态三值互斥;taskLinks 保留(D3),note 落 history reason */
1526
+ markDone(issueId: string, note?: string): Promise<Issue>;
1514
1527
  /** 列表(进度聚合 + view=open 过滤;两支合并 createdAt 降序统一切片) */
1515
1528
  listIssues(filter?: IssueListFilter): Promise<{
1516
1529
  items: IssueWithProgress[];
package/dist/index.js CHANGED
@@ -157,7 +157,8 @@ var BIZ_CODE_MESSAGES = {
157
157
  UPGRADE_DECISION_REQUIRED: "\u672C\u5730\u5DF2\u4FEE\u6539\u7684\u8282\u70B9\u5FC5\u987B\u663E\u5F0F\u51B3\u7B56\uFF08upgrade/keep/skip\uFF09",
158
158
  UPGRADE_STALE: "\u6A21\u677F\u72B6\u6001\u4E0E\u5347\u7EA7\u8BA1\u5212\u4E0D\u7B26\uFF08\u8BA1\u5212\u540E\u6A21\u677F\u5DF2\u88AB\u7F16\u8F91\uFF09\uFF0C\u8BF7\u91CD\u65B0\u83B7\u53D6\u5347\u7EA7\u8BA1\u5212",
159
159
  FLOW_MANIFEST_INVALID: "\u8282\u70B9\u5E93 manifest \u683C\u5F0F\u6216\u7248\u672C\u4E0D\u652F\u6301",
160
- // T202609150002:问题池域(message 由路由层按状态差异化——已转任务携带关联任务标识 / 已取消提示废弃)
160
+ // T202609150002:问题池域(终态含 done 自身,T202609200001;message 由路由层按状态差异化——
161
+ // 已转任务携带关联任务标识 / 已取消提示废弃 / done 提示不可再次终态)
161
162
  ISSUE_NOT_FOUND: "\u95EE\u9898\u4E0D\u5B58\u5728",
162
163
  ISSUE_INVALID_TRANSITION: "\u95EE\u9898\u5DF2\u662F\u7EC8\u6001\uFF0C\u7981\u6B62\u8BE5\u64CD\u4F5C\uFF08\u7EC8\u6001\u65E0\u56DE\u8F6C\uFF09",
163
164
  ISSUE_PROJECT_MISMATCH: "\u95EE\u9898\u4E0E\u76EE\u6807\u9879\u76EE\u5F52\u5C5E\u4E0D\u4E00\u81F4\uFF0C\u7981\u6B62\u8DE8\u9879\u76EE\u7EC4\u5408\u8F6C\u51FA",
@@ -2063,7 +2064,7 @@ function generateEntryId(existing) {
2063
2064
 
2064
2065
  // src/schemas/issue.ts
2065
2066
  import { z as z5 } from "zod";
2066
- var ISSUE_STATUSES = ["pending", "partial", "converted", "cancelled"];
2067
+ var ISSUE_STATUSES = ["pending", "partial", "converted", "cancelled", "done"];
2067
2068
  var IssueStatusSchema = z5.enum(ISSUE_STATUSES);
2068
2069
  var ISSUE_ACTIVE_STATUSES = ["pending", "partial"];
2069
2070
  var ISSUE_SOURCES = ["mcp", "cli", "web"];
@@ -2095,6 +2096,8 @@ var IssueSchema = z5.object({
2095
2096
  source: IssueSourceSchema,
2096
2097
  projectId: z5.string().regex(OBJECT_ID_HEX, "projectId must be a valid ObjectId hex"),
2097
2098
  status: IssueStatusSchema.default("pending"),
2099
+ /** 人类审批原话(T202609200001:AI 通道 mcp/cli 录入必须携带用户批准时的原文逐字,供事后审计;Web 豁免——人工录入天然即批准) */
2100
+ approved: z5.string().min(1).max(500).optional(),
2098
2101
  taskLinks: z5.array(TaskLinkSchema).default([]),
2099
2102
  history: z5.array(IssueHistoryEntrySchema).default([]),
2100
2103
  createdAt: z5.date().optional(),
@@ -2102,7 +2105,8 @@ var IssueSchema = z5.object({
2102
2105
  });
2103
2106
  var IssueCreateSchema = z5.object({
2104
2107
  title: z5.string().min(1).max(200),
2105
- description: z5.string().max(1e4).optional()
2108
+ description: z5.string().max(1e4).optional(),
2109
+ approved: z5.string().min(1).max(500).optional()
2106
2110
  });
2107
2111
  var IssueUpdateSchema = z5.object({
2108
2112
  title: z5.string().min(1).max(200).optional(),
@@ -2145,6 +2149,8 @@ function createIssueRepo(db) {
2145
2149
  issueId,
2146
2150
  title: data.title,
2147
2151
  ...data.description !== void 0 ? { description: data.description } : {},
2152
+ // 审批原话(T202609200001):AI 通道强制已在 server 路由层校验,repo 仅透传落库
2153
+ ...data.approved !== void 0 ? { approved: data.approved } : {},
2148
2154
  source,
2149
2155
  projectId,
2150
2156
  status: "pending",
@@ -2179,7 +2185,7 @@ function createIssueRepo(db) {
2179
2185
  if (!current) {
2180
2186
  throw new NotFoundError("issue", issueId, { bizCode: "ISSUE_NOT_FOUND" });
2181
2187
  }
2182
- if (current.status === "converted" || current.status === "cancelled") {
2188
+ if (current.status === "converted" || current.status === "cancelled" || current.status === "done") {
2183
2189
  throw new ConflictError("issue", issueId, {
2184
2190
  bizCode: "ISSUE_INVALID_TRANSITION",
2185
2191
  message: `\u95EE\u9898\u5DF2\u662F\u7EC8\u6001\uFF08${String(current.status)}\uFF09\uFF0C\u7981\u6B62\u53D6\u6D88`
@@ -2220,7 +2226,45 @@ function createIssueRepo(db) {
2220
2226
  if (!result) {
2221
2227
  throw new ConflictError("issue", issueId, {
2222
2228
  bizCode: "ISSUE_INVALID_TRANSITION",
2223
- message: "\u95EE\u9898\u5DF2\u88AB\u5E76\u53D1\u5904\u7406\uFF08\u8F6C\u51FA/\u53D6\u6D88\uFF09\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5"
2229
+ message: "\u95EE\u9898\u5DF2\u88AB\u5E76\u53D1\u5904\u7406\uFF08\u8F6C\u51FA/\u53D6\u6D88/\u5B8C\u6210\uFF09\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5"
2230
+ });
2231
+ }
2232
+ return toEntity(result);
2233
+ },
2234
+ async markDone(issueId, note) {
2235
+ const current = await base._collection.findOne({ issueId });
2236
+ if (!current) {
2237
+ throw new NotFoundError("issue", issueId, { bizCode: "ISSUE_NOT_FOUND" });
2238
+ }
2239
+ if (current.status === "converted" || current.status === "cancelled" || current.status === "done") {
2240
+ throw new ConflictError("issue", issueId, {
2241
+ bizCode: "ISSUE_INVALID_TRANSITION",
2242
+ message: `\u95EE\u9898\u5DF2\u662F\u7EC8\u6001\uFF08${String(current.status)}\uFF09\uFF0C\u7981\u6B62\u6807\u8BB0\u5B8C\u6210`
2243
+ });
2244
+ }
2245
+ const now = /* @__PURE__ */ new Date();
2246
+ const result = await base._collection.findOneAndUpdate(
2247
+ // 并发安全带:读后窗口内外部转出/取消/完成 → 命中 0 行报终态冲突(终态以先到者为准)
2248
+ { issueId, status: { $in: ISSUE_ACTIVE_STATUSES } },
2249
+ {
2250
+ $set: { status: "done", updatedAt: now },
2251
+ $push: {
2252
+ history: {
2253
+ id: generateEntryId([]),
2254
+ action: "status-change",
2255
+ from: current.status,
2256
+ to: "done",
2257
+ ...note !== void 0 ? { reason: note } : {},
2258
+ at: now
2259
+ }
2260
+ }
2261
+ },
2262
+ { returnDocument: "after" }
2263
+ );
2264
+ if (!result) {
2265
+ throw new ConflictError("issue", issueId, {
2266
+ bizCode: "ISSUE_INVALID_TRANSITION",
2267
+ message: "\u95EE\u9898\u5DF2\u88AB\u5E76\u53D1\u5904\u7406\uFF08\u8F6C\u51FA/\u53D6\u6D88/\u5B8C\u6210\uFF09\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5"
2224
2268
  });
2225
2269
  }
2226
2270
  return toEntity(result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siming-org/core",
3
- "version": "0.7.3",
3
+ "version": "0.8.0",
4
4
  "license": "MIT",
5
5
  "author": "Wuchao",
6
6
  "engines": {