universal-dev-standards 6.14.0-beta.2 → 6.14.0-beta.4

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 (53) hide show
  1. package/bundled/ai/standards/ai-response-navigation.ai.yaml +43 -3
  2. package/bundled/ai/standards/checkin-standards.ai.yaml +25 -6
  3. package/bundled/ai/standards/open-work-tracking.ai.yaml +4 -1
  4. package/bundled/ai/standards/pipeline-security-gates.ai.yaml +5 -1
  5. package/bundled/core/ai-response-navigation.md +128 -12
  6. package/bundled/core/open-work-tracking.md +1 -1
  7. package/bundled/extensions/frameworks/fat-free-patterns.md +937 -0
  8. package/bundled/extensions/languages/csharp-style.md +464 -0
  9. package/bundled/extensions/languages/php/fat-free-patterns.md +915 -0
  10. package/bundled/extensions/languages/php/php-style.md +693 -0
  11. package/bundled/extensions/languages/php-style.md +700 -0
  12. package/bundled/extensions/locales/zh-cn.md +717 -0
  13. package/bundled/extensions/locales/zh-tw.md +717 -0
  14. package/bundled/locales/COVERAGE.md +5 -4
  15. package/bundled/locales/zh-CN/CHANGELOG.md +44 -3
  16. package/bundled/locales/zh-CN/README.md +2 -2
  17. package/bundled/locales/zh-CN/SECURITY.md +1 -1
  18. package/bundled/locales/zh-CN/core/ai-response-navigation.md +110 -12
  19. package/bundled/locales/zh-CN/skills/README.md +1 -0
  20. package/bundled/locales/zh-CN/skills/comprehension-ladder/SKILL.md +289 -0
  21. package/bundled/locales/zh-CN/skills/comprehension-ladder/eval-cases.md +261 -0
  22. package/bundled/locales/zh-TW/CHANGELOG.md +44 -3
  23. package/bundled/locales/zh-TW/README.md +2 -2
  24. package/bundled/locales/zh-TW/SECURITY.md +1 -1
  25. package/bundled/locales/zh-TW/core/ai-response-navigation.md +110 -12
  26. package/bundled/locales/zh-TW/core/open-work-tracking.md +3 -3
  27. package/bundled/locales/zh-TW/skills/README.md +1 -0
  28. package/bundled/locales/zh-TW/skills/comprehension-ladder/SKILL.md +289 -0
  29. package/bundled/locales/zh-TW/skills/comprehension-ladder/eval-cases.md +261 -0
  30. package/bundled/skills/README.md +1 -0
  31. package/bundled/skills/comprehension-ladder/SKILL.md +283 -0
  32. package/bundled/skills/comprehension-ladder/eval-cases.md +255 -0
  33. package/package.json +2 -2
  34. package/src/commands/check.js +9 -0
  35. package/src/commands/init.js +100 -27
  36. package/src/commands/uninstall.js +144 -30
  37. package/src/commands/update.js +62 -3
  38. package/src/core/install-records.js +191 -0
  39. package/src/i18n/messages.js +39 -6
  40. package/src/installers/hooks-installer.js +61 -30
  41. package/src/installers/integration-installer.js +5 -1
  42. package/src/installers/standards-installer.js +16 -23
  43. package/src/reconciler/plan-executor.js +10 -11
  44. package/src/uninstallers/hook-uninstaller.js +219 -33
  45. package/src/uninstallers/integration-uninstaller.js +35 -5
  46. package/src/utils/copier.js +57 -0
  47. package/src/utils/git-hooks.js +139 -7
  48. package/src/utils/hasher.js +36 -0
  49. package/src/utils/integration-generator.js +16 -6
  50. package/src/utils/legacy-hook-migration.js +112 -0
  51. package/src/utils/locale.js +19 -0
  52. package/src/utils/open-work-tracking.mjs +124 -23
  53. package/standards-registry.json +21 -7
@@ -44,6 +44,19 @@
44
44
  * could not apply (command, test
45
45
  * name, identifier)
46
46
  * unnamed VIOLATION — the only failure
