openxiangda-devkit-core 2.0.0-alpha.115 → 2.0.0-alpha.117

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.
Files changed (62) hide show
  1. package/dist/app-spec.d.ts +23 -11
  2. package/dist/app-spec.d.ts.map +1 -1
  3. package/dist/app-spec.js +171 -52
  4. package/dist/app-spec.js.map +1 -1
  5. package/dist/application-services.d.ts +290 -23
  6. package/dist/application-services.d.ts.map +1 -1
  7. package/dist/application-services.js +171 -76
  8. package/dist/application-services.js.map +1 -1
  9. package/dist/backend-image-build.js +6 -11
  10. package/dist/backend-image-build.js.map +1 -1
  11. package/dist/command-process.d.ts +14 -0
  12. package/dist/command-process.d.ts.map +1 -0
  13. package/dist/command-process.js +51 -0
  14. package/dist/command-process.js.map +1 -0
  15. package/dist/command-registry.d.ts +2 -2
  16. package/dist/command-registry.js +1 -1
  17. package/dist/command-registry.js.map +1 -1
  18. package/dist/compiler/permission-review.d.ts.map +1 -1
  19. package/dist/configuration-preflight.d.ts +7 -0
  20. package/dist/configuration-preflight.d.ts.map +1 -0
  21. package/dist/configuration-preflight.js +13 -0
  22. package/dist/configuration-preflight.js.map +1 -0
  23. package/dist/control-plane-client.d.ts +8 -1
  24. package/dist/control-plane-client.d.ts.map +1 -1
  25. package/dist/control-plane-client.js +13 -0
  26. package/dist/control-plane-client.js.map +1 -1
  27. package/dist/delivery-cache.d.ts +34 -0
  28. package/dist/delivery-cache.d.ts.map +1 -0
  29. package/dist/delivery-cache.js +186 -0
  30. package/dist/delivery-cache.js.map +1 -0
  31. package/dist/delivery-source.d.ts +17 -0
  32. package/dist/delivery-source.d.ts.map +1 -0
  33. package/dist/delivery-source.js +85 -0
  34. package/dist/delivery-source.js.map +1 -0
  35. package/dist/deployment-observer.d.ts +14 -0
  36. package/dist/deployment-observer.d.ts.map +1 -0
  37. package/dist/deployment-observer.js +68 -0
  38. package/dist/deployment-observer.js.map +1 -0
  39. package/dist/deployment.d.ts +13 -0
  40. package/dist/deployment.d.ts.map +1 -1
  41. package/dist/deployment.js +34 -7
  42. package/dist/deployment.js.map +1 -1
  43. package/dist/developer-operations.d.ts +147 -43
  44. package/dist/developer-operations.d.ts.map +1 -1
  45. package/dist/developer-operations.js +22 -3
  46. package/dist/developer-operations.js.map +1 -1
  47. package/dist/development-lifecycle.d.ts +48 -0
  48. package/dist/development-lifecycle.d.ts.map +1 -0
  49. package/dist/development-lifecycle.js +180 -0
  50. package/dist/development-lifecycle.js.map +1 -0
  51. package/dist/documentation.d.ts +3 -3
  52. package/dist/documentation.js +1 -1
  53. package/dist/documentation.js.map +1 -1
  54. package/dist/index.d.ts +2 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +2 -0
  57. package/dist/index.js.map +1 -1
  58. package/dist/operation-progress.d.ts +26 -0
  59. package/dist/operation-progress.d.ts.map +1 -0
  60. package/dist/operation-progress.js +92 -0
  61. package/dist/operation-progress.js.map +1 -0
  62. package/package.json +2 -2
@@ -4,7 +4,7 @@ export declare const APP_SPEC_SCHEMAS: {
4
4
  readonly capability: "openxiangda.appspec/capability/v1";
5
5
  readonly change: "openxiangda.appspec/change/v1";
6
6
  readonly decision: "openxiangda.appspec/decision/v1";
7
- readonly context: "openxiangda.appspec/context/v2";
7
+ readonly context: "openxiangda.appspec/context/v3";
8
8
  };
