Skip to content

fix(cli): parse heatmapDays strictly#7218

Merged
wenshao merged 2 commits into
QwenLM:mainfrom
VectorPeak:codex/strict-heatmap-days
Jul 25, 2026
Merged

fix(cli): parse heatmapDays strictly#7218
wenshao merged 2 commits into
QwenLM:mainfrom
VectorPeak:codex/strict-heatmap-days

Conversation

@VectorPeak

@VectorPeak VectorPeak commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

What this PR does

Strictly parses the heatmapDays query parameter for the usage dashboard before applying the existing [1, 366] clamp.

Unsigned decimal integer strings keep the current behavior, including 0 clamping to 1 and oversized values clamping to 366. Malformed values such as -5, 1.5, 7junk, and 1e2 now fall back to the default 183 days instead of being accepted through Number.parseInt() prefix parsing or signed-number parsing.

Why it's needed

GET /usage/dashboard?heatmapDays= is a public daemon/Web Shell query surface, and the local parser says invalid values should fall back to the default. Before this change, parseHeatmapDays() used Number.parseInt(raw, 10), so malformed strings could still be treated as numbers: -5 became -5 and then clamped to 1, 1.5 became 1, 7junk became 7, and 1e2 became 1.

That made malformed query values produce a different heatmap window than the caller actually requested. This PR keeps the existing clamp semantics for valid unsigned integer inputs while making malformed values follow the documented default fallback path.

Reviewer Test Plan

How to verify

Review parseHeatmapDays() and the route test. A reviewer can confirm that valid unsigned integer query values still clamp as before, while negative values and malformed partial numeric strings now return the default heatmap window.

Run:

npm test --workspace=packages/cli -- src/serve/routes/usage-stats.test.ts
npx prettier --check packages/cli/src/serve/routes/usage-stats.ts packages/cli/src/serve/routes/usage-stats.test.ts
npx eslint packages/cli/src/serve/routes/usage-stats.ts packages/cli/src/serve/routes/usage-stats.test.ts

Evidence (Before & After)

Before, the parser accepted signed or partial numeric strings before clamping:

-5 => 1
1.5 => 1
7junk => 7
1e2 => 1
abc => 183
99999 => 366
0 => 1

After, the focused route test pins negative and malformed partial numeric values to the default:

await request(app).get('/usage/dashboard?heatmapDays=-5') // heatmapDays === 183
await request(app).get('/usage/dashboard?heatmapDays=1.5') // heatmapDays === 183
await request(app).get('/usage/dashboard?heatmapDays=7junk') // heatmapDays === 183
await request(app).get('/usage/dashboard?heatmapDays=1e2') // heatmapDays === 183

Tested on

OS Status
macOS not tested
Windows tested
Linux not tested - WSL run was blocked before tests by missing Linux Rollup optional dependency in the Windows-installed node_modules (@rollup/rollup-linux-x64-gnu)

Environment (optional)

Windows local validation used Node.js v24.15.0 from the existing workspace environment.

Risk & Scope

  • Main risk or tradeoff: Low. Callers that accidentally depended on Number.parseInt() behavior for malformed values such as -5 or 7junk will now receive the documented default instead.
  • Not validated / out of scope: Full npm run typecheck --workspace=packages/cli currently fails in this checkout on unrelated upstream/local build-state errors involving ACP bridge declarations, channel memory exports, and Ink selection types; this PR only ran the focused route test plus file-level format/lint checks.
  • Breaking changes / migration notes: None expected for valid unsigned integer heatmapDays values.

Linked Issues

N/A

中文说明

这个 PR 做了什么

这个 PR 收紧了 usage dashboard 的 heatmapDays query 参数解析:只有完整的无符号十进制整数字符串才会进入原有的 [1, 366] clamp 逻辑。

合法整数仍保持原行为,例如 0 clamp 到 1、超大值 clamp 到 366。但 -51.57junk1e2 这类格式错误的值不再通过 Number.parseInt() 被当作数字接受,而是回退到默认的 183 天。

为什么需要

GET /usage/dashboard?heatmapDays= 是 daemon/Web Shell usage dashboard 的公开查询参数。当前注释说明 invalid values 应回退到默认值,但旧实现使用 Number.parseInt(raw, 10),导致 malformed 输入被悄悄转成数字:-5 会先变成 -5 再 clamp 到 11.5 变成 17junk 变成 71e2 变成 1

这会让调用方传入的错误参数产生一个非预期的 heatmap window。这个 PR 保留合法无符号整数的 clamp 行为,只把 malformed value 拉回到已有的默认 fallback 语义。

