Skip to content

Feat/iteration 1 p0 4 5 8#30

Merged
chenliuyun merged 22 commits intomainfrom
feat/iteration-1-p0-4-5-8
Apr 25, 2026
Merged

Feat/iteration 1 p0 4 5 8#30
chenliuyun merged 22 commits intomainfrom
feat/iteration-1-p0-4-5-8

Conversation

@chenliuyun
Copy link
Copy Markdown
Collaborator

♻️ Current situation

Describe the current situation. Explain current problems, if there are any. Be as descriptive as possible (e.g., including examples or code snippets).

💡 Proposed solution

Describe the proposed solution and changes. How does it affect the project? How does it affect the internal structure (e.g., refactorings)?

⚙️ Release Notes

Provide a summary of the changes or features from a user's point of view. If there are breaking changes, provide migration guides using code examples of the affected features.

➕ Additional Information

If applicable, provide additional context in this section.

Testing

Which tests were added? Which existing tests were adapted/changed? Which situations are covered, and what edge cases are missing?

Reviewer Nudging

Where should the reviewer start? what is a good entry point?

chenliuyun added 22 commits April 24, 2026 23:34
…ctor

- output.ts: add `resolutionHint` and `candidateMatches` as top-level fields in
  ErrorPayload; promote from StructuredUsageError context on ambiguous-name-match
  and other structured errors; backward-compat context preserved unchanged
- output.ts: add `ambiguous-name-match` to ErrorSubKind enum
- output.ts: normalize candidate shapes (sceneName → name) across devices/scenes
- doctor.ts: new `path` check — detects whether the switchbot binary is reachable
  on PATH via npm global bin; emits fix hint with shell-specific export command
- json-contract.md: document resolutionHint, candidateMatches, and subKind fields
- tests: 4 new doctor tests for path check; 30→34 total doctor tests; 1765→1769 total
P0-2: add RiskLevel/IdempotencyHint/RecommendedMode types and
deriveRiskMeta() to capabilities.ts; enumerateLeaves() and commandMeta
now include riskLevel, requiresConfirmation, supportsDryRun,
idempotencyHint, recommendedMode for every command leaf.

P0-3: add planId?: string to AuditEntry; executeCommand() accepts
planId in options and stamps it on every audit line produced during
plan run; plan run now generates a randomUUID() planId at invocation
start for cross-step correlation. Destructive guard hint in
devices command also mentions plan run --require-approval as an
alternative to --yes.
P0-6: extend policy schema v0.2 rule definition with three new optional
safety fields — cooldown (shorthand throttle), throttle.dedupe_window
(deduplicate rapid sensor bursts), and requires_stable_for (hysteresis
guard, schema-validated). ThrottleGate.check() gains a dedupeWindowMs
parameter; ThrottleCheckResult adds dedupedBy field. lintRules() validates
all three new fields and warns when cooldown and throttle.max_per overlap.
dispatchRule() applies cooldown precedence and dedupe_window checks.

P0-7: new src/rules/conflict-analyzer.ts detects opposing-action pairs,
high-frequency catch-all MQTT rules without throttle, and destructive
commands. rules.ts gains `rules conflicts` and `rules doctor` subcommands
(combined lint + conflict analysis); both support --json for CI pipelines.
…in-first hints

P1-8: add CircuitBreaker class to utils/retry.ts; wire module-level
apiCircuitBreaker singleton in api/client.ts — opens after 5 consecutive
5xx/network failures, auto-probes after 60 s; 4xx excluded from failure count.

P1-2: add utils/health.ts (quota, audit error-rate, circuit state, process)
and commands/health.ts (`switchbot health`); supports --prometheus output
for Prometheus/Grafana scraping.

P1-5: add --explain flag to `devices command` — prints risk level, device
type, and safety reason before execution without sending the request.

P1-3: keychain-first credential hints — doctor `checkKeychain()` check
reports when file-backend is in use; config set-token prints a tip to run
`switchbot auth keychain store` when on darwin/win32/linux with a native
keychain available.

tests: reset apiCircuitBreaker in beforeEach to prevent state bleed across
client.test.ts cases.
…te, rules summary

P1-1: add `switchbot daemon` command with start/stop/status subcommands.
start spawns `rules run` as a detached background process (cross-platform),
writes PID to ~/.switchbot/daemon.pid and stdout/stderr to daemon.log.
stop sends SIGTERM; status reports running state.

P1-4: add `switchbot upgrade-check` — fetches latest version from npm
registry, compares semver, prints update instructions; exits 1 when stale.

P1-6: add `scenes validate [sceneId...]` (confirms scene IDs exist) and
`scenes simulate <sceneId>` (shows what execute would POST without
sending the request). Both support --json output.

P1-7: add `rules summary` (aggregate fire/throttle/error counts per rule
over a configurable time window; table output via printTable) and
`rules last-fired` (show N most recent rule-fire/rule-fire-dry entries
sorted newest-first; --rule filter supported).

bump: 3.1.0 → 3.2.0
…rimitives

P0-3: Add plan save/list/review/approve/execute lifecycle with persistent
PlanRecord store at ~/.switchbot/plans/<planId>.json. Plans flow through
pending → approved → executed; plan execute enforces approved gate;
all steps emit audit entries with planId for traceability.