9
9
  export declare const APP_SPEC_LIMITS: {
10
10
  readonly maximumFiles: 128;
@@ -57,8 +57,8 @@ export interface AppSpecDocumentSummary {
57
57
  export interface AppSpecContext {
58
58
  schemaVersion: typeof APP_SPEC_SCHEMAS.context;
59
59
  enabled: boolean;
60
- mode: "advisory";
61
- releaseGate: false;
60
+ mode: "guided";
61
+ releaseGate: true;
62
62
  root: "appspec";
63
63
  selector: string | null;
64
64
  contract: AppSpecContractIndex;
@@ -74,6 +74,12 @@ export interface AppSpecContext {
74
74
  activeChanges: AppSpecDocument[];
75
75
  archivedChanges: AppSpecDocument[];
76
76
  decisions: AppSpecDocument[];
77
+ historyPage: {
78
+ offset: number;
79
+ limit: number;
80
+ total: number;
81
+ nextOffset: number | null;
82
+ };
77
83
  contextBudget: {
78
84
  maximumBytes: number;
79
85
  contentBytes: number;
@@ -123,7 +129,7 @@ export interface CloseAppSpecChangeInput {
123
129
  contract: AppSpecContractIndex;
124
130
  }
125
131
  export declare function initializeAppSpec(input: InitializeAppSpecInput): {
126
- schemaVersion: "openxiangda.appspec/context/v2";
132
+ schemaVersion: "openxiangda.appspec/context/v3";
127
133
  enabled: boolean;
128
134
  advisory: boolean;
129
135
  releaseGate: boolean;
@@ -131,14 +137,14 @@ export declare function initializeAppSpec(input: InitializeAppSpecInput): {
131
137
  existing: string[];
132
138
  };
133
139
  export declare function createAppSpecCapability(input: CreateAppSpecCapabilityInput): {
134
- schemaVersion: "openxiangda.appspec/context/v2";
140
+ schemaVersion: "openxiangda.appspec/context/v3";
135
141
  enabled: boolean;
136
142
  advisory: boolean;
137
143
  releaseGate: boolean;
138
144
  created: string;
139
145
  };
140
146
  export declare function createAppSpecChange(input: CreateAppSpecChangeInput): {
141
- schemaVersion: "openxiangda.appspec/context/v2";
147
+ schemaVersion: "openxiangda.appspec/context/v3";
142
148
  enabled: boolean;
143
149
  advisory: boolean;
144
150
  releaseGate: boolean;
@@ -147,7 +153,7 @@ export declare function createAppSpecChange(input: CreateAppSpecChangeInput): {
147
153
  status: string;
148
154
  };
149
155
  export declare function closeAppSpecChange(input: CloseAppSpecChangeInput): {
150
- schemaVersion: "openxiangda.appspec/context/v2";
156
+ schemaVersion: "openxiangda.appspec/context/v3";
151
157
  enabled: boolean;
152
158
  advisory: boolean;
153
159
  releaseGate: boolean;
@@ -156,7 +162,7 @@ export declare function closeAppSpecChange(input: CloseAppSpecChangeInput): {
156
162
  converged: boolean;
157
163
  diagnostics: Diagnostic[];
158
164
  } | {
159
- schemaVersion: "openxiangda.appspec/context/v2";
165
+ schemaVersion: "openxiangda.appspec/context/v3";
160
166
  enabled: boolean;
161
167
  advisory: boolean;
162
168
  releaseGate: boolean;
@@ -165,7 +171,7 @@ export declare function closeAppSpecChange(input: CloseAppSpecChangeInput): {
165
171
  converged: boolean;
166
172
  diagnostics: Diagnostic[];
167
173
  };
168
- export declare function inspectAppSpec(root: string, contract: AppSpecContractIndex, selector?: string): AppSpecContext;
174
+ export declare function inspectAppSpec(root: string, contract: AppSpecContractIndex, selector?: string, historyOffset?: number): AppSpecContext;
169
175
  export declare function advisoryAppSpecDiagnostics(diagnostics: Diagnostic[]): {
170
176
  severity: "info" | "warning";
171
177
  remediation: string;
@@ -179,10 +185,16 @@ export declare function advisoryAppSpecDiagnostics(diagnostics: Diagnostic[]): {
179
185
  }[];
180
186
  export declare function summarizeAppSpecContext(context: AppSpecContext): {
181
187
  enabled: boolean;
182
- mode: "advisory";
183
- releaseGate: false;
188
+ mode: "guided";
189
+ releaseGate: true;
184
190
  workspaceDigest: string | null;
185
191
  selectionDigest: string | null;
192
+ historyPage: {
193
+ offset: number;
194
+ limit: number;
195
+ total: number;
196
+ nextOffset: number | null;
197
+ };
186
198
  contextBudget: {
187
199
  maximumBytes: number;
188
200
  contentBytes: number;
@@ -1 +1 @@
1
- {"version":3,"file":"app-spec.d.ts","sourceRoot":"","sources":["../src/app-spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app-spec.d.ts","sourceRoot":"","sources":["../src/app-spec.ts"],"names":[],"mappings":"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"}
package/dist/app-spec.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync, } from "node:fs";
2
+ import { existsSync, openSync, readSync, closeSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync, } from "node:fs";
3
3
  import { basename, dirname, join, relative, resolve } from "node:path";
4
4
  import { SCHEMA_VERSIONS, } from "openxiangda-contracts";
5
5
  export const APP_SPEC_SCHEMAS = {
@@ -7,7 +7,7 @@ export const APP_SPEC_SCHEMAS = {
7
7
  capability: "openxiangda.appspec/capability/v1",
8
8
  change: "openxiangda.appspec/change/v1",
9
9
  decision: "openxiangda.appspec/decision/v1",
10
- context: "openxiangda.appspec/context/v2",
10
+ context: "openxiangda.appspec/context/v3",
11
11
  };
12
12
  export const APP_SPEC_LIMITS = {
13
13
  maximumFiles: 128,
@@ -49,8 +49,8 @@ export function initializeAppSpec(input) {
49
49
  return {
50
50
  schemaVersion: APP_SPEC_SCHEMAS.context,
51
51
  enabled: true,
52
- advisory: true,
53
- releaseGate: false,
52
+ advisory: false,
53
+ releaseGate: true,
54
54
  created,
55
55
  existing,
56
56
  };
@@ -72,8 +72,8 @@ export function createAppSpecCapability(input) {
72
72
  return {
73
73
  schemaVersion: APP_SPEC_SCHEMAS.context,
74
74
  enabled: true,
75
- advisory: true,
76
- releaseGate: false,
75
+ advisory: false,
76
+ releaseGate: true,
77
77
  created: relativePath(input.root, path),
78
78
  };
79
79
  }
@@ -82,6 +82,9 @@ export function createAppSpecChange(input) {
82
82
  if (!CHANGE_ID.test(input.id)) {
83
83
  throw stableError("APPSPEC_CHANGE_ID_INVALID", input.id);
84
84
  }
85
+ if (historyCandidates(input.root, input.id, 0, []).paths.some(path => basename(path) === `${input.id}.md`)) {
86
+ throw stableError('APPSPEC_HISTORY_CHANGE_EXISTS', input.id);
87
+ }
85
88
  if (!input.title.trim())
86
89
  throw stableError("APPSPEC_TITLE_REQUIRED", input.id);
87
90
  const risk = input.risk || "L1";
@@ -102,8 +105,8 @@ export function createAppSpecChange(input) {
102
105
  return {
103
106
  schemaVersion: APP_SPEC_SCHEMAS.context,
104
107
  enabled: true,
105
- advisory: true,
106
- releaseGate: false,
108
+ advisory: false,
109
+ releaseGate: true,
107
110
  created: relativePath(input.root, path),
108
111
  risk,
109
112
  status: "draft",
@@ -171,8 +174,8 @@ export function closeAppSpecChange(input) {
171
174
  return {
172
175
  schemaVersion: APP_SPEC_SCHEMAS.context,
173
176
  enabled: true,
174
- advisory: true,
175
- releaseGate: false,
177
+ advisory: false,
178
+ releaseGate: true,
176
179
  archived: null,
177
180
  status: document.status,
178
181
  converged: false,
@@ -186,15 +189,15 @@ export function closeAppSpecChange(input) {
186
189
  return {
187
190
  schemaVersion: APP_SPEC_SCHEMAS.context,
188
191
  enabled: true,
189
- advisory: true,
190
- releaseGate: false,
192
+ advisory: false,
193
+ releaseGate: true,
191
194
  archived: relativePath(input.root, target),
192
195
  status: "archived",
193
196
  converged: true,
194
197
  diagnostics,
195
198
  };
196
199
  }
197
- export function inspectAppSpec(root, contract, selector) {
200
+ export function inspectAppSpec(root, contract, selector, historyOffset = 0) {
198
201
  const paths = appSpecPaths(root);
199
202
  if (!existsSync(paths.root))
200
203
  return emptyContext(contract, selector);
@@ -203,7 +206,7 @@ export function inspectAppSpec(root, contract, selector) {
203
206
  diagnostics.push(issue("error", "APPSPEC_ROOT_INVALID", "appspec 必须是普通目录,不能是符号链接", "appspec"));
204
207
  return emptyContext(contract, selector, diagnostics, true);
205
208
  }
206
- const collection = collectDocuments(root, diagnostics);
209
+ const collection = collectDocuments(root, diagnostics, selector, historyOffset);
207
210
  const application = collection.app[0] || null;
208
211
  validateDocuments(collection, contract, diagnostics);
209
212
  const selected = selectContext(collection, selector, diagnostics);
@@ -215,7 +218,7 @@ export function inspectAppSpec(root, contract, selector) {
215
218
  ...collection.decisions,
216
219
  ];
217
220
  const index = summarizeCollection(collection);
218
- const workspaceDigest = digestDocuments(allDocuments, contract);
221
+ const workspaceDigest = digestDocuments(allDocuments.filter(document => !document.path.startsWith("appspec/changes/history/")), contract);
219
222
  const selectionDocuments = selectedDocuments(application, selected);
220
223
  const selectionDigest = allDocuments.length === 0
221
224
  ? null
@@ -227,8 +230,8 @@ export function inspectAppSpec(root, contract, selector) {
227
230
  return {
228
231
  schemaVersion: APP_SPEC_SCHEMAS.context,
229
232
  enabled: true,
230
- mode: "advisory",
231
- releaseGate: false,
233
+ mode: "guided",
234
+ releaseGate: true,
232
235
  root: "appspec",
233
236
  selector: selector || null,
234
237
  contract,
@@ -238,16 +241,17 @@ export function inspectAppSpec(root, contract, selector) {
238
241
  activeChanges: budgeted.activeChanges,
239
242
  archivedChanges: budgeted.archivedChanges,
240
243
  decisions: budgeted.decisions,
244
+ historyPage: collection.historyPage,
241
245
  contextBudget: budgeted.contextBudget,
242
246
  stats: {
243
247
  files: allDocuments.length,
244
248
  bytes: collection.bytes,
245
249
  capabilities: collection.capabilities.length,
246
250
  activeChanges: collection.activeChanges.length,
247
- historyChanges: collection.history.length,
251
+ historyChanges: collection.historyPage.total,
248
252
  decisions: collection.decisions.length,
249
- requirements: unique(collection.capabilities.flatMap(document => document.requirementIds)).length,
250
- acceptanceScenarios: unique(collection.capabilities.flatMap(document => document.acceptanceIds)).length,
253
+ requirements: unique([...collection.app, ...collection.capabilities].flatMap(document => document.requirementIds)).length,
254
+ acceptanceScenarios: unique([...collection.app, ...collection.capabilities].flatMap(document => document.acceptanceIds)).length,
251
255
  },
252
256
  workspaceDigest,
253
257
  selectionDigest,
@@ -259,7 +263,7 @@ export function advisoryAppSpecDiagnostics(diagnostics) {
259
263
  ...diagnostic,
260
264
  severity: diagnostic.severity === "error" ? "warning" : diagnostic.severity,
261
265
  remediation: diagnostic.remediation ||
262
- "AppSpec 是辅助层;可运行 openxiangda spec check 获取完整诊断,应用 check/deploy 不受阻断",
266
+ "运行 openxiangda spec check 查看文档诊断;普通 check 可以继续,正式发布按阶段核对需求与验收",
263
267
  }));
264
268
  }
265
269
  export function summarizeAppSpecContext(context) {
@@ -269,6 +273,7 @@ export function summarizeAppSpecContext(context) {
269
273
  releaseGate: context.releaseGate,
270
274
  workspaceDigest: context.workspaceDigest,
271
275
  selectionDigest: context.selectionDigest,
276
+ historyPage: context.historyPage,
272
277
  contextBudget: context.contextBudget,
273
278
  stats: context.stats,
274
279
  diagnostics: {
@@ -285,8 +290,8 @@ function emptyContext(contract, selector, diagnostics = [], enabled = false) {
285
290
  return {
286
291
  schemaVersion: APP_SPEC_SCHEMAS.context,
287
292
  enabled,
288
- mode: "advisory",
289
- releaseGate: false,
293
+ mode: "guided",
294
+ releaseGate: true,
290
295
  root: "appspec",
291
296
  selector: selector || null,
292
297
  contract,
@@ -302,6 +307,7 @@ function emptyContext(contract, selector, diagnostics = [], enabled = false) {
302
307
  activeChanges: [],
303
308
  archivedChanges: [],
304
309
  decisions: [],
310
+ historyPage: { offset: 0, limit: 50, total: 0, nextOffset: null },
305
311
  contextBudget: {
306
312
  maximumBytes: APP_SPEC_LIMITS.maximumContextBytes,
307
313
  contentBytes: 0,
@@ -323,7 +329,55 @@ function emptyContext(contract, selector, diagnostics = [], enabled = false) {
323
329
  diagnostics,
324
330
  };
325
331
  }
326
- function collectDocuments(root, diagnostics) {
332
+ function historyCandidates(root, selector, offset, diagnostics) {
333
+ if (!Number.isSafeInteger(offset) || offset < 0)
334
+ throw stableError('APPSPEC_HISTORY_OFFSET_INVALID', String(offset));
335
+ const directory = appSpecPaths(root).history;
336
+ const all = [];
337
+ let selected;
338
+ const started = Date.now();
339
+ if (existsSync(directory)) {
340
+ if (!isRegularDirectory(directory))
341
+ diagnostics.push(issue('error', 'APPSPEC_DIRECTORY_INVALID', '历史目录必须是普通目录', 'appspec/changes/history'));
342
+ else {
343
+ const years = readdirSync(directory, { withFileTypes: true }).sort((a, b) => b.name.localeCompare(a.name));
344
+ for (const year of years.slice(0, 256)) {
345
+ const path = join(directory, year.name);
346
+ if (year.isSymbolicLink()) {
347
+ diagnostics.push(issue('error', 'APPSPEC_SYMLINK_FORBIDDEN', '历史记录不能使用符号链接', relativePath(root, path)));
348
+ continue;
349
+ }
350
+ if (year.isFile() && year.name.endsWith('.md'))
351
+ all.push(path);
352
+ else if (year.isDirectory()) {
353
+ // 稳定 ID 的读取不依赖当前索引页;正文仍受单文件与上下文预算约束。
354
+ const direct = selector && CHANGE_ID.test(selector) ? join(path, `${selector}.md`) : undefined;
355
+ if (direct && existsSync(direct))
356
+ selected = direct;
357
+ if (all.length >= 100_000 || Date.now() - started > 5_000)
358
+ continue;
359
+ for (const entry of readdirSync(path, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
360
+ if (all.length >= 100_000)
361
+ break;
362
+ const file = join(path, entry.name);
363
+ if (entry.isFile() && entry.name.endsWith('.md'))
364
+ all.push(file);
365
+ else if (entry.isSymbolicLink() || entry.isDirectory())
366
+ diagnostics.push(issue('warning', 'APPSPEC_HISTORY_ENTRY_INVALID', '历史只索引 history/<year>/*.md 普通文件', relativePath(root, file)));
367
+ }
368
+ }
369
+ }
370
+ if (years.length > 256 || all.length >= 100_000 || Date.now() - started > 5_000)
371
+ diagnostics.push(issue('warning', 'APPSPEC_HISTORY_INDEX_LIMIT', '历史索引达到读取预算;记录仍保留,可用稳定变更 ID 读取正文', 'appspec/changes/history'));
372
+ }
373
+ }
374
+ const paths = all.slice(offset, offset + 50);
375
+ const match = selected || (selector ? all.find(path => basename(path) === `${selector}.md`) : undefined);
376
+ if (match && !paths.includes(match))
377
+ paths.push(match);
378
+ return { paths, page: { offset, limit: 50, total: all.length, nextOffset: offset + 50 < all.length ? offset + 50 : null } };
379
+ }
380
+ function collectDocuments(root, diagnostics, selector, historyOffset = 0) {
327
381
  const paths = appSpecPaths(root);
328
382
  const result = {
329
383
  app: [],
@@ -332,6 +386,7 @@ function collectDocuments(root, diagnostics) {
332
386
  history: [],
333
387
  decisions: [],
334
388
  bytes: 0,
389
+ historyPage: { offset: historyOffset, limit: 50, total: 0, nextOffset: null },
335
390
  };
336
391
  const candidates = [];
337
392
  if (existsSync(paths.app))
@@ -342,10 +397,6 @@ function collectDocuments(root, diagnostics) {
342
397
  })), ...markdownFiles(paths.activeChanges, false, diagnostics, root).map(path => ({
343
398
  path,
344
399
  kind: "change",
345
- })), ...markdownFiles(paths.history, true, diagnostics, root).map(path => ({
346
- path,
347
- kind: "change",
348
- history: true,
349
400
  })), ...markdownFiles(paths.decisions, false, diagnostics, root).map(path => ({
350
401
  path,
351
402
  kind: "decision",
@@ -354,7 +405,11 @@ function collectDocuments(root, diagnostics) {
354
405
  diagnostics.push(issue("error", "APPSPEC_FILE_LIMIT_EXCEEDED", `AppSpec 文件数 ${candidates.length} 超过上限 ${APP_SPEC_LIMITS.maximumFiles}`, "appspec"));
355
406
  candidates.splice(APP_SPEC_LIMITS.maximumFiles);
356
407
  }
357
- for (const candidate of candidates.sort((left, right) => left.path.localeCompare(right.path))) {
408
+ const history = historyCandidates(root, selector, historyOffset, diagnostics);
409
+ result.historyPage = history.page;
410
+ candidates.push(...history.paths.map(path => ({ path, kind: "change", history: true })));
411
+ let historyBytes = 0;
412
+ for (const candidate of candidates.sort((left, right) => Number(!!left.history) - Number(!!right.history) || left.path.localeCompare(right.path))) {
358
413
  let stat;
359
414
  try {
360
415
  stat = lstatSync(candidate.path);
@@ -371,12 +426,30 @@ function collectDocuments(root, diagnostics) {
371
426
  diagnostics.push(issue("error", "APPSPEC_FILE_SIZE_EXCEEDED", `AppSpec 单文件超过 ${APP_SPEC_LIMITS.maximumFileBytes} 字节上限`, relativePath(root, candidate.path)));
372
427
  continue;
373
428
  }
374
- if (result.bytes + stat.size > APP_SPEC_LIMITS.maximumTotalBytes) {
429
+ const indexOnly = candidate.history && basename(candidate.path) !== `${selector}.md`;
430
+ const readBytes = indexOnly ? Math.min(stat.size, 8192) : stat.size;
431
+ if ((candidate.history ? historyBytes : result.bytes) + readBytes > APP_SPEC_LIMITS.maximumTotalBytes) {
375
432
  diagnostics.push(issue("error", "APPSPEC_TOTAL_SIZE_EXCEEDED", `AppSpec 总大小超过 ${APP_SPEC_LIMITS.maximumTotalBytes} 字节上限`, "appspec"));
376
433
  break;
377
434
  }
378
- result.bytes += stat.size;
379
- const content = readFileSync(candidate.path, "utf8");
435
+ if (candidate.history)
436
+ historyBytes += readBytes;
437
+ else
438
+ result.bytes += readBytes;
439
+ let content;
440
+ if (indexOnly) {
441
+ const descriptor = openSync(candidate.path, 'r');
442
+ try {
443
+ const buffer = Buffer.alloc(readBytes);
444
+ const bytes = readSync(descriptor, buffer, 0, readBytes, 0);
445
+ content = buffer.subarray(0, bytes).toString('utf8').match(/^---\r?\n[\s\S]*?\r?\n---/)?.[0] || '';
446
+ }
447
+ finally {
448
+ closeSync(descriptor);
449
+ }
450
+ }
451
+ else
452
+ content = readFileSync(candidate.path, "utf8");
380
453
  const document = parseAppSpecDocument(root, candidate.path, candidate.kind, content, diagnostics);
381
454
  if (!document)
382
455
  continue;
@@ -412,11 +485,11 @@ function validateDocuments(collection, contract, diagnostics) {
412
485
  }
413
486
  }
414
487
  duplicateDiagnostics([...collection.capabilities, ...collection.activeChanges, ...collection.history, ...collection.decisions], document => document.id, "APPSPEC_DOCUMENT_ID_DUPLICATED", diagnostics);
415
- duplicateDiagnostics(collection.capabilities.flatMap(document => document.requirementIds.map(id => ({ ...document, id }))), document => document.id, "APPSPEC_REQUIREMENT_ID_DUPLICATED", diagnostics);
416
- duplicateDiagnostics(collection.capabilities.flatMap(document => document.acceptanceIds.map(id => ({ ...document, id }))), document => document.id, "APPSPEC_ACCEPTANCE_ID_DUPLICATED", diagnostics);
488
+ duplicateDiagnostics([...collection.app, ...collection.capabilities].flatMap(document => document.requirementIds.map(id => ({ ...document, id }))), document => document.id, "APPSPEC_REQUIREMENT_ID_DUPLICATED", diagnostics);
489
+ duplicateDiagnostics([...collection.app, ...collection.capabilities].flatMap(document => document.acceptanceIds.map(id => ({ ...document, id }))), document => document.id, "APPSPEC_ACCEPTANCE_ID_DUPLICATED", diagnostics);
417
490
  const capabilityIds = new Set(collection.capabilities.map(document => document.id));
418
- const requirementIds = new Set(collection.capabilities.flatMap(document => document.requirementIds));
419
- const acceptanceIds = new Set(collection.capabilities.flatMap(document => document.acceptanceIds));
491
+ const requirementIds = new Set([...collection.app, ...collection.capabilities].flatMap(document => document.requirementIds));
492
+ const acceptanceIds = new Set([...collection.app, ...collection.capabilities].flatMap(document => document.acceptanceIds));
420
493
  const resourceCodes = new Set(contract.resourceCodes);
421
494
  const actionCodes = new Set(contract.actionCodes);
422
495
  const decisionIds = new Set(collection.decisions.map(document => document.id));
@@ -436,7 +509,7 @@ function validateDocuments(collection, contract, diagnostics) {
436
509
  }
437
510
  for (const id of document.references.requirements) {
438
511
  if (!requirementIds.has(id)) {
439
- diagnostics.push(issue(referenceSeverity, "APPSPEC_CHANGE_REQUIREMENT_UNKNOWN", `ChangeSpec 引用的需求 ${id} 尚未进入当前 CapabilitySpec`, document.path));
512
+ diagnostics.push(issue(referenceSeverity, "APPSPEC_CHANGE_REQUIREMENT_UNKNOWN", `ChangeSpec 引用的需求 ${id} 尚未进入当前总纲或能力规格`, document.path));
440
513
  }
441
514
  }
442
515
  for (const id of document.references.decisions) {
@@ -460,13 +533,17 @@ function validateDocuments(collection, contract, diagnostics) {
460
533
  }
461
534
  }
462
535
  function validateCloseConvergence(document, collection, contract, diagnostics) {
536
+ for (const title of ['验证与发布', '交接']) {
537
+ if (!hasMeaningfulSection(document.content, [title], []))
538
+ diagnostics.push(issue('error', 'APPSPEC_HANDOFF_INCOMPLETE', `关闭前补齐“${title}”的实际结果;未发布或取消时明确原因`, `${document.path}#${title}`));
539
+ }
463
540
  const currentSpec = currentSpecStatus(document);
464
541
  if (currentSpec === "pending") {
465
542
  diagnostics.push(issue("error", "APPSPEC_CURRENT_SPEC_NOT_CONVERGED", "关闭前必须确认 currentSpec=merged 或 not-applicable;该检查只约束 spec close", document.path));
466
543
  }
467
544
  const capabilityIds = new Set(collection.capabilities.map(item => item.id));
468
- const requirementIds = new Set(collection.capabilities.flatMap(item => item.requirementIds));
469
- const acceptanceIds = new Set(collection.capabilities.flatMap(item => item.acceptanceIds));
545
+ const requirementIds = new Set([...collection.app, ...collection.capabilities].flatMap(item => item.requirementIds));
546
+ const acceptanceIds = new Set([...collection.app, ...collection.capabilities].flatMap(item => item.acceptanceIds));
470
547
  const decisionIds = new Set(collection.decisions.map(item => item.id));
471
548
  for (const id of document.references.capabilities) {
472
549
  const matches = collection.capabilities.filter(item => item.id === id);
@@ -478,9 +555,9 @@ function validateCloseConvergence(document, collection, contract, diagnostics) {
478
555
  }
479
556
  }
480
557
  for (const id of document.references.requirements) {
481
- const matches = collection.capabilities.filter(item => item.requirementIds.includes(id));
558
+ const matches = [...collection.app, ...collection.capabilities].filter(item => item.requirementIds.includes(id));
482
559
  if (!requirementIds.has(id)) {
483
- diagnostics.push(issue("error", "APPSPEC_CHANGE_REQUIREMENT_UNKNOWN", `关闭前必须把需求 ${id} 合入当前 CapabilitySpec`, document.path));
560
+ diagnostics.push(issue("error", "APPSPEC_CHANGE_REQUIREMENT_UNKNOWN", `关闭前必须把需求 ${id} 合入当前总纲或能力规格`, document.path));
484
561
  }
485
562
  else if (matches.length > 1) {
486
563
  diagnostics.push(issue("error", "APPSPEC_REQUIREMENT_ID_DUPLICATED", `关闭前必须消除需求 ${id} 的重复定义`, document.path));
@@ -496,13 +573,13 @@ function validateCloseConvergence(document, collection, contract, diagnostics) {
496
573
  }
497
574
  }
498
575
  for (const id of document.requirementIds) {
499
- const matches = collection.capabilities.filter(item => item.requirementIds.includes(id));
576
+ const matches = [...collection.app, ...collection.capabilities].filter(item => item.requirementIds.includes(id));
500
577
  if (matches.length > 1) {
501
578
  diagnostics.push(issue("error", "APPSPEC_REQUIREMENT_ID_DUPLICATED", `关闭前必须消除需求 ${id} 的重复定义`, document.path));
502
579
  }
503
580
  }
504
581
  for (const id of document.acceptanceIds) {
505
- const matches = collection.capabilities.filter(item => item.acceptanceIds.includes(id));
582
+ const matches = [...collection.app, ...collection.capabilities].filter(item => item.acceptanceIds.includes(id));
506
583
  if (matches.length > 1) {
507
584
  diagnostics.push(issue("error", "APPSPEC_ACCEPTANCE_ID_DUPLICATED", `关闭前必须消除验收场景 ${id} 的重复定义`, document.path));
508
585
  }
@@ -514,12 +591,12 @@ function validateCloseConvergence(document, collection, contract, diagnostics) {
514
591
  function validateEmbeddedCurrentSpecIds(document, currentRequirementIds, currentAcceptanceIds, diagnostics) {
515
592
  for (const id of document.requirementIds) {
516
593
  if (!currentRequirementIds.has(id)) {
517
- diagnostics.push(issue("error", "APPSPEC_CHANGE_REQUIREMENT_NOT_CURRENT", `ChangeSpec 中的长期需求 ${id} 尚未进入当前 CapabilitySpec`, document.path));
594
+ diagnostics.push(issue("error", "APPSPEC_CHANGE_REQUIREMENT_NOT_CURRENT", `ChangeSpec 中的长期需求 ${id} 尚未进入当前总纲或能力规格`, document.path));
518
595
  }
519
596
  }
520
597
  for (const id of document.acceptanceIds) {
521
598
  if (!currentAcceptanceIds.has(id)) {
522
- diagnostics.push(issue("error", "APPSPEC_CHANGE_ACCEPTANCE_NOT_CURRENT", `ChangeSpec 中的验收场景 ${id} 尚未进入当前 CapabilitySpec`, document.path));
599
+ diagnostics.push(issue("error", "APPSPEC_CHANGE_ACCEPTANCE_NOT_CURRENT", `ChangeSpec 中的验收场景 ${id} 尚未进入当前总纲或能力规格`, document.path));
523
600
  }
524
601
  }
525
602
  }
@@ -984,7 +1061,7 @@ status: active
984
1061
  ---
985
1062
  # ${appName}
986
1063
 
987
- AppSpec 是可选的业务意图辅助层,不是发布门禁。资源、字段、权限和动作仍以
1064
+ 本文件保存当前有效目标与设计约束。正式变更关联 ChangeSpec;测试发布前核对设计与验收计划,生产晋级前核对实际验收。资源、字段、权限和动作的实现以
988
1065
  \`openxiangda.config.ts\` 与实时编译合同为准。
989
1066
 
990
1067
  ## 业务目标
@@ -1003,12 +1080,28 @@ AppSpec 是可选的业务意图辅助层,不是发布门禁。资源、字段
1003
1080
 
1004
1081
  ## 能力目录
1005
1082
 
1006
- <!-- 复杂业务再在 capabilities/ 下增加 CAP-*;小应用可以只维护本文件。 -->
1083
+ <!-- 复杂业务再在 capabilities/ 下增加 CAP-*;简单应用的稳定规则直接写在本文件。 -->
1007
1084
 
1008
1085
  ## 术语
1009
1086
 
1010
1087
  ## 跨能力约束
1011
1088
 
1089
+ ## 架构与数据关系
1090
+
1091
+ <!-- 写明领域边界、核心模型关系、平台能力与自定义动作的归属、状态转换与不变量。 -->
1092
+
1093
+ ## 业务任务与页面
1094
+
1095
+ <!-- 按用户任务规划管理端、PC、移动入口与数据关系;辅助表不必有页面。 -->
1096
+
1097
+ ## 权限矩阵与确认
1098
+
1099
+ <!-- 记录角色、页面、操作、行范围、字段和多角色组合;写明需求依据与未明确的边界。 -->
1100
+
1101
+ ## 性能与容量预算
1102
+
1103
+ <!-- 记录数据量及增长、并发、分页与索引、请求次数、延迟目标和测量口径;区分估算、目标与实测。 -->
1104
+
1012
1105
  ## 未确认问题
1013
1106
 
1014
1107
  - 无。
@@ -1040,9 +1133,9 @@ actions: ${JSON.stringify(input.actions)}
1040
1133
 
1041
1134
  #### AC-DOMAIN-001-01 正向或反向场景
1042
1135
 
1043
- - Given 已知前置条件
1044
- - When 用户或系统执行动作
1045
- - Then 观察到明确结果
1136
+ - 前置:已知条件
1137
+ - 操作:用户或系统执行动作
1138
+ - 预期:观察到明确结果
1046
1139
  -->
1047
1140
 
1048
1141
  ## 状态与异常
@@ -1072,6 +1165,22 @@ decisions: []
1072
1165
 
1073
1166
  ${input.summary || "<!-- 一两句话说明问题、证据和期望结果。 -->"}
1074
1167
 
1168
+ ## 需求依据
1169
+
1170
+ <!-- 记录用户原始需求或当前规格引用、已明确规则与假设;不代替用户编造确认。 -->
1171
+
1172
+ ## 方案与影响
1173
+
1174
+ <!-- 说明能力所有者、模型/页面/权限/状态影响,引用已有架构决定。 -->
1175
+
1176
+ ## 任务与实现
1177
+
1178
+ <!-- 用稳定需求 ID 关联实施任务、源码/声明路径与验收场景;实施后更新实际结果。 -->
1179
+
1180
+ ## 性能与容量预算
1181
+
1182
+ <!-- 引用总纲默认预算,或记录本次数据规模、查询/分页/批量/超时/并发和延迟目标。 -->
1183
+
1075
1184
  ## 变更
1076
1185
 
1077
1186
  - ADDED:
@@ -1084,6 +1193,8 @@ ${input.summary || "<!-- 一两句话说明问题、证据和期望结果。 -->
1084
1193
  - [ ] 一个可观察的正向结果
1085
1194
  - [ ] 需要时补充拒绝、异常或权限反例
1086
1195
 
1196
+ <!-- 使用 #### AC-领域-编号 给场景稳定 ID;写明角色、前置、操作、预期和拒绝路径。测试部署后再记录实际观察。 -->
1197
+
1087
1198
  ## 数据与权限
1088
1199
 
1089
1200
  - 无,或说明资源、字段、角色和数据范围变化。
@@ -1100,6 +1211,14 @@ ${input.summary || "<!-- 一两句话说明问题、证据和期望结果。 -->
1100
1211
 
1101
1212
  - L3 如涉及 ADR,在 front matter 的 decisions 中引用。
1102
1213
 
1214
+ ## 验证与发布
1215
+
1216
+ <!-- 记录检查结果、源码提交、测试/生产运行 ID、包摘要、验收报告路径、未覆盖项和恢复入口。区分本地通过、部署成功与业务验收。 -->
1217
+
1218
+ ## 交接
1219
+
1220
+ <!-- 写明持续有效规则已更新的位置、剩余问题与下一步;无需复制当前规格全文。 -->
1221
+
1103
1222
  ## 未确认问题
1104
1223
 
1105
1224
  - 无。
@@ -1170,8 +1289,8 @@ function issue(severity, code, message, path) {
1170
1289
  path,
1171
1290
  retryable: false,
1172
1291
  remediation: severity === "error"
1173
- ? "修正 AppSpec 后重新运行 openxiangda spec check;普通应用 check/deploy 不受阻断"
1174
- : "按当前变更风险决定是否补充;AppSpec 质量建议不阻断应用交付",
1292
+ ? "修正记录后运行 openxiangda spec check;普通 check 可继续,正式发布按阶段核对"
1293
+ : "按实际变化范围补充;确定性缺口会在相应发布阶段核对",
1175
1294
  };
1176
1295
  }
1177
1296
  function summarize(document) {