Reviewer Test Plan

可以检查 parseHeatmapDays() 和现有 route test:合法无符号整数仍按原逻辑 clamp,负数和格式错误的 partial numeric string 会回到默认 183

已在 Windows 本地运行:

npm test --workspace=packages/cli -- src/serve/routes/usage-stats.test.ts
npx prettier --check packages/cli/src/serve/routes/usage-stats.ts packages/cli/src/serve/routes/usage-stats.test.ts
npx eslint packages/cli/src/serve/routes/usage-stats.ts packages/cli/src/serve/routes/usage-stats.test.ts

WSL/Linux 侧尝试运行同一 focused test,但当前共享的 Windows node_modules 缺少 Rollup Linux optional dependency,因此测试在启动 Vitest 前失败;这不是本 PR 代码路径的失败。

风险与范围

风险较低。唯一行为变化是 malformed 值不再被 Number.parseInt() 接受,而是按注释回到默认值;合法无符号整数输入不受影响。

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: observed behavior mismatch — the code comment says "anything invalid falls back to the default," but Number.parseInt() prefix-parses malformed strings (-5-5, 1.51, 7junk7, 1e21). The before/after in the PR body demonstrates this concretely.

Direction: aligned — this is a small input-validation fix making the parser match its documented contract on a daemon query surface. No CHANGELOG reference needed for a parse-correctness fix.

Size: not applicable (no core paths touched — packages/cli/src/serve/routes/).

Approach: the scope is tight — one regex gate before the existing parseInt + clamp, plus four new test cases. Minimal and focused.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题:已观测到的行为不一致——代码注释说"无效值回退到默认",但 Number.parseInt() 会前缀解析格式错误的字符串(-5-51.517junk71e21)。PR 正文提供了具体的 before/after 演示。

方向:对齐——这是一个小型输入验证修复,让解析器符合其在 daemon 查询接口上的文档约定。

规模:不适用(未触及核心路径)。

方案:范围紧凑——在现有 parseInt + clamp 前加一个正则校验,外加四个新测试用例。最小且聚焦。

进入代码审查 🔍

Qwen Code · qwen3.8-max-preview

Reviewed at 6066b03c698a32094a19807816a1c8973781993a · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal: given the problem (parseInt prefix-parses partial numeric strings), I'd add a /^\d+$/ regex guard before the existing parseInt + clamp — exactly what this PR does.

Findings: no blockers. The implementation is correct:

  • typeof raw !== 'string' handles non-string inputs (arrays, undefined).
  • /^\d+$/ rejects negatives, decimals, exponents, trailing junk, and empty strings — requires at least one digit.
  • After the guard, parseInt is safe (input is guaranteed all-digits); the existing [1, 366] clamp is preserved unchanged.
  • Edge case "0" → passes regex → parseInt → 0 → clamped to 1. Correct.
  • Very long digit strings → parseInt may lose precision beyond MAX_SAFE_INTEGER, but the clamp to 366 makes this irrelevant.

Tests cover the four malformed categories (-5, 1.5, 7junk, 1e2) plus the existing valid/clamp cases remain. No scope creep, no unrelated changes.

Testing

Check Conclusion
Test (ubuntu-latest, Node 22.x) ✅ success
Test (windows-latest, Node 22.x) ⏭️ skipped
Test (macos-latest, Node 22.x) ⏭️ skipped
Serve A/B (ubuntu-latest, Node 22.x) ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
precheck-pr / precheck ✅ success
Classify PR ✅ success

Ubuntu unit tests and the Serve A/B integration both pass on the reviewed commit. Windows/macOS test jobs were skipped (infra scheduling, not PR-related). No failures.

Real-scenario testing: N/A — this is a query-parameter parser fix with no user-visible TUI surface; the route test and Serve A/B integration cover the behavior.

中文说明

代码审查

独立方案:给定问题(parseInt 前缀解析部分数字字符串),我会在现有 parseInt + clamp 前加 /^\d+$/ 正则校验——与本 PR 完全一致。

无阻塞问题。实现正确:正则拒绝负数、小数、指数、尾部垃圾和空字符串;guard 之后 parseInt 安全;clamp 逻辑不变。测试覆盖四类 malformed 输入。无范围蔓延。

测试

Ubuntu 单元测试和 Serve A/B 集成测试在审查提交上通过。Windows/macOS 测试任务被跳过(基础设施调度,非 PR 相关)。无失败。

