Skip to content

feat: 2.5.0 — history aggregate + v2.4.0 report bug fixes#19

Merged
chenliuyun merged 25 commits intomainfrom
docs/history-aggregation-spec
Apr 20, 2026
Merged

feat: 2.5.0 — history aggregate + v2.4.0 report bug fixes#19
chenliuyun merged 25 commits intomainfrom
docs/history-aggregation-spec

Conversation

@chenliuyun
Copy link
Copy Markdown
Collaborator

@chenliuyun chenliuyun commented Apr 20, 2026

Summary

This PR ships switchbot-cli 2.5.0. Two logical halves bundled into one release:

Half 1 — History aggregation feature (design + implementation)

  • New history aggregate <deviceId> CLI subcommand and MCP aggregate_device_history tool
  • Per-device bucketed statistics (count / min / max / avg / sum / p50 / p95) streamed over the existing JSONL rotation files — zero storage format change
  • Flags: --since / --from / --to, repeatable --metric, --agg <csv>, --bucket <dur>, --max-bucket-samples <n>
  • Memory-bounded: hard ceiling at maxBucketSamples × 8 bytes per (bucket × metric) cell for quantile computation
  • Design spec: docs/superpowers/specs/2026-04-20-device-history-aggregation-design.md
  • Implementation plan: docs/superpowers/plans/2026-04-20-device-history-aggregation.md

Half 2 — v2.4.0 smoke-test report bug fixes

Closes ~15 of the 18 items in the OpenClaw v2.4.0 bug report. Security, correctness, and drift between release-note claims and shipped behavior.

Security & correctness:

Release-note drift fixed:

Polish:

Explicitly deferred (documented in CHANGELOG):

Test plan

  • npm run build clean
  • npm test895 / 895 passing (including the previously-flaky events.test.ts > rejects oversized bodies with 413)
  • New test files: tests/devices/history-agg.test.ts, tests/commands/strict-schemas.test.ts, tests/commands/dry-run.test.ts, tests/mcp/server-version.test.ts, tests/mcp/tool-meta.test.ts, tests/utils/color-flag.test.ts
  • Existing test suites extended: history.test.ts, name-resolver.test.ts, format.test.ts, cache.test.ts, events.test.ts, scenes.test.ts

Why bundle into one release

External v2.4.0 assessment scored the project 7.8/10, with release-note credibility the weakest dimension. Shipping a separate 2.4.1 would have delayed the security fix (MCP dryRun) behind 2.5.0 feature work. All bug-fix items are additive or bug-for-bug compatible; zero breaking changes.

chenliuyun added 5 commits April 20, 2026 14:58
Design for 2.5.0 on-demand bucketed aggregation over existing JSONL
storage. Additive only: new `history aggregate` CLI subcommand, new
`aggregate_device_history` MCP tool, backed by a pure async function
that reuses history-query.ts streaming primitives.

Scope: count/min/max/avg/sum/p50/p95 per (bucket × metric), per-device
only. Explicit non-goals: trend helpers, cross-device aggregation,
storage migration, streaming subscriptions.
@chenliuyun chenliuyun closed this Apr 20, 2026
@chenliuyun chenliuyun deleted the docs/history-aggregation-spec branch April 20, 2026 08:09
chenliuyun added 9 commits April 20, 2026 16:14
The test now uses a record with a timestamp inside the 5m query window
but file mtime outside it. Before the fix, the record was 3 days old,
so it failed the per-record timestamp filter even without mtime pruning.

The test now correctly verifies the mtime prune is necessary. Verified
by temporarily disabling mtime check in history-agg.ts; test fails with
count===2 (both files' records included) when prune is disabled.
Register aggregate_device_history MCP tool delegating to aggregateDeviceHistory
from history-agg.ts; uses z.object().strict() to reject unknown input keys;
annotated with _meta.agentSafetyTier='read'. Bumps tool count test to 11.
…story

Add 'history aggregate' to COMMAND_META as a read-tier leaf (80ms latency).
Append 'aggregate_device_history' to MCP_TOOLS array.
Register history.aggregate subcommand in test program.
@chenliuyun chenliuyun reopened this Apr 20, 2026
chenliuyun added 11 commits April 20, 2026 17:14
… fix for bug #4)

Convert all 10 plain-object inputSchemas to z.object({...}).strict() so unknown
keys are rejected with JSON-RPC -32602, closing the silent-passthrough hole that
allowed dryRun:true (or any unknown key) to reach the Smart Lock / Garage Door
live API calls. Add dryRun:boolean?.optional() to send_command and run_scene;
when true, returns { ok:true, dryRun:true, wouldSend:{...} } without any API call.
MCP initialize response now reports accurate serverInfo.version from package.json
instead of hardcoded '2.0.0'. This fixes misleading version to MCP clients that
gate capabilities on reported version. Updated four references in mcp.ts
(initialize response, account_overview, /healthz, /ready endpoints) to use
centralized VERSION constant imported from version.ts.
The v2.4.0 release notes claimed "MCP tools mirror the tier in
meta.agentSafetyTier" but only aggregate_device_history (added in 2.5.0
work) actually exposed it. This fix adds _meta: { agentSafetyTier: <tier> }
to all other 10 MCP tool registrations, matching their CLI safety tiers
from COMMAND_META:
- list_devices, get_device_status, get_device_history, query_device_history,
  list_scenes, search_catalog, describe_device, account_overview: read
