sillyspec 3.29.4 → 3.29.5

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 (56) hide show
  1. package/assets/command-cards/run-archive.md +26 -0
  2. package/assets/command-cards/run-brainstorm.md +33 -0
  3. package/assets/command-cards/run-execute.md +29 -0
  4. package/assets/command-cards/run-plan.md +27 -0
  5. package/assets/command-cards/run-quick.md +34 -0
  6. package/assets/command-cards/run-verify.md +29 -0
  7. package/assets/command-cards/status.md +27 -0
  8. package/package.json +1 -1
  9. package/round4/account.mjs +85 -0
  10. package/round4/brief-autocompact.md +34 -0
  11. package/round4/brief-session-replay.md +50 -0
  12. package/round4/deep-read-synthesis.md +38 -0
  13. package/round4/extract-full.mjs +38 -0
  14. package/round4/extract-timeline.mjs +43 -0
  15. package/round4/find-sessions.mjs +13 -0
  16. package/round4/full-r4-l.md +4707 -0
  17. package/round4/full-r4-o-l.md +4300 -0
  18. package/round4/full-r4-os.md +1681 -0
  19. package/round4/full-r4-sf.md +4255 -0
  20. package/round4/full-r4-sq.md +1734 -0
  21. package/round4/gate-audit-relay-receipt.md +30 -0
  22. package/round4/gate-value-audit.md +52 -0
  23. package/round4/prereg.md +67 -0
  24. package/round4/prompt-R4-L.md +63 -0
  25. package/round4/prompt-R4-O-L.md +59 -0
  26. package/round4/prompt-R4-O-S.md +56 -0
  27. package/round4/prompt-R4-S-F.md +60 -0
  28. package/round4/prompt-R4-S-Q.md +66 -0
  29. package/round4/r4-os-timeline.txt +297 -0
  30. package/round4/r4-sf-timeline.txt +738 -0
  31. package/round4/r4-sq-timeline.txt +322 -0
  32. package/round4/r4l-timeline.txt +793 -0
  33. package/round4/r4ol-timeline.txt +729 -0
  34. package/round4/r4sf-timeline.txt +852 -0
  35. package/round4/result-S.md +171 -0
  36. package/round4/result-final.md +46 -0
  37. package/round4/result-quality.md +63 -0
  38. package/src/command-cards.js +73 -0
  39. package/src/contract-matrix.js +26 -5
  40. package/src/diagnostic-codes.js +2 -1
  41. package/src/index.js +33 -6
  42. package/src/init.js +25 -8
  43. package/src/machine-interface.js +88 -3
  44. package/src/module-impact.js +51 -22
  45. package/src/run/command.js +4 -0
  46. package/src/run/complete-handlers.js +36 -1
  47. package/src/run/complete.js +6 -3
  48. package/src/run/gate-snapshot.js +10 -4
  49. package/src/run/gates.js +114 -37
  50. package/src/run/quick-audit.js +28 -2
  51. package/src/run/shared.js +3 -1
  52. package/src/stages/execute.js +2 -2
  53. package/src/stages/plan-postcheck.js +33 -19
  54. package/src/taskcard-frontmatter.js +57 -0
  55. package/src/verify-postcheck.js +59 -1
  56. package/src/verify-probes.js +45 -23
@@ -26,7 +26,6 @@
26
26
  */
27
27
  import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from 'fs'
28
28
  import { join, dirname, basename, resolve, isAbsolute, relative } from 'path'
29
- import jsYaml from 'js-yaml'
30
29
  import { gitQuiet, unquoteGitPath } from './git-helper.js'
31
30
  import { hasUnconfirmedPrefill } from './prefill.js'