真实场景测试:N/A——这是查询参数解析修复,无用户可见 TUI 界面;路由测试和 Serve A/B 集成已覆盖行为。

Qwen Code · qwen3.8-max-preview

Reviewed at 6066b03c698a32094a19807816a1c8973781993a · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Confidence: 5/5 — clean across every stage; minimal fix that makes the parser match its documented contract, CI green, no reservations.

This is exactly the kind of PR that's easy to say yes to: one regex gate, four test cases, zero scope creep. The parseInt prefix-parsing gotcha is well-known and the fix is the obvious correct one. The before/after in the PR body demonstrates the problem concretely, and the route test pins the new behavior. If I had to maintain this in six months, I'd thank the author — the code reads clearly and the comment above the function now matches what it actually does.

Approving. ✅

中文说明

置信度:5/5——每个阶段都干净;最小修复让解析器符合文档约定,CI 绿色,无保留意见。

这是一个容易说"是"的 PR:一个正则校验、四个测试用例、零范围蔓延。parseInt 前缀解析的陷阱众所周知,修复方案是显而易见的正确做法。批准。✅

Qwen Code · qwen3.8-max-preview

Reviewed at 6066b03c698a32094a19807816a1c8973781993a · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

🩺 serve daemon A/B

Built the PR base vs this PR head 6066b03, drove a fixed endpoint set against each, and diffed the JSON responses. Only fields that changed are shown.

No response changes against the PR base across 4 scenario(s).

Qwen Code · serve A/B

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline.

— qwen3.8-max-preview via Qwen Code /review