47
+ * A field is read in three shapes, all through the one
48
+ * VOCAB.nextAction list: a heading section, an inline
49
+ * "Next action: ..." label, and EVERY ROW of a table column whose
50
+ * header is in that list (each report carries the line and the
51
+ * row's first cell, so it can be found).
52
+ * Not evaluated, counted: an empty cell, `-`, `—`, `n/a`, `none`,
53
+ * `done`. OWT-019 is NOT violated by them: it judges a next action
54
+ * that was written; "no next action written" is another failure
55
+ * this check does not decide (a finished item has none either).
56
+ * Undecidable, listed: a table row whose cell count differs from its
57
+ * header (a stray or missing `|`). It is neither judged nor read as
58
+ * empty. A violation elsewhere still exits 1; otherwise the exit is 2,
59
+ * because "no violation found" would then cover only part of the field.
47
60
  * revision OWT-018 the acceptance/goal/constraint sections changed
48
61
  * between two versions of a carrier; a NEW revision
49
62
  * record (change, approver, reason) must exist. No
@@ -75,8 +88,8 @@
75
88
  * (--id-pattern) and read the heading vocabulary as a starting point.
76
89
  *
77
90
  * Exit codes: 0 no violation · 1 violation · 2 cannot decide (no structure
78
- * found, git failed, or this script's own self-test arms failed). 2 is NOT a
79
- * pass.
91
+ * found, a next-action table row could not be read, git failed, or this
92
+ * script's own self-test arms failed). 2 is NOT a pass.
80
93
  *
81
94
  * Usage (`uds` from the npm package, or the repo shim; same arguments):
82
95
  * uds open-work next-action <file...> [--root DIR] [--id-pattern RE]
@@ -97,7 +110,16 @@ import { fileURLToPath } from 'node:url';
97
110
 
98
111
  export const VOCAB = {
99
112
  revision: /revision|change[\s-]?log|amendment|history|修訂|變更紀錄|變更記錄|修改紀錄|修改記錄|異動/i,
100
- nextAction: /next[\s-]?(action|step)s?|下一步|下一動/i,
113
+ // ONE list of "next action" words. It is read three ways and there is no second copy:
114
+ // a heading (classifyHeading), a table column header (extractNextActions) and an inline
115
+ // label ("Next action: ...", built from .source below). Keep it free of capture groups:
116
+ // the label regex embeds it and reads its own group 1.
117
+ // 回來要做什麼 ("what to do when you come back") is the header the dev-platform worklog's
118
+ // main table actually uses (DEC-122 H2 baseline carrier); that file describes the column as
119
+ // 下一動 in prose but the header text drifted. Added because a header outside this list is
120
+ // invisible to the check, which is the failure DEC-122 measured. Adopter-specific and
121
+ // UNCALIBRATED (OWT-016); an adopter whose header differs adds its own word here.
122
+ nextAction: /next[\s-]?(?:action|step)s?|下一步|下一動|回來要做什麼/i,
101
123
  progress: /\bprogress\b|\bstate\b|\bblockers?\b|\bblocked\b|進度|現況|卡在/i,
102
124
  intent: /acceptance|criteria|\brequirements?\b|\bgoals?\b|objectives?|constraints?|驗收|需求|目標|限制/i,
103
125
  // revision-table columns / labelled list fields
@@ -198,14 +220,58 @@ function innermost(stack) {
198
220
 
199
221
  const norm = (s) => s.replace(/\s+/g, ' ').trim();
200
222
 
223
+ /** A table line may sit inside a Markdown blockquote ("> | a | b |"): drop the quote marker. */
224
+ const unquote = (line) => line.replace(/^\s*(?:>\s?)+/, '');
225
+
226
+ /** Index of the closing backtick run of exactly `n` backticks at or after `from`, or -1. */
227
+ function closingRun(t, from, n) {
228
+ let j = from;
229
+ while (j < t.length) {
230
+ if (t[j] !== '`') { j++; continue; }
231
+ let m = 0;
232
+ while (t[j + m] === '`') m++;
233
+ if (m === n) return j;
234
+ j += m;
235
+ }
236
+ return -1;
237
+ }
238
+
239
+ /**
240
+ * Split one table row into cells. A `|` does NOT split a cell when it is escaped (`\|`) or
241
+ * sits inside a code span that closes on the same line. The second rule is deliberately
242
+ * looser than GFM (which splits inside code unless escaped): a hand-written note such as
243
+ * `tar -tzf \| grep` or `a|b` in backticks means one cell, and splitting it would shift
244
+ * every later column and misread the next-action cell. An unclosed backtick is literal.
245
+ */
201
246
  function splitRow(line) {
202
- let t = line.trim();
247
+ let t = unquote(line).trim();
203
248
  if (t.startsWith('|')) t = t.slice(1);
204
- if (t.endsWith('|')) t = t.slice(0, -1);
205
- return t.split(/(?<!\\)\|/).map((c) => norm(c.replace(/\\\|/g, '|')));
249
+ const cells = [];
250
+ let cur = '';
251
+ let lastDelim = false;
252
+ let i = 0;
253
+ while (i < t.length) {
254
+ const ch = t[i];
255
+ lastDelim = false;
256
+ if (ch === '\\' && t[i + 1] === '|') { cur += '|'; i += 2; continue; }
257
+ if (ch === '`') {
258
+ let n = 0;
259
+ while (t[i + n] === '`') n++;
260
+ const close = closingRun(t, i + n, n);
261
+ if (close !== -1) { cur += t.slice(i, close + n).replace(/\\\|/g, '|'); i = close + n; continue; }
262
+ cur += '`'.repeat(n);
263
+ i += n;
264
+ continue;
265
+ }
266
+ if (ch === '|') { cells.push(cur); cur = ''; lastDelim = true; i++; continue; }
267
+ cur += ch;
268
+ i++;
269
+ }
270
+ if (!lastDelim) cells.push(cur); // a trailing unescaped `|` closes the row; it opens no cell
271
+ return cells.map(norm);
206
272
  }
207
273
 
208
- const isSepRow = (line) => /^\s*\|?[\s:|-]+\|?\s*$/.test(line) && /-/.test(line);
274
+ const isSepRow = (line) => /^\s*\|?[\s:|-]+\|?\s*$/.test(unquote(line)) && /-/.test(line);
209
275
 
210
276
  /** Tables among annotated lines: [{header, rows:[{cells, idx}]}] */
211
277
  function findTables(lines) {
@@ -213,12 +279,12 @@ function findTables(lines) {
213
279
  let k = 0;
214
280
  while (k < lines.length) {
215
281
  const l = lines[k];
216
- if (!l.inFence && !l.fence && !l.front && l.text.trim().startsWith('|') && k + 1 < lines.length
217
- && isSepRow(lines[k + 1].text) && lines[k + 1].text.trim().startsWith('|')) {
282
+ if (!l.inFence && !l.fence && !l.front && unquote(l.text).trim().startsWith('|') && k + 1 < lines.length
283
+ && isSepRow(lines[k + 1].text) && unquote(lines[k + 1].text).trim().startsWith('|')) {
218
284
  const header = splitRow(l.text);
219
285
  const rows = [];
220
286
  let j = k + 2;
221
- while (j < lines.length && lines[j].text.trim().startsWith('|')) {
287
+ while (j < lines.length && unquote(lines[j].text).trim().startsWith('|')) {
222
288
  rows.push({ cells: splitRow(lines[j].text), idx: j });
223
289
  j++;
224
290
  }
@@ -456,24 +522,45 @@ export function classifyNextAction(text, opts = {}) {
456
522
  };
457
523
  }
458
524
 
459
- /** Find "next action" fields in a carrier. @returns {{fields:number, items:{text:string,where:string}[]}} */
525
+ const clip = (s, n) => (s.length > n ? `${s.slice(0, n)}…` : s);
526
+
527
+ /**
528
+ * Find "next action" fields in a carrier. Three shapes are read, all through the ONE
529
+ * vocabulary VOCAB.nextAction: a heading section, an inline label, and a table column whose
530
+ * HEADER is in that vocabulary (every row of that column is one field).
531
+ *
532
+ * A table row whose cell count differs from the header's is returned in `ragged`, not in
533
+ * `items`: after a stray or missing `|` the next-action cell cannot be told from its
534
+ * neighbour, and reading an absent cell as "empty" would turn a broken row into a quiet
535
+ * "no next action written". It is neither judged nor dropped: the caller counts and lists it.
536
+ * @returns {{fields:number, items:{text:string,where:string}[], ragged:{where:string,cells:number,expected:number}[]}}
537
+ */
460
538
  export function extractNextActions(md) {
461
539
  const lines = parseDoc(md);
462
540
  const items = [];
541
+ const ragged = [];
463
542
  let fields = 0;
464
543
  const tableIdx = new Set();
465
544
  const headerIdx = new Set();
466
545
  const tables = findTables(lines);
467
546
 
468
547
  for (const t of tables) {
469
- const col = t.header.findIndex((c) => VOCAB.nextAction.test(c));
470
- const hasCol = col >= 0;
548
+ // every header cell in the shared vocabulary is a next-action column (usually exactly one)
549
+ const cols = t.header.map((c, n) => (VOCAB.nextAction.test(c) ? n : -1)).filter((n) => n >= 0); // [mutation-anchor:table-header]
550
+ const hasCol = cols.length > 0;
471
551
  tableIdx.add(t.headerIdx); tableIdx.add(t.headerIdx + 1); headerIdx.add(t.headerIdx);
472
552
  t.rows.forEach((r) => tableIdx.add(r.idx));
473
553
  if (hasCol) {
474
554
  for (const r of t.rows) {
475
- fields++;
476
- items.push({ text: r.cells[col] || '', where: `table column "${t.header[col]}" (line ${r.idx + 1})` });
555
+ const who = `row "${clip(r.cells[0].replace(/\*\*|__/g, ''), 40)}"`;
556
+ if (r.cells.length !== t.header.length) {
557
+ ragged.push({ idx: r.idx, where: `table row (line ${r.idx + 1}, ${who})`, cells: r.cells.length, expected: t.header.length });
558
+ continue;
559
+ }
560
+ for (const col of cols) {
561
+ fields++;
562
+ items.push({ text: r.cells[col], where: `table column "${t.header[col]}" (line ${r.idx + 1}, ${who})` });
563
+ }
477
564
  }
478
565
  }
479
566
  // rows inside a nextAction-class section are handled by the section walk below
@@ -481,12 +568,16 @@ export function extractNextActions(md) {
481
568
  }
482
569
 
483
570
  // inline labelled "**Next action**: ..." (outside tables that have their own column)
484
- const labelRe = /(?:\*\*|__)?(?:next[\s-]?(?:action|step)s?|下一步|下一動)(?:\*\*|__)?\s*[::]\s*(.+)$/i;
571
+ const labelRe = new RegExp(`(?:\\*\\*|__)?(?:${VOCAB.nextAction.source})(?:\\*\\*|__)?\\s*[::]\\s*(.+)$`, 'i');
485
572
  const colTableRows = new Set();
486
- for (const t of tables) if (t.hasNextActionColumn) t.rows.forEach((r) => colTableRows.add(r.idx));
573
+ // Only rows read through the column are exempt from the label scan. A ragged row was not
574
+ // read, so an inline "Next action: ..." inside it must still be seen (else it goes dark).
575
+ const raggedIdx = new Set(ragged.map((g) => g.idx));
576
+ for (const t of tables) if (t.hasNextActionColumn) t.rows.forEach((r) => { if (!raggedIdx.has(r.idx)) colTableRows.add(r.idx); });
487
577
  lines.forEach((l, idx) => {
488
578
  if (l.front || l.fence || l.inFence || l.heading || l.cls === 'nextAction' || colTableRows.has(idx)) return;
489
- const m = labelRe.exec(l.text);
579
+ // a label inside a table row reads to the end of the row, not into its closing pipe
580
+ const m = labelRe.exec(unquote(l.text).trim().startsWith('|') ? l.text.replace(/\s*\|\s*$/, '') : l.text);
490
581
  if (m && !/^\s*$/.test(m[1])) { fields++; items.push({ text: m[1], where: `label (line ${idx + 1})` }); }
491
582
  });
492
583
 
@@ -522,14 +613,15 @@ export function extractNextActions(md) {
522
613
  if (sec.length === 0) items.push({ text: '', where: `section "${owner.heading}" (empty)` });
523
614
  for (const s of sec) items.push({ text: s, where: `section "${owner.heading}"` });
524
615
  }
525
- return { fields, items };
616
+ return { fields, items, ragged };
526
617
  }
527
618
 
528
619
  export function checkNextActions(carriers, opts = {}) {
529
- const res = { walked: 0, empty: 0, counts: { 'named-resolved': 0, 'named-unresolved': 0, unnamed: 0 }, violations: [], detail: [], noFieldCarriers: [] };
620
+ const res = { walked: 0, empty: 0, counts: { 'named-resolved': 0, 'named-unresolved': 0, unnamed: 0 }, violations: [], detail: [], noFieldCarriers: [], undecidable: [] };
530
621
  for (const c of carriers) {
531
- const { fields, items } = extractNextActions(c.content);
532
- if (fields === 0) { res.noFieldCarriers.push(c.path); continue; }
622
+ const { fields, items, ragged } = extractNextActions(c.content);
623
+ for (const g of ragged) res.undecidable.push({ path: c.path, ...g });
624
+ if (fields === 0 && ragged.length === 0) { res.noFieldCarriers.push(c.path); continue; }
533
625
  for (const it of items) {
534
626
  const text = it.text.replace(/\*\*|__/g, '').trim();
535
627
  if (EMPTY_FIELD.test(text)) { res.empty++; continue; }
@@ -557,6 +649,11 @@ export function runSelfTest() {
557
649
  expect('D2 clean: identifier', classifyNextAction('finish XSPEC-436 R1', {}).status !== 'unnamed');
558
650
  expect('D2 clean: test name', classifyNextAction('make the test "a next action that names nothing is reported" pass', {}).status !== 'unnamed');
559
651
 
652
+ // OWT-019, table columns: the header decides which column is read, never the position
653
+ const tbl = extractNextActions('| item | state | Next action |\n|---|---|---|\n| a | open | keep going |\n');
654
+ expect('D2 clean: only the header-named column is a next-action field', tbl.fields === 1 && tbl.items[0].text === 'keep going');
655
+ expect('D2 clean: a table without such a header has no next-action field', extractNextActions('| item | state |\n|---|---|\n| a | open |\n').fields === 0);
656
+
560
657
  // OWT-018
561
658
  const spec = (ac, rev) => `# S\n\n## Acceptance criteria\n\n${ac}\n\n## Revisions\n\n${rev}\n`;
562
659
  const table = (rows) => `| Change | Approver | Reason |\n|---|---|---|\n${rows}`;
@@ -639,12 +736,16 @@ export function main(argv, io = { log: console.log, err: console.error }) {
639
736
  if (!files.length) { say('[owt] next-action: no files given'); return finish(2); }
640
737
  const res = checkNextActions(files.map(readCarrier), { root, idPattern });
641
738
  say(`[owt] OWT-019 walked ${res.walked} next-action field(s) in ${files.length} carrier(s); ${res.empty} empty/done field(s) not evaluated; ${res.noFieldCarriers.length} carrier(s) had no next-action field`);
642
- say(`[owt] named-resolved=${res.counts['named-resolved']} named-unresolved=${res.counts['named-unresolved']} unnamed=${res.counts.unnamed}`);
739
+ say(`[owt] named-resolved=${res.counts['named-resolved']} named-unresolved=${res.counts['named-unresolved']} unnamed=${res.counts.unnamed} undecidable-table-rows=${res.undecidable.length}`);
643
740
  for (const d of res.detail) say(`[owt] ${d.status.padEnd(16)} ${d.path} ${d.where}: ${d.text.slice(0, 80)}${d.kinds.length ? ' <- ' + d.kinds.map((k) => `${k.kind}:${k.value}`).join(', ') : ''}`);
644
741
  for (const v of res.violations) say(`[owt] VIOLATION OWT-019: ${v.path} ${v.where} names no file path, test name, command or requirement identifier: "${v.text.slice(0, 80)}"`);
742
+ for (const u of res.undecidable) say(`[owt] UNDECIDABLE: ${u.path} ${u.where} has ${u.cells} cell(s) but its header has ${u.expected}; the next-action cell cannot be located, so the row is neither judged nor counted as empty`);
645
743
  say(`[owt] ${COVERAGE_NOTE}`);
646
744
  say(`[owt] ${UNCALIBRATED_NOTE}`);
647
745
  if (res.walked + res.empty === 0) { say('[owt] CANNOT DECIDE: no next-action field found in any carrier (walked 0). Exit 2 is not a pass.'); return finish(2); }
746
+ // A violation is a definite answer whatever else is unreadable. Absent one, a row we could
747
+ // not read means "no violation found" is not "no violation": exit 2, never a green 0.
748
+ if (!res.violations.length && res.undecidable.length) { say(`[owt] CANNOT DECIDE: ${res.undecidable.length} table row(s) could not be read (see UNDECIDABLE above), so a clean result would cover only part of the field. Exit 2 is not a pass.`); return finish(2); }
648
749
  return finish(res.violations.length ? 1 : 0);
649
750
  }
650
751
  if (cmd === 'revision') {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "version": "6.14.0-beta.2",
3
+ "version": "6.14.0-beta.4",
4
4
  "lastUpdated": "2026-05-13",
5
5
  "description": "Standards registry for universal-dev-standards with integrated skills and AI-optimized formats",
6
6
  "formats": {
@@ -58,14 +58,14 @@
58
58
  "standards": {
59
59
  "name": "universal-dev-standards",
60
60
  "url": "https://github.com/AsiaOstrich/universal-dev-standards",
61
- "version": "6.14.0-beta.2"
61
+ "version": "6.14.0-beta.4"
62
62
  },
63
63
  "skills": {
64
64
  "name": "universal-dev-standards",
65
65
  "url": "https://github.com/AsiaOstrich/universal-dev-standards",
66
66
  "localPath": "skills",
67
67
  "rawUrl": "https://raw.githubusercontent.com/AsiaOstrich/universal-dev-standards/main/skills",
68
- "version": "6.14.0-beta.2",
68
+ "version": "6.14.0-beta.4",
69
69
  "note": "Skills are now included in the main repository under skills/"
70
70
  }
71
71
  },
@@ -382,6 +382,10 @@
382
382
  "skills/brainstorm-assistant/SKILL.md",
383
383
  "skills/brainstorm-assistant/guide.md"
384
384
  ],
385
+ "comprehension-ladder": [
386
+ "skills/comprehension-ladder/SKILL.md",
387
+ "skills/comprehension-ladder/eval-cases.md"
388
+ ],
385
389
  "bdd-assistant": [
386
390
  "skills/bdd-assistant/SKILL.md",
387
391
  "skills/bdd-assistant/guide.md",
@@ -1536,6 +1540,16 @@
1536
1540
  "description": "Traditional Chinese (Taiwan) localization for all standards",
1537
1541
  "applicability": "Traditional Chinese speaking teams"
1538
1542
  },
1543
+ {
1544
+ "id": "zh-cn-locale",
1545
+ "name": "Simplified Chinese Locale",
1546
+ "nameZh": "简体中文本地化",
1547
+ "source": "extensions/locales/zh-cn.md",
1548
+ "category": "extension",
1549
+ "skillName": null,
1550
+ "description": "Simplified Chinese (Mainland China) localization for all standards",
1551
+ "applicability": "Simplified Chinese speaking teams"
1552
+ },
1539
1553
  {
1540
1554
  "id": "copilot-integration",
1541
1555
  "name": "GitHub Copilot Instructions",
@@ -2295,7 +2309,7 @@
2295
2309
  "id": "license-compliance",
2296
2310
  "name": "License Compliance Standards",
2297
2311
  "nameZh": "授權合規標準",
2298
- "version": "6.14.0-beta.2",
2312
+ "version": "6.14.0-beta.4",
2299
2313
  "source": {
2300
2314
  "human": "core/license-compliance.md",
2301
2315
  "ai": "ai/standards/license-compliance.ai.yaml"
@@ -2307,7 +2321,7 @@
2307
2321
  "id": "verification-oracle",
2308
2322
  "name": "Verification Oracle Standards",
2309
2323
  "nameZh": "驗證 Oracle 標準",
2310
- "version": "6.14.0-beta.2",
2324
+ "version": "6.14.0-beta.4",
2311
2325
  "source": {
2312
2326
  "human": "core/verification-oracle.md",
2313
2327
  "ai": "ai/standards/verification-oracle.ai.yaml"
@@ -2319,7 +2333,7 @@
2319
2333
  "id": "model-provenance",
2320
2334
  "name": "Model Provenance Policy Standards",
2321
2335
  "nameZh": "模型來源政策標準",
2322
- "version": "6.14.0-beta.2",
2336
+ "version": "6.14.0-beta.4",
2323
2337
  "source": {
2324
2338
  "human": "core/model-provenance.md",
2325
2339
  "ai": "ai/standards/model-provenance.ai.yaml"
@@ -2331,7 +2345,7 @@
2331
2345
  "id": "resource-cost-boundary",
2332
2346
  "name": "Resource / Cost Boundary Declaration Standards",
2333
2347
  "nameZh": "資源/成本邊界宣告標準",
2334
- "version": "6.14.0-beta.2",
2348
+ "version": "6.14.0-beta.4",
2335
2349
  "source": {
2336
2350
  "human": "core/resource-cost-boundary.md",
2337
2351
  "ai": "ai/standards/resource-cost-boundary.ai.yaml"