32
31
  import {
@@ -36,6 +35,7 @@ import { parseFileChangeListDetailed } from './change-list.js'
36
35
  import { parseDecisions } from './decision-distill.js'
37
36
  import { parseAllowedPaths, parseRepo, parseRepoRegistry } from './stages/plan-postcheck.js'
38
37
  import { verifyApiParity, _readWorktreeMeta } from './contract-matrix.js'
38
+ import { parseTaskFrontmatter } from './taskcard-frontmatter.js'
39
39
  import { RECEIPT_SOURCE_CROSS_LAYER_RE as RECEIPT_CROSS_LAYER_RE, RECEIPT_SOURCE_UNIT_RE as RECEIPT_UNIT_RE, CLI_SMOKE_SOURCE_MARK as SMOKE_RECEIPT_SOURCE_MARK } from './change-risk-profile.js'
40
40
  import { splitOwnVsForeignDiffFiles } from './foreign-declared.js'
41
41
  import { resolveSpecDir, resolveRuntimeRoot, detectWorktreeSpecDrift } from './run/shared.js'
@@ -1073,7 +1073,7 @@ function designHasSubmitEndpoint(text) {
1073
1073
  // sectionHint(命中的接口段头列表,审计注记)。
1074
1074
  // 落盘面(数据通道=落盘即消费,stage-contract 零 import 纪律):backfillFactsFromMdAndTests
1075
1075
  // 主路径写 facts.apiFace / facts.consumerHints,task-05 validator 经 facts 消费不经函数调用。
1076
- const API_FACE_SECTION_RE = /接口|端点|api|rest/i
1076
+ const API_FACE_SECTION_RE = /接口|端点|\bapi\b|\brest\b/i // quick-B 词边界:OpenAPI 标题(api 前邻字母)不再误收进 sectionHint
1077
1077
  const API_FACE_METHOD_RE = /(?:^|[^A-Za-z])(GET|POST|PUT|DELETE|PATCH)(?![A-Za-z])/
1078
1078
  // 路径样式 token:/ 起始、前邻非字母数字(日期 2026/09/17 的 /09 前是数字段不认),
1079
1079
  // 首字符字母数字/_/-/{,后续段字符含 {}/: 模板形态(/orders/{id}、/orders/:id 全串命中)
@@ -1713,26 +1713,34 @@ const PROBE7_NONTESTABLE_RE = /文档|部署|doc|deploy|manual|config/i
1713
1713
 
1714
1714
  /**
1715
1715
  * 解析 task-NN.md frontmatter 的 acceptance(string/array 双形态归一为数组)。
1716
- * 口径锚 src/stages/plan-postcheck.js acceptance best-effort 段(string → 原文单条、array →
1717
- * 逐条)——不 import 它:parseTaskContracts 只返回 provides/expects_from 不含 acceptance。
1716
+ * 解析归一 taskcard-frontmatter.js 单一源(2026-09-20-taskcard-yaml-hardgate:此前坏 YAML
1717
+ * catch 返回 [] 冒充「无 acceptance」,探针 7 渲染假防御文案)——status 三态让调用方区分
1718
+ * 「真无 acceptance」与「frontmatter 非法 YAML」。口径锚 src/stages/plan-postcheck.js
1719
+ * acceptance best-effort 段(string → 原文单条、array → 逐条)。
1718
1720
  * @param {string} content task 卡全文
1719
- * @returns {string[]|null} acceptance 条目数组;null = 无 frontmatter(调用方跳过该卡);
1720
- * frontmatter 在场但无 acceptance / 非法 YAML → [](防御行,plan-postcheck 已拦缺失)
1721
+ * @returns {{ status: 'no-frontmatter'|'invalid-yaml'|'ok', acceptance: string[],
1722
+ * error: {message: string, line: number, column: number}|null }}
1723
+ * no-frontmatter = 无 frontmatter(调用方跳过该卡);invalid-yaml = frontmatter 非法 YAML
1724
+ * (acceptance 不可读,error 带文件 1 基行:列);ok = 合法解析,acceptance 归一数组
1725
+ * (frontmatter 在场但无 acceptance → [],此时防御行为真——plan-postcheck 已拦缺失)
1721
1726
  */
1722
1727
  export function parseTaskAcceptance(content) {
1723
- const fmMatch = String(content || '').match(/^---\r?\n([\s\S]*?)\r?\n---/)
1724
- if (!fmMatch) return null
1725
- try {
1726
- const fmObj = jsYaml.load(fmMatch[1]) || {}
1727
- if (typeof fmObj.acceptance === 'string') {
1728
- const t = fmObj.acceptance.trim()
1729
- return t ? [t] : []
1730
- }
1731
- if (Array.isArray(fmObj.acceptance)) {
1732
- return fmObj.acceptance.filter(x => typeof x === 'string' && x.trim() !== '').map(x => x.trim())
1728
+ const parsed = parseTaskFrontmatter(content)
1729
+ if (!parsed.hasFrontmatter) return { status: 'no-frontmatter', acceptance: [], error: null }
1730
+ if (!parsed.ok) return { status: 'invalid-yaml', acceptance: [], error: parsed.error }
1731
+ const fmObj = parsed.fm || {}
1732
+ if (typeof fmObj.acceptance === 'string') {
1733
+ const t = fmObj.acceptance.trim()
1734
+ return { status: 'ok', acceptance: t ? [t] : [], error: null }
1735
+ }
1736
+ if (Array.isArray(fmObj.acceptance)) {
1737
+ return {
1738
+ status: 'ok',
1739
+ acceptance: fmObj.acceptance.filter(x => typeof x === 'string' && x.trim() !== '').map(x => x.trim()),
1740
+ error: null,
1733
1741
  }
1734
- } catch { /* frontmatter 非法 YAML → 防御性空(plan-postcheck 侧已拦) */ }
1735
- return []
1742
+ }
1743
+ return { status: 'ok', acceptance: [], error: null }
1736
1744
  }
1737
1745
 
1738
1746
  /**
@@ -1939,6 +1947,12 @@ function renderProbe7Lines(p7) {
1939
1947
  for (const t of (p7.tasks || [])) {
1940
1948
  L.push('')
1941
1949
  L.push(`**${t.task}**`)
1950
+ if (t.fmError) {
1951
+ // 坏 YAML 如实陈述(2026-09-20-taskcard-yaml-hardgate):非「无 acceptance」防御——
1952
+ // plan 门禁 0b 硬校验应已拦截;已过 plan 门仍见此行 = 门禁失效信号(可反馈工具缺陷)
1953
+ L.push(`- ⚠️ frontmatter 非法 YAML(${t.task}.md:${t.fmError.line}:${t.fmError.column} ${mdEscapeCell(t.fmError.message, 160)})——acceptance 不可读,本行非「无 acceptance」防御;plan 门禁 frontmatter 硬校验应已拦截,若已过 plan 门仍见此行即门禁失效信号`)
1954
+ continue
1955
+ }
1942
1956
  if (!Array.isArray(t.acceptance) || t.acceptance.length === 0) {
1943
1957
  L.push('- (卡无 acceptance——防御,plan-postcheck 已拦)')
1944
1958
  continue
@@ -2138,10 +2152,17 @@ export function runVerifyProbes({ cwd, changeName, specDir = null }) {
2138
2152
  try {
2139
2153
  for (const f of readdirSync(tasksDir).filter(n => /^task-\d+\.md$/.test(n)).sort()) {
2140
2154
  const raw = readFileSync(join(tasksDir, f), 'utf8')
2141
- const acceptance = parseTaskAcceptance(raw)
2142
- if (acceptance === null) continue // 无 frontmatter → 跳过(plan-postcheck 已拦)
2155
+ // 三态分流(2026-09-20-taskcard-yaml-hardgate):no-frontmatter 跳卡(原 null 语义);
2156
+ // invalid-yaml 挂 fmError 进条目——渲染层区分「坏 YAML」与「真无 acceptance」防御行
2157
+ const parsed = parseTaskAcceptance(raw)
2158
+ if (parsed.status === 'no-frontmatter') continue // 无 frontmatter → 跳过(plan-postcheck 已拦)
2143
2159
  const fmId = (raw.match(/^id:\s*(\S+)/m) || [])[1]
2144
- cards.push({ task: fmId || f.replace(/\.md$/, ''), acceptance, raw })
2160
+ cards.push({
2161
+ task: fmId || f.replace(/\.md$/, ''),
2162
+ acceptance: parsed.acceptance,
2163
+ fmError: parsed.status === 'invalid-yaml' ? parsed.error : null,
2164
+ raw,
2165
+ })
2145
2166
  }
2146
2167
  } catch { /* tasks 目录不可读 → applicable 维持 false */ }
2147
2168
  probe7.applicable = cards.length > 0
@@ -2196,6 +2217,7 @@ export function runVerifyProbes({ cwd, changeName, specDir = null }) {
2196
2217
  probe7.tasks.push({
2197
2218
  task: card.task,
2198
2219
  acceptance: card.acceptance,
2220
+ fmError: card.fmError || null,
2199
2221
  testFiles,
2200
2222
  hints: buildAcceptanceHints(card.acceptance, testFiles, cwd, wtRoot, probe7CrossRoots),
2201
2223
  })
@@ -2568,7 +2590,7 @@ function renderApiCoverageMatrixLines(apiFace) {
2568
2590
  L.push('<!-- 预填说明:端点行由 CLI 机械预填,判定/用例依据 ID/结果/证据由 agent 逐格填写——用例依据 ID 锚点五形态:design接口表#METHOD /path、权限矩阵[角色×动作]、契约表@行标识、DDL@列名、载荷@构造点路径(须真实命中对应表/段,防空指)。 -->')
2569
2591
  L.push('<!-- 文法注释:子行 = 端点行下一行、两空格缩进、以「↳ <消费端>:」前缀书写(消费端细分承接面,不计矩阵行账);探索行 = 判定 uncovered 且证据列含 [探索] 标记(探索性验证不算覆盖)。 -->')
2570
2592
  if (endpoints.length === 0 && declared === null) {
2571
- L.push('- 无接口面(design 接口段解析零端点且无「本变更接口面:N 端点」声明行)——本变更若实际触碰接口,先补 design 接口段表格或声明行,再重跑 `verify-probes --init` 刷新本段;判级 critical 的零面拦截归 validator')
2593
+ L.push('- 无接口面(design 接口段解析零端点且无「本变更接口面:N 端点」声明行)——本变更若实际触碰接口,先补 design 接口段表格或声明行,再重跑 `verify-probes --change <变更名> --init --force` 重生成本段(⚠️ 全骨架重生成,手填结论会重置——先备份;quick-B 起 --force 通道存在);判级 critical 的零面拦截归 validator')
2572
2594
  return L
2573
2595
  }
2574
2596
  if (endpoints.length > 0 && declared !== null && declared !== endpoints.length) {
@@ -2582,7 +2604,7 @@ function renderApiCoverageMatrixLines(apiFace) {
2582
2604
  }
2583
2605
  } else {
2584
2606
  L.push(`| 本变更接口面:${declared} 端点(agent 声明) | <待填:五选一> | <待填:用例 ID> | <待填> | <待填:锚点> |`)
2585
- L.push('<!-- 解析零行降级(D-005):接口面以 agent 声明为准(对账分母=声明数);声明与实际不符时补 design 接口段表格后重跑 --init 刷新本段 -->')
2607
+ L.push('<!-- 解析零行降级(D-005):接口面以 agent 声明为准(对账分母=声明数);声明与实际不符时补 design 接口段表格后重跑 --init --force 重生成本段(quick-B:--force 才有刷新通道,手填内容会重置先备份) -->')
2586
2608
  }
2587
2609
  L.push('<!-- advisory 尾注(warning 计算归 validator,本段只留位):有消费端未填子行的端点将列于此(advisory——消费端归类=design 清单启发式,数据面 facts.consumerHints);写端点(POST/PUT/DELETE/PATCH)未在权限矩阵段声明的将列于此(advisory——补行或显式豁免「无权限约束」,数据面 facts.apiFace.writeEndpoints;表缺行会让派生框架继承你的洞) -->')
2588
2610
  return L