Comment on lines +62 to +64
if (typeof raw !== 'string' || !/^\d+$/.test(raw)) {
return DEFAULT_HEATMAP_DAYS;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The new /^\d+$/ guard also changes behavior for negative integer strings: ?heatmapDays=-5 previously parsed to -5 and clamped to the minimum (1), but now falls back to the default (183). That is consistent with the "anything invalid falls back to the default" contract, so it is not a bug — but no test pins it down. — Concrete cost: if a future change relaxes the regex (e.g. /^-?\d+$/), negative inputs would silently revert to clamping at 1 with no failing test to catch it.

Consider adding a negative-value assertion to the existing clamps the heatmapDays query test:

const negative = await request(app).get('/usage/dashboard?heatmapDays=-5');
expect(negative.body.heatmapDays).toBe(183);

— qwen3.8-max-preview via Qwen Code /review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 6066b03c: I added the explicit ?heatmapDays=-5 route-test assertion so negative strings are pinned to the documented default fallback behavior. I also updated the PR body evidence/risk wording to mention the negative-value behavior.

@VectorPeak

Copy link
Copy Markdown
Contributor Author

Thanks for the review note on #7218 (comment). I added an explicit route-test assertion for ?heatmapDays=-5\ so the new invalid-value fallback behavior is pinned: negative strings now stay on the documented default path instead of slipping back to min-clamp behavior.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

— qwen3.8-max-preview via Qwen Code /review

@gwinthis

Copy link
Copy Markdown
Collaborator

🔍 Local Verification Report — PR #7218

Scope: +16 lines, 2 files — parse heatmapDays strictly

Test Results

Suite Result
cli/usage-stats.test.ts ✅ 7/7 passed

tmux CLI Startup

✅ CLI 正常启动(v0.20.0)

Architecture Review

论点: 输入解析应使用严格正则而非宽松 parseInt

论据:

  1. Before: Number.parseInt(raw, 10) 接受 -5-51.517junk71e21
  2. After: /^\d+$/ 正则仅接受纯数字字符串,其他全部回退到默认值
  3. 新增测试覆盖:负数、小数、尾部文本、科学记数法

论证: parseInt 的宽松解析是安全边界上的漏洞——7junk 不应该被解析为 7。严格正则 + 回退默认值是输入校验的正确模式。

Verdict

7/7 测试通过,CLI 正常启动。建议合并。


Verified locally: unit tests + CLI startup on macOS

@wenshao

wenshao commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Code Review — #7218 fix(cli): parse heatmapDays strictly

Verdict: LGTM / merge-ready. Correct, minimal, and it lands on the daemon's existing query-validation convention rather than inventing one. Notes below are optional polish — none blocking.

Local verification

Check Result
usage-stats.test.ts @ head 6066b03 ✅ 7/7 pass
Discriminating A/B — revert only usage-stats.ts to base, keep new test ✅ fails 1 failed | 6 passed, AssertionError: expected 1 to be 183
prettier --check + eslint on both files ✅ clean
Full old-vs-new parser matrix (19 inputs) ✅ see below

Reverting only the source (not the test) is what proves the new assertions are load-bearing rather than vacuously green.

Old vs new, full matrix

"-5"          old=1    new=183   CHANGED
"1.5"         old=1    new=183   CHANGED
"7junk"       old=7    new=183   CHANGED
"1e2"         old=1    new=183   CHANGED
" 7" / "7 "   old=7    new=183   CHANGED   (not in PR body)
"+5"          old=5    new=183   CHANGED   (not in PR body)
"30.0"        old=30   new=183   CHANGED   (not in PR body)
"0x10"        old=1    new=183   CHANGED   (not in PR body)
"9"×400       old=183  new=366   CHANGED   (not in PR body — see note 2)
"abc" / ""    old=183  new=183
"0"           old=1    new=1                (clamp preserved ✓)
"99999"       old=366  new=366              (clamp preserved ✓)
"007"         old=7    new=7
["5","6"]     old=183  new=183              (Express array → default, unchanged)

All valid-unsigned-integer behavior is preserved exactly as the PR body claims.


1. The convention argument is stronger than the PR body makes it (context, not a change request)

The /^\d+$/-then-parseInt shape isn't ad hoc — it is the house pattern for daemon range params, in two places already:

  • packages/cli/src/serve/server/request-helpers.ts:351parseMaxQueuedQuery
  • packages/cli/src/serve/routes/workspace-file-read.ts:133parseIntInRange, whose docstring states it outright:

    "Strict on ^\d+$ so '', 'abc', '1.5', '-3' all reject — the daemon's other range parsers (parseMaxQueuedQuery) use the same regex shape, keeping query-validation behavior consistent."

Worth citing in the PR body, since it pre-empts the obvious reviewer alternative ("why not Number(raw) + Number.isInteger, which would map 1e2 → 100?"). Consistency with the two sibling parsers is a better tiebreaker than either parse semantic on its own.

2. [Low] Dropped Number.isFinite guard — one undocumented behavior delta

The old code's if (!Number.isFinite(value)) is gone. It was load-bearing in exactly one case: a ≥309-digit input overflows parseInt to Infinity.

"9".repeat(400)  →  old: 183 (default)   new: 366 (clamped to MAX)

Not a bug — still bounded by the clamp, and 366 is arguably more consistent (99999366 too, so the old code was the odd one out). And it's not a perf lever: buildHeatmap (usage-dashboard-service.ts:119) iterates records, not days, so a larger window costs nothing extra.

Two small asks:

  • Add this row to the PR body's before/after table — it's the one delta not listed.
  • Consider one line for exact parity with the siblings, both of which retain a numeric guard (parseIntInRange uses Number.isSafeInteger, parseMaxQueuedQuery uses Number.isFinite):
    const value = Number.parseInt(raw, 10);
    if (!Number.isSafeInteger(value)) return DEFAULT_HEATMAP_DAYS;
    This PR is currently the only one of the three without such a guard.

3. [Low] SDK-side: a non-integer heatmapDays now silently widens the window

DaemonClient.usageDashboard (packages/sdk-typescript/src/daemon/DaemonClient.ts:971-976) types the option as heatmapDays?: number — not integer-constrained — and serializes with String(opts.heatmapDays). So an external SDK consumer computing e.g. 30.5 used to get a 30-day window and now silently gets the 183-day default: a wider window than requested, with no error. Same for useDaemonUsageDashboard in webui.

No in-repo caller is affected — I checked every useDaemonUsageDashboard / loadUsageDashboard call site and none passes heatmapDays at all (web-shell relies on the server default), which also explains the clean serve-A/B bot result. So this is doc-tightening, not a fix:

  • Either params.set('heatmapDays', String(Math.floor(opts.heatmapDays))) in the SDK,
  • or say "integer" in the jsdoc at DaemonClient.ts:970 and useDaemonUsageDashboard.ts:16-17 (both currently only say "clamped server-side to 1..366", which reads as though any number is accepted).

4. [Nit] Test hygiene — 4 assertions appended to one it()

All four new cases land in the existing it('clamps the heatmapDays query'). My A/B confirms the cost: with the fix reverted, only the -5 case reports (expected 1 to be 183) — the 1.5 / 7junk / 1e2 regressions are masked behind the first failure. it.each would give per-case signal for the same line count:

it.each([
  ['99999', 366], ['0', 1], ['abc', 183],
  ['-5', 183], ['1.5', 183], ['7junk', 183], ['1e2', 183],
])('heatmapDays=%s → %i', async (raw, expected) => {
  const res = await request(app).get(`/usage/dashboard?heatmapDays=${raw}`);
  expect(res.status).toBe(200);
  expect(res.body.heatmapDays).toBe(expected);
});

Also: the block asserts only .body.heatmapDays and never .status. If the route 500s, the failure surfaces as expected undefined to be 183 rather than pointing at the actual cause — the file's other tests do assert status.

5. Scope note (follow-up, not this PR)

This normalizes one of several loose parsers on the same surface. Still outstanding, same bug class:

  • workspace-git-log.ts:95-96parsePagination uses bare parseInt for ?limit= / ?skip=, so ?limit=7junk7
  • session.ts:257, workspace-status.ts:115, workspace-channel-observed-contacts.ts:30 — bare Number() (different profile: rejects 7junk, but accepts 1e2, " 7", 1.5)

Fine to leave; worth a tracking issue if the goal is uniform query validation across the daemon.

Security / performance

No impact either way. Both old and new clamp to [1, 366], so no unbounded window was ever reachable; buildHeatmap is O(records) not O(days); the route is an open read-only GET by existing design; and the regex ^\d+$ is linear with no backtracking risk. Multi-value (?heatmapDays=5&heatmapDays=6 → array) still falls to the default, unchanged.

中文说明

结论:LGTM,可以合并。 改动正确、最小,而且和 daemon 已有的 query 校验约定一致。以下均为可选打磨,不阻塞合并。

本地验证:head 6066b03usage-stats.test.ts 7/7 通过;只回滚 usage-stats.ts(保留新测试)后 1 failed / 6 passed(expected 1 to be 183),证明新断言确实是 load-bearing 而非空转;prettier + eslint 干净;另外跑了 19 组输入的新旧解析对照矩阵,合法无符号整数行为(0→1、99999→366、007→7)完全保留。

  1. 约定论据比 PR 描述里写的更强/^\d+$/ + parseInt 正是 daemon range 参数的既有写法 —— request-helpers.ts:351parseMaxQueuedQueryworkspace-file-read.ts:133parseIntInRange,后者的注释明确写了「和 parseMaxQueuedQuery 用同样的正则形状,保持 query 校验行为一致」。建议在 PR 正文里引用,可以直接挡掉「为什么不用 Number() + isInteger(那样 1e2 → 100)」这个必然会被问到的替代方案。

  2. [Low] 去掉 Number.isFinite 带来一个未记录的行为变化:≥309 位的数字串会让 parseInt 溢出成 Infinity,旧代码返回默认值 183,新代码 clamp 成 366。不是 bug(仍有上界,而且和 99999→366 更一致),但建议补进 before/after 表格;另外两个同类 parser 都保留了数值 guard(Number.isSafeInteger / Number.isFinite),可以加一行 if (!Number.isSafeInteger(value)) return DEFAULT_HEATMAP_DAYS; 做到完全对齐。

  3. [Low] SDK 侧:非整数会静默把窗口变大DaemonClient.usageDashboard 的类型是 heatmapDays?: number(未约束整数),用 String() 序列化。外部 SDK 调用方若传 30.5,以前得到 30 天,现在静默得到默认的 183 天 —— 比请求的更宽且不报错。仓库内没有任何调用点传 heatmapDays(web-shell 用服务端默认值),所以这也解释了 serve A/B bot 无差异的结果。建议 SDK 里 Math.floor,或在 jsdoc 里写明「整数」。

  4. [Nit] 测试组织:4 个新断言都塞进同一个 it()。A/B 显示回滚修复后只有 -5 这条报出来,1.5 / 7junk / 1e2 被首个失败掩盖;改成 it.each 行数相同但每条单独出信号。另外该块只断言 .body.heatmapDays 没断言 .status,一旦 500 会报成 expected undefined to be 183,掩盖真实原因。

  5. 范围备注(后续,不属于本 PR):同类 loose parser 仍在 —— workspace-git-log.ts:95-96?limit=7junk → 7)、session.ts:257workspace-status.ts:115workspace-channel-observed-contacts.ts:30。可留作 follow-up issue。

安全 / 性能:无影响。新旧都 clamp 到 [1, 366]buildHeatmap 是 O(records) 而非 O(days);^\d+$ 线性无回溯风险;多值参数(数组)仍走默认值,行为不变。

@wenshao

wenshao commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@wenshao
wenshao added this pull request to the merge queue Jul 25, 2026
Merged via the queue into QwenLM:main with commit 8edfa31 Jul 25, 2026
83 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants