@chenmk/superflow 0.5.13 → 0.5.14

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.
@@ -14,6 +14,7 @@ mechanically:
14
14
  from __future__ import annotations
15
15
 
16
16
  import argparse
17
+ import json
17
18
  import re
18
19
  import subprocess
19
20
  import sys
@@ -99,6 +100,13 @@ EXTERNAL_EVIDENCE_RE = re.compile(
99
100
  re.I,
100
101
  )
101
102
  CASE_ID_RE = re.compile(r"\bT\d+(?:\.\d+)+\b")
103
+ CASE_EVIDENCE_HEADER = re.compile(
104
+ r"^\|\s*(?:用例\s*ID|Case\s*ID)\s*\|\s*(?:入口\s*ID|Entry\s*ID)\s*\|"
105
+ r"\s*(?:验收级别|Acceptance\s*level)\s*\|\s*(?:结果|Result)\s*\|"
106
+ r"\s*(?:证据|Evidence)(?:路径|\s*path)?\s*\|$",
107
+ re.I,
108
+ )
109
+ CASE_RESULTS = {"PASS", "FAIL", "BLOCKED", "PARTIAL"}
102
110
  OLD_PHRASES = (
103
111
  "不会按周期边界拆分子切片",
104
112
  "1 秒空窗落入下一周期对账无实质影响",
@@ -253,6 +261,69 @@ def lint_against_tests_contract(report_path: Path, report_text: str, tests_path:
253
261
  return issues
254
262
 
255
263
 
264
+ def lint_case_evidence(report_path: Path, report_text: str, review_path: Path) -> list[Issue]:
265
+ """Check the frozen R/E/C index against per-case execution receipts."""
266
+ issues: list[Issue] = []
267
+ try:
268
+ review = json.loads(review_path.read_text(encoding="utf-8"))
269
+ coverage = review["coverage"]
270
+ if coverage["schemaVersion"] != "superflow.review-coverage.v1":
271
+ raise ValueError("unsupported coverage schema")
272
+ cases = {case["id"]: case for case in coverage["cases"]}
273
+ decisions = coverage["decisions"]
274
+ except (OSError, ValueError, KeyError, TypeError) as error:
275
+ return [Issue("FAIL", report_path, 0, f"入口评审凭证无法读取:{error}")]
276
+
277
+ required = {
278
+ case_id
279
+ for decision in decisions
280
+ if decision.get("disposition") != "EXCLUDED"
281
+ for case_id in decision.get("caseIds", [])
282
+ }
283
+ lines = report_text.splitlines()
284
+ header_at = next(
285
+ (i for i, line in enumerate(lines) if CASE_EVIDENCE_HEADER.fullmatch(line.strip())),
286
+ None,
287
+ )
288
+ if header_at is None:
289
+ return [Issue("FAIL", report_path, 0, "缺少逐用例执行证据表;不能用汇总通过数代替 R/E/C 回填")]
290
+
291
+ receipts: dict[str, tuple[str, str, str, str]] = {}
292
+ for index, line in enumerate(lines[header_at + 2 :], start=header_at + 3):
293
+ if not line.startswith("|"):
294
+ break
295
+ columns = [column.strip() for column in line.strip().strip("|").split("|")]
296
+ if len(columns) != 5:
297
+ issues.append(Issue("FAIL", report_path, index, "逐用例执行证据表必须有五列"))
298
+ continue
299
+ case_id, entry_id, level, result, evidence = columns
300
+ if case_id in receipts:
301
+ issues.append(Issue("FAIL", report_path, index, f"用例 {case_id} 重复回填"))
302
+ receipts[case_id] = (entry_id, level, result.upper(), evidence)
303
+
304
+ for case_id in sorted(required):
305
+ if case_id not in receipts:
306
+ issues.append(Issue("FAIL", report_path, 0, f"用例 {case_id} 缺少逐项执行结果"))
307
+ continue
308
+ entry_id, level, result, evidence = receipts[case_id]
309
+ case = cases.get(case_id)
310
+ if case is None or entry_id != case.get("entryId") or level != case.get("level"):
311
+ issues.append(Issue("FAIL", report_path, 0, f"用例 {case_id} 的入口或验收级别与冻结合同不一致"))
312
+ if result not in CASE_RESULTS or not evidence or evidence.lower() in {"pending", "todo", "待补"}:
313
+ issues.append(Issue("FAIL", report_path, 0, f"用例 {case_id} 缺少有效结果或证据位置"))
314
+ elif result == "PASS" and not re.match(r"^https?://", evidence, re.I):
315
+ evidence_file = Path(evidence.split("#", 1)[0])
316
+ if not evidence_file.is_absolute():
317
+ evidence_file = report_path.parent / evidence_file
318
+ if not evidence_file.is_file():
319
+ issues.append(Issue("FAIL", report_path, 0, f"用例 {case_id} 的通过证据文件不存在:{evidence}"))
320
+ if result != "PASS" and re.search(r"验证结果\s*[::]\s*PASS|Verification Result\s*[::]\s*PASS", report_text, re.I):
321
+ issues.append(Issue("FAIL", report_path, 0, f"用例 {case_id} 为 {result},报告不能声明整体验证 PASS"))
322
+ for case_id in receipts.keys() - required:
323
+ issues.append(Issue("FAIL", report_path, 0, f"用例 {case_id} 未在冻结入口评审中要求执行"))
324
+ return issues
325
+
326
+
256
327
  def infer_tests_path(report: Path) -> Path | None:
257
328
  direct = report.parent / "tests.md"
258
329
  if direct.exists():
@@ -268,7 +339,8 @@ def infer_tests_path(report: Path) -> Path | None:
268
339
  return None
269
340
 
270
341
 
271
- def lint_report(path: Path, repo: Path | None, tests_path: Path | None = None) -> list[Issue]:
342
+ def lint_report(path: Path, repo: Path | None, tests_path: Path | None = None,
343
+ review_path: Path | None = None) -> list[Issue]:
272
344
  issues: list[Issue] = []
273
345
  text = path.read_text(encoding="utf-8", errors="replace")
274
346
  lines = text.splitlines()
@@ -278,6 +350,8 @@ def lint_report(path: Path, repo: Path | None, tests_path: Path | None = None) -
278
350
  blocked = bool(BLOCKED_RE.search(text))
279
351
  passed_or_partial = bool(PASS_WORDS.search(text) or re.search(r"Partially verified|部分验证", text, re.I))
280
352
  issues.extend(lint_against_tests_contract(path, text, tests_path or infer_tests_path(path)))
353
+ if review_path is not None:
354
+ issues.extend(lint_case_evidence(path, text, review_path))
281
355
 
282
356
  if db_backed and passed_or_partial and not blocked:
283
357
  if not TABLE_REVERSE_HEADER_RE.search(text):
@@ -395,6 +469,7 @@ def main() -> int:
395
469
  parser = argparse.ArgumentParser(description="Lint SuperBridge Flow test-report evidence")
396
470
  parser.add_argument("reports", nargs="+")
397
471
  parser.add_argument("--tests", default="")
472
+ parser.add_argument("--review", default="")
398
473
  parser.add_argument("--repo-root", default="")
399
474
  parser.add_argument("--warn-only", action="store_true")
400
475
  args = parser.parse_args()
@@ -407,7 +482,10 @@ def main() -> int:
407
482
  continue
408
483
  repo = Path(args.repo_root).resolve() if args.repo_root else repo_root_for(path)
409
484
  tests_path = Path(args.tests).resolve() if args.tests else None
410
- all_issues.extend(lint_report(path, repo, tests_path))
485
+ if tests_path is not None and not tests_path.exists():
486
+ all_issues.append(Issue("FAIL", path, 0, f"显式指定的 tests.md 不存在:{tests_path}"))
487
+ review_path = Path(args.review).resolve() if args.review else None
488
+ all_issues.extend(lint_report(path, repo, tests_path, review_path))
411
489
 
412
490
  printable = []
413
491
  for issue in all_issues:
@@ -380,6 +380,18 @@ DB 状态 | 结算/通知/展示消费点 | 真实验证方式`.
380
380
  `用例ID | 需求/Scenario | 层级L1/L2/L3/L4 | 前置数据 | 操作步骤 |
381
381
  自动化命令 | 响应断言 | DB断言 | 日志断言 | RED预期失败 | GREEN预期通过 |
382
382
  test-report证据位置`.
383
+ - For cross-page management or database-backed display changes, reconcile the
384
+ frontend action/list inventory with real Controller, Service, exact Mapper
385
+ statement and table anchors before freezing tasks. Put the confirmed chain
386
+ and entry ID in `source-code-audit.md` and reuse it in `design.md`, `tasks.md`
387
+ and `tests.md`. A similarly named query is not evidence for the page route.
388
+ Include every visible mutation button, such as enable/disable, as an entry
389
+ even when the original task list omitted it.
390
+ - For a returned persisted field, `tests.md` must freeze at least one non-null
391
+ fixture and an allowed historical-NULL fixture separately. Assert API and DB
392
+ values for the same record ID, then browser rendering where a page is in
393
+ scope. Key-presence, HTTP 200, empty-page and NULL-only assertions cannot
394
+ close the non-null data path. Name the exact query/command and evidence file.
383
395
  - 对 L3/L4 的输入校验、协议拒绝或异常分支,`tests.md` 还必须冻结真实注入载荷及其到达
384
396
  目标解析/校验分支的证据;仅断言同一个 HTTP 状态码不算覆盖。原始畸形 JSON、错误
385
397
  Content-Type、越权身份或非法字段必须按真实线协议发送,并同时断言无副作用。
@@ -41,6 +41,9 @@ CLI 命令:
41
41
  停止,不创建托管 Task;已有用户评审通过的 change/Prompt 时原路径冻结;只有边界清晰的
42
42
  口头小需求才直接进入 minimal/standard。若口头需求存在会改变实现方向的 API、数据、
43
43
  并发、跨仓或 owner 决策,先进入澄清/文档流程,不能把这一判断交给 Runner 正则或 Executor。
44
+ 多页面、跨前后端、数据库字段横向改造要求闭环交付时,按完整 SDD 冻结真实入口、
45
+ `tests.md` 和逐入口验收合同。仅有 OpenSpec `spec/design/tasks` 或通用冒烟报告,
46
+ 只能声明相应范围已实现/验证,不得称为完整 SDD 验收。
44
47
 
45
48
  ```bash
46
49
  superflow pipeline "<implementation-prompt 路径、change 目录或简单任务>" --managed --project "<项目根目录>" \
@@ -24,6 +24,14 @@
24
24
  同页面不同角色的分支用不同入口 ID;不得只测试开发者选中的新接口。
25
25
  5. 验收同时断言返回、最终状态和禁止的副作用。只读/纯函数场景也要给出明确
26
26
  无持久化变更的理由,不能留空。browser 入口必须有 browser 验收,API 测试只能补充。
27
+ 6. 横向页面/字段改造先从前端路由、列表列和可点击动作建立页面入口清单,再从后端
28
+ Controller、Mapper statement、全部写入方反向建立清单。逐项对账后才生成 tasks/tests;
29
+ 相似方法名不得作为调用关系证据。每个列表记录实际页面请求、Controller、Service、
30
+ 精确 Mapper statement、返回 DTO 与表;每个管理按钮记录实际 mutation API 和写入点。
31
+ 未定位或两份清单不一致的入口标记待调查,不得以总数相等替代逐项核验。
32
+ 7. 数据库字段展示用例必须分别覆盖一个非空记录与允许的历史 NULL 记录。非空记录
33
+ 用同一业务 ID 比较 API 字段与数据库真源值,并在浏览器核对展示;仅有字段键、HTTP
34
+ 200、空列表或历史 NULL 不能证明字段链路正确。修改入口还要验证写后读及禁止的副作用。
27
35
 
28
36
  ## 凭证扩展
29
37
 
@@ -80,3 +88,13 @@ coding-ready 复检失败会写 BLOCKED 撤销旧 READY。
80
88
  验收阶段沿同一 R/E/C ID 在 `test-report.md` 回填实际角色、动作、请求路由、状态/
81
89
  副作用断言、原始日志路径及结果。主 Agent 对照原始来源和实际证据,不只看测试汇总。
82
90
  计划用例不是执行证据;API-only 不能报页面 E2E 成功。脚本未验证业务语义的部分不得宣称自动保证。
91
+ 完整 SDD 报告使用唯一的逐用例执行证据表,供现有 report lint 核对冻结索引:
92
+
93
+ | 用例 ID | 入口 ID | 验收级别 | 结果 | 证据路径 |
94
+ |---|---|---|---|---|
95
+ | C1 | E1 | browser | PASS | logs/C1-trace.zip |
96
+
97
+ 结果只用 `PASS/FAIL/BLOCKED/PARTIAL`;证据路径指向实际命令输出、浏览器 trace、
98
+ 响应或数据库对账记录。PASS 的本地证据文件必须保留到验收并可读取;远端证据可用
99
+ HTTP(S) 链接。阻塞项在报告正文写明原因。表格仅证明用例逐项回填,不能证明
100
+ 证据内容正确。任何非 PASS 用例存在时,不得声明整体验证 PASS。
@@ -71,6 +71,17 @@ mvn test -Dtest=UserServiceTest
71
71
 
72
72
  ## 4.1 真实测试数据与证据
73
73
 
74
+ 完整 SDD 变更还需按 `document-review.json` 的 R/E/C 索引逐项回填,不得用总通过数
75
+ 代替单个入口的结果。下表供 `superflow-test-report-lint.py --review` 校验:
76
+
77
+ | 用例 ID | 入口 ID | 验收级别 | 结果 | 证据路径 |
78
+ |---|---|---|---|---|
79
+ | C1 | E1 | browser | PASS | logs/C1-trace.zip |
80
+
81
+ `PASS/FAIL/BLOCKED/PARTIAL` 之外的值无效;非 PASS 项在正文说明原因。
82
+ 数据库字段展示要分别记录非空数据按同一 ID 的 API/DB 值对账、历史 NULL 结果和
83
+ 真实浏览器展示。API 键存在、静态前端配置与浏览器显示分别记录证据等级。
84
+
74
85
  | 用例 ID | 真实参数 | 参数来源 | 自动化命令 | 证据等级 | 执行证据 | 结论 |
75
86
  |---------|----------|----------|------------|----------|----------|------|
76
87
  | TC-001 | parkCode=____ | 前端请求 / 用户提供 / DB查询 / Mock | curl/Newman/pytest/RestAssured | Mock only / 测试端点 / 真实入口 | 响应 + DB + 日志 | Passed / Blocked / Partial |
@@ -87,15 +87,21 @@ project_root_for_change() {
87
87
 
88
88
  require_test_report_lint() {
89
89
  local report="$CHANGE_DIR/test-report.md"
90
- local root lint output
90
+ local root lint output review
91
+ local -a lint_args
91
92
  root="$(project_root_for_change)"
93
+ review="$CHANGE_DIR/.sdd/reviews/document-review.json"
94
+ lint_args=(--tests "$CHANGE_DIR/tests.md")
95
+ if [[ -f "$review" ]]; then
96
+ lint_args+=(--review "$review")
97
+ fi
92
98
 
93
99
  for lint in \
94
100
  "$root/.codex/hooks/superflow-test-report-lint.py" \
95
101
  "$HOME/.codex/hooks/superflow-test-report-lint.py"; do
96
102
  if [[ -x "$lint" ]]; then
97
103
  output="$(mktemp)"
98
- if ! "$lint" --tests "$CHANGE_DIR/tests.md" "$report" >"$output" 2>&1; then
104
+ if ! "$lint" "${lint_args[@]}" "$report" >"$output" 2>&1; then
99
105
  issues+=("superflow-test-report-lint failed: $(tr '\n' ' ' < "$output" | sed -E 's/[[:space:]]+/ /g')")
100
106
  fi
101
107
  rm -f "$output"
@@ -34,6 +34,8 @@ unverifiable claims separate rather than giving them a guessed severity.
34
34
  完整 workflow 验收必须读取[需求×真实入口评审](../superflow-pipeline/references/document-review-coverage.md)。
35
35
  沿 R/E/C ID 在现有 test-report 回填实际角色、页面动作、请求路由、最终状态与禁止副作用
36
36
  的原始证据。独立对照原始来源及实际调用方;API-only 不得冒充 browser E2E,计划不等于执行。
37
+ 横向字段改造还要复核页面请求命中的精确 Mapper statement,并查看非空记录的同 ID
38
+ API/DB 值对照与浏览器显示。历史 NULL、字段键存在和静态列配置各自只能支持较弱结论。
37
39
 
38
40
  Verification is a phase, not a final sentence. Use this skill after
39
41
  implementation work claims completion and before archive or delivery.
@@ -140,7 +142,8 @@ Before pass:
140
142
  DB/log/API evidence, handoff hash, and phase state.
141
143
  2. `~/.codex/hooks/superflow-verify-integration.sh <test-report.md>` passes when the
142
144
  report involves integration evidence.
143
- 3. `~/.codex/hooks/superflow-test-report-lint.py --tests <tests.md> <test-report.md>`
145
+ 3. `~/.codex/hooks/superflow-test-report-lint.py --tests <tests.md> --review
146
+ <.sdd/reviews/document-review.json> <test-report.md>`
144
147
  passes. If `tests.md` declares L3/L4, real-entry, third-party, device,
145
148
  database, log, or dev-tool evidence, mock-only/unit-only reports are hard
146
149
  failures unless the report is explicitly `Blocked` or `Partially verified`
@@ -376,6 +376,18 @@ Use references only as needed:
376
376
  `Case ID | Requirement/scenario | Level L1/L2/L3/L4 | Precondition data | Steps |
377
377
  Automation command | Response assertion | DB assertion | Log assertion | RED expected failure | GREEN expected pass |
378
378
  test-report evidence location`.
379
+ - For cross-page management or database-backed display changes, reconcile the
380
+ frontend action/list inventory with real Controller, Service, exact Mapper
381
+ statement and table anchors before freezing tasks. Put the confirmed chain
382
+ and entry ID in `source-code-audit.md` and reuse it in `design.md`, `tasks.md`
383
+ and `tests.md`. A similarly named query is not evidence for the page route.
384
+ Include every visible mutation button, such as enable/disable, as an entry
385
+ even when the original task list omitted it.
386
+ - For a returned persisted field, `tests.md` must freeze at least one non-null
387
+ fixture and an allowed historical-NULL fixture separately. Assert API and DB
388
+ values for the same record ID, then browser rendering where a page is in
389
+ scope. Key-presence, HTTP 200, empty-page and NULL-only assertions cannot
390
+ close the non-null data path. Name the exact query/command and evidence file.
379
391
  - For L3/L4 input validation, protocol rejection, and exceptional branches,
380
392
  `tests.md` also freezes the real injected payload and evidence that it reaches
381
393
  the intended parser or validation branch; matching the same HTTP status alone
@@ -61,6 +61,10 @@ user-approved change/prompt is frozen from its original path. Only a bounded
61
61
  verbal request enters minimal or standard directly. Direction-changing API,
62
62
  data, concurrency, cross-repository, or owner choices return to
63
63
  clarification/documents; Runner regexes and Executor improvisation do not decide them.
64
+ For cross-page, cross-stack or database-field changes requesting closed delivery,
65
+ use full SDD to freeze real entries, `tests.md` and per-entry acceptance. A raw
66
+ OpenSpec `spec/design/tasks` set or generic smoke report supports only its
67
+ actually verified scope, not a full SDD acceptance claim.
64
68
 
65
69
  ```bash
66
70
  superflow pipeline "<implementation prompt, change directory, or direct task>" --managed --project "<project root>" \
@@ -26,6 +26,16 @@ investigate source-resolvable questions autonomously.
26
26
  5. Assert response, final state and forbidden effects. For read-only/pure-function work explain
27
27
  why no persistent change applies; do not leave blanks. Browser paths require browser acceptance;
28
28
  API checks can supplement, not replace them.
29
+ 6. For cross-page or field changes, inventory frontend routes, list columns and clickable actions
30
+ independently from backend Controllers, exact Mapper statements and all writers. Reconcile both
31
+ inventories before writing tasks/tests. Similar method names are not call-chain evidence. For
32
+ every list record page request, Controller, Service, exact Mapper statement, response DTO and
33
+ table; for every management button record its mutation API and write point. Investigate missing
34
+ or conflicting entries instead of treating matching totals as proof.
35
+ 7. A database-backed display case needs both a non-null fixture and a separate allowed historical
36
+ NULL case. Compare API fields with authoritative DB values for the same business ID, then check
37
+ browser rendering. Field keys, HTTP 200, empty lists and historical NULL alone are insufficient.
38
+ Mutation cases also check write-then-read behavior and forbidden side effects.
29
39
 
30
40
  ## Receipt extension
31
41
 
@@ -87,3 +97,13 @@ During verification, reuse R/E/C IDs in `test-report.md`, recording actual actor
87
97
  request route, state/forbidden-effect assertions, raw evidence paths and outcomes. The Host
88
98
  compares them to original sources, not only a test summary. Planned cases are not execution
89
99
  evidence; API-only results cannot claim browser E2E. Do not claim automatic semantic assurance.
100
+ Full SDD reports use one per-case execution evidence table for the existing report linter:
101
+
102
+ | Case ID | Entry ID | Acceptance level | Result | Evidence path |
103
+ |---|---|---|---|---|
104
+ | C1 | E1 | browser | PASS | logs/C1-trace.zip |
105
+
106
+ Results are `PASS/FAIL/BLOCKED/PARTIAL`. Evidence points to actual command output, browser trace,
107
+ response or DB reconciliation. Retain readable local evidence files for PASS; HTTP(S) links may
108
+ identify remote evidence. Explain blockers in the report body. This table proves case-level
109
+ accounting only; the Host must inspect evidence contents. Any non-PASS case prevents an overall PASS.
@@ -55,6 +55,18 @@ Archive Readiness: PASS / BLOCKED
55
55
 
56
56
  Required for `verify_mode: full`:
57
57
 
58
+ For full SDD changes, fill every R/E/C case from `document-review.json` in one
59
+ execution table checked by `superflow-test-report-lint.py --review`:
60
+
61
+ | Case ID | Entry ID | Acceptance level | Result | Evidence path |
62
+ |---|---|---|---|---|
63
+ | C1 | E1 | browser | PASS | logs/C1-trace.zip |
64
+
65
+ Use only `PASS/FAIL/BLOCKED/PARTIAL`; explain non-PASS results in the report.
66
+ For database-backed display fields, record same-ID API/DB values on non-null
67
+ data, historical NULL separately, and real browser rendering. Distinguish
68
+ field-key checks, static frontend inspection and browser evidence.
69
+
58
70
  - Test environment:
59
71
  - Branch/commit/build number or image:
60
72
  - Base URL:
@@ -87,15 +87,21 @@ project_root_for_change() {
87
87
 
88
88
  require_test_report_lint() {
89
89
  local report="$CHANGE_DIR/test-report.md"
90
- local root lint output
90
+ local root lint output review
91
+ local -a lint_args
91
92
  root="$(project_root_for_change)"
93
+ review="$CHANGE_DIR/.sdd/reviews/document-review.json"
94
+ lint_args=(--tests "$CHANGE_DIR/tests.md")
95
+ if [[ -f "$review" ]]; then
96
+ lint_args+=(--review "$review")
97
+ fi
92
98
 
93
99
  for lint in \
94
100
  "$root/.codex/hooks/superflow-test-report-lint.py" \
95
101
  "$HOME/.codex/hooks/superflow-test-report-lint.py"; do
96
102
  if [[ -x "$lint" ]]; then
97
103
  output="$(mktemp)"
98
- if ! "$lint" --tests "$CHANGE_DIR/tests.md" "$report" >"$output" 2>&1; then
104
+ if ! "$lint" "${lint_args[@]}" "$report" >"$output" 2>&1; then
99
105
  issues+=("superflow-test-report-lint failed: $(tr '\n' ' ' < "$output" | sed -E 's/[[:space:]]+/ /g')")
100
106
  fi
101
107
  rm -f "$output"
@@ -40,6 +40,10 @@ For full workflow acceptance, read [requirement × real-entry review](../superfl
40
40
  Reuse R/E/C IDs in the existing test-report with actual actors, page actions, request routes,
41
41
  final state and forbidden-effect evidence. Independently compare originals and actual callers;
42
42
  API-only is not browser E2E, and planned cases are not execution evidence.
43
+ For cross-page field changes, recheck the exact Mapper statement reached by each
44
+ page request and inspect same-ID API/DB comparisons on non-null records plus
45
+ browser rendering. Historical NULL, field keys and static column configuration
46
+ support narrower claims only.
43
47
 
44
48
  Verification is a phase, not a final sentence. Use this skill after
45
49
  implementation work claims completion and before archive or delivery.
@@ -146,7 +150,8 @@ Before pass:
146
150
  DB/log/API evidence, handoff hash, and phase state.
147
151
  2. `~/.codex/hooks/superflow-verify-integration.sh <test-report.md>` passes when the
148
152
  report involves integration evidence.
149
- 3. `~/.codex/hooks/superflow-test-report-lint.py --tests <tests.md> <test-report.md>`
153
+ 3. `~/.codex/hooks/superflow-test-report-lint.py --tests <tests.md> --review
154
+ <.sdd/reviews/document-review.json> <test-report.md>`
150
155
  passes. If `tests.md` declares L3/L4, real-entry, third-party, device,
151
156
  database, log, or dev-tool evidence, mock-only/unit-only reports are hard
152
157
  failures unless the report is explicitly `Blocked` or `Partially verified`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenmk/superflow",
3
- "version": "0.5.13",
3
+ "version": "0.5.14",
4
4
  "description": "SuperBridge Flow workflow CLI for Claude Code and Codex",
5
5
  "license": "MIT",
6
6
  "type": "module",