P0-2: MCP send_command now returns riskProfile per action (riskLevel,
requiresConfirmation, supportsDryRun, idempotencyHint, recommendedMode)
computed from actual device type and command at request time.

P0-6: Rules engine enforces maxFiringsPerHour (sliding 3600s count window
via ThrottleGate.checkMaxFirings/recordFire), suppressIfAlreadyDesired
(skips turnOn/turnOff when fetchStatus confirms powerState already matches),
and hysteresis/requires_stable_for (fires only after trigger is continuously
stable for the configured duration). Schema v0.2.json and types.ts updated.
…JSON contract

plan execute: only transition to 'executed' when summary.error===0 &&
summary.skipped===0; otherwise write 'failed' with failedAt and
failureReason so the plan can be re-approved and retried. Add 'failed'
to PlanStatus in plan-store.ts.

plan subcommands: replace bare console.error+process.exit with
exitWithError() in review/approve/execute so --json always emits the
standard { error: { code, kind, message, context } } envelope. Fix
plan approve JSON response to use ok:true instead of approved:true.

hysteresis: clear hysteresisFirstSeen when conditions are unmatched so
intermittent satisfaction never accumulates into a false "stable" window.
Add ingestEventForTest() to RulesEngine for time-controlled MQTT testing.

idempotencyHint: compute from CommandSpec.idempotent in the device catalog
instead of deriving from isDestructive, fixing toggle (non-idempotent) and
unlock (idempotent:true despite being destructive). Extract buildRiskProfile()
helper used by both dryRun and live paths of send_command.

Tests: add plan-resource.test.ts (11 cases) covering execute status
transitions, retry-after-failure, and JSON error envelopes; extend
engine.test.ts with hysteresis first-seen, stability, and reset-on-mismatch
cases; extend mcp.test.ts with 5 idempotencyHint cases (turnOn/toggle/unlock
dry+live).
…ppressIfAlreadyDesired verb extraction, and JSON error contracts

- plan execute: marks status=failed (with failureReason) on error/skipped steps; only sets executed on full success; allows re-approve of failed plans
- engine: extract verb via parseRuleCommand in suppressIfAlreadyDesired so "devices command <id> turnOn" format is recognized
- engine: ingestEventForTest now increments stats.eventsProcessed
- plan save: validation error path uses exitWithError() for proper JSON error envelope
- tests: add regression tests for maxFiringsPerHour (cap + sliding-window reset), suppressIfAlreadyDesired (suppress/allow/best-effort), and CircuitBreaker state machine (closed→open→half-open→closed)
…cution hint

Adds `scenes explain <sceneId>` to complete the validate/simulate/explain triad.
The command fetches the scene by ID and emits a human-readable explanation
including riskLevel (always "low" for scenes), idempotency note, and the exact
command to run. JSON mode returns a structured envelope compatible with
Agent/MCP consumers.
- Refactor `health` command into `health check` (one-shot report) and
  `health serve` (long-running HTTP server)
- health serve exposes GET /healthz (JSON, 503 when circuit open) and
  GET /metrics (Prometheus text); handler extracted as createHealthHandler()
  for direct test use without binding a port
- daemon start: add --healthz-port option that spawns a `health serve`
  process alongside the rules daemon and writes its PID to healthz.pid
- policy backup [file]: copies active policy to <policy>.bak.yaml (or explicit path); refuses to overwrite without --force; JSON envelope includes source, dest, sizeBytes
- policy restore <file>: validates backup against schema before overwriting active policy; auto-saves pre-restore backup; --no-validate bypasses schema check; error paths use exitWithError per JSON error contract
…ofile template command

doctor --json now includes maturityScore (0–100) and maturityLabel
(production-ready/mostly-ready/needs-work/not-ready) computed from
the ok/warn/fail check distribution.

config agent-profile emits or writes a conservative starter profile
(dailyCap: 100, auditLog: true) for AI agent / MCP integration,
with --write and --force options.

9 new tests cover both features.
…nd quiet-hours gap detection

- rules explain <name> [path]: new subcommand showing trigger, conditions,
  actions, cooldown, hysteresis, maxFiringsPerHour, suppressIfAlreadyDesired,
  and last-fired time from the audit log; supports --json
- upgrade-check --json: add breakingChange boolean (true when latestMajor >
  currentMajor) so callers can gate on API-incompatible upgrades
- conflict-analyzer: add pattern 4 — event-driven (mqtt/webhook) rules without
  a time_between guard flagged as warnings when quiet_hours is configured in
  policy; analyzeConflicts() now accepts optional quietHours param (backward
  compatible); rules conflicts and rules doctor both thread it through
- tests: 4 rules explain tests, 3 breakingChange tests, 8 conflict-analyzer
  unit tests (98 files, 1856 tests green)
…, agent-profile, maturityScore, scenes explain, rules explain/conflicts/doctor, quiet-hours, policy backup/restore, test count 1856
@chenliuyun chenliuyun merged commit 361476c into main Apr 25, 2026
6 checks passed
@chenliuyun chenliuyun deleted the feat/iteration-1-p0-4-5-8 branch April 25, 2026 04:44
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