- send_command, run_scene: action

Also adds tests/mcp/tool-meta.test.ts to verify every tool has _meta and
spot-check key tiers match expected values.

Fixes bug #6.
…tch (bug #1)

Under require-unique, the exact-name short-circuit at line 73 returned
immediately on the first exact hit, bypassing the ambiguity check entirely.
This meant a query like '空调' on an account with both '空调' (exact) and
'卧室空调' (substring) silently resolved to the exact device instead of
raising ambiguous_name_match (exit 2).

Fix: gate the short-circuit by strategy. For require-unique, the exact hit is
pushed as a score-0 candidate and the full candidate list is assembled before
the ambiguity check runs. Fuzzy/first/exact/prefix/substring behaviour is
unchanged.

Tests added (4 new):
- exact + substring under require-unique → ambiguous (reporter scenario)
- two exact-same-name devices under require-unique → ambiguous
- single exact match, no other matches under require-unique → resolves OK
- fuzzy still short-circuits on exact match (regression pin)
Adds .alias('status') to the 'cache show' subcommand registration so that
'switchbot cache status' works alongside 'switchbot cache show', matching
the parallel quota command's UX.
…yle (bug #8)

v2.4.0 release note claimed '--format markdown' was supported but actual
behavior was 'Unknown --format markdown' exit 2. --table-style markdown
was already wired; --format markdown now routes through printTable with
an explicit style override, independent of --table-style.
)

Add fileMissing flag to VerifyReport. When audit.log does not exist (fresh install),
return fileMissing=true, exit with 0, and set status to 'warn' in JSON output.
Existing behavior preserved for malformed/unversioned content (still exits 1).

Tests: 6 new tests added to verify the behavior: fresh account, empty file,
malformed entries, and JSON output variants.
Add --no-color as a root-level option to suppress ANSI colors in output.
Also respect the NO_COLOR environment variable per https://no-color.org/.
Early initialization sets chalk.level=0 when either flag or env is set,
ensuring colors are disabled globally before any commands execute.
v2.4.0 release note claimed every mqtt-tail event is appended to device
history, but control events (__connect, __reconnect, __disconnect,
__heartbeat) have no deviceId and were only emitted to stdout. Route
them into a dedicated ~/.switchbot/device-history/__control.jsonl
alongside the per-device files, sharing the same 50MB rotation logic.
Errors are swallowed — history writes never block the event stream.
C1 (#14): update --idempotency-key / --idempotency-key-prefix help text
  in devices.ts and batch.ts to mention process-local scope, per-process
  cache semantics, and that independent CLI invocations do not share cache.

C2 (#15): mcp --help "eight tools" → "eleven tools"; list all 11 by name
  including get_device_history, query_device_history, aggregate_device_history.

C3 (#17): add `scenes describe <sceneId>` subcommand. Returns sceneId,
  sceneName, stepCount:null, and a note explaining v1.1 API limitation.
  Exits 2 with scene_not_found + candidate list on unknown sceneId.
  Adds 'scenes describe' to COMMAND_META in capabilities.ts.
  Adds 2 tests (known + unknown scene).

C4 (#13): add JSDoc comment on hints field in agent-bootstrap.ts clarifying
  empty array semantics. Add 'hints' field to cliAddedFields in schema.ts.

C5 (#16): create docs/verbose-redaction.md documenting masked headers
  (authorization, token, sign, nonce, x-api-key, cookie, set-cookie,
  x-auth-token, t) and the --trace-unsafe opt-out flag.

C6 (#18): plan schema output now includes agentNotes.deviceNameStrategy
  documenting that deviceName uses require-unique resolution and plans
  should pin deviceId for determinism.
Document every fix landed in this branch beyond the history-aggregate
feature: bugs #1, #4, #5, #6, #8, #9, #10, #11, #12, #13, #14, #15,
#16, #17, #18 from the OpenClaw v2.4.0 smoke-test report. Call out
the deferred items (#2, #7) explicitly so readers don't assume they
were overlooked.
@chenliuyun chenliuyun changed the title docs: device-history-aggregation design spec (2.5.0) feat: 2.5.0 — history aggregate + v2.4.0 report bug fixes Apr 20, 2026
@chenliuyun chenliuyun force-pushed the docs/history-aggregation-spec branch from 04419fb to 90f07be Compare April 20, 2026 10:07
@chenliuyun chenliuyun merged commit 2a537ff into main Apr 20, 2026
4 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.

1 participant