Skip to content

feat(websocket): complete target topology and secure release gates - #98

Merged
unbraind merged 6 commits into
masterfrom
feat/complete-target-topology-secure-release
Aug 9, 2026
Merged

feat(websocket): complete target topology and secure release gates#98
unbraind merged 6 commits into
masterfrom
feat/complete-target-topology-secure-release

Conversation

@unbraind

@unbraind unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • add typed --include-secondary support to WebSocket target extraction and related-registry discovery while preserving the primary-only default
  • align help, README, API, agent, and LLM documentation with the Home Assistant Core 2026.8.1 contract and privacy-safe live 2026.7.4 acceptance
  • refresh current compatible tooling, remediate GHSA-2v37-7h3g-55p8 with nanoid 3.3.17, and force authoritative PM drift validation before release gates
  • regenerate the managed changelog from closed canonical PM work

Verification

  • bun run release:verify
  • 1,407 tests; exact 100/100/100/100 coverage
  • 134/134 source files with docstrings
  • Bun audit, Trivy, full-history secret scan, duplication and file-size gates
  • privacy-safe live E2E and actual hassio ws target extract --include-secondary acceptance
  • bun run release:auto:dry-run: target v2026.8.9, dry_run=true, pushed=false
  • independent tag, GitHub release, and npm checks remained unchanged

PM context

Addresses #97. Upstream PM defects are tracked in unbraind/pm-cli#779 and unbraind/pm-cli#955.

No package, tag, release, or release commit was created.

Summary by Sourcery

Add optional secondary-entity support to WebSocket target topology helpers while keeping primary-only behavior by default, and restore strict PM-based release validation and security compliance.

New Features:

  • Expose an --include-secondary flag on ws target extract and ws target related to include non-primary entities in target resolution when explicitly requested.

Enhancements:

  • Align WebSocket target helper docs, API references, README, and LLM guidance with Home Assistant’s primary vs secondary entity topology and agent privacy expectations.
  • Refine E2E live tests and PM quality gating so releases rely on an authoritative PM rescan via the installed CLI binary instead of ad-hoc latest installs.
  • Regenerate the managed changelog to capture the new target topology behavior and related PM work.

Build:

  • Update package scripts to use the locally installed pm CLI for changelog generation and PM quality gates, including runtime cache GC before validation.
  • Refresh dependencies for WebSocket, linting, duplication detection, TypeScript tooling, and pin nanoid via overrides for security.

Documentation:

  • Document --include-secondary usage, defaults, and privacy implications across API, optimization, LLM integration, releasing guide, and README examples.

Tests:

  • Extend WebSocket command unit tests and live E2E tests to cover explicit secondary-entity inclusion in extract and related target resolution flows.

Chores:

  • Add PM feature, plan, task, issue, and history artifacts to track target topology and the nanoid security advisory in project management tooling.

Summary by cubic

Adds optional secondary-entity support to ws target extract and ws target related while keeping the primary-only default. Refreshes docs/governance, pins nanoid to 3.3.17, and restores secure release gates with authoritative PM validation.

  • New Features

    • Add --include-secondary to ws target extract and ws target related; default stays primary-only.
    • Clarify help/docs that only extract and related accept --include-secondary (aligned with Home Assistant Core 2026.8.1).
  • Refactors

    • Type and share the extract_from_target payload across extract and related, deriving selector keys from typed options and returning an explicit Home Assistant payload; send primary_entities_only only when --include-secondary is set, and handle expand-group with secondary topology.
    • Synchronize PM test replay order and reuse the lockfile-installed @unbrained/pm-cli with an authoritative rescan to keep timeout and evidence metadata stable.

Written for commit ca287b1. Summary will update on new commits.

Review in cubic

Expose Home Assistant secondary entities through typed target extraction and related-registry discovery while preserving the primary-only default. Refresh audited tooling, remediate the nanoid advisory, and force authoritative PM drift validation after extension setup.
Close the implementation and security remediation items with exact coverage, live acceptance, automatic-release dry-run, and no-publish evidence. Regenerate the managed changelog from canonical PM state.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @unbraind, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Summary by CodeRabbit

  • New Features
    • Added optional --include-secondary support to target extraction and related-entity lookups.
    • Default results remain limited to primary entities for concise output.
  • Security
    • Remediated a Nanoid security advisory affecting release tooling.
  • Documentation
    • Updated command examples and guidance for expanded topology results, including privacy considerations.
    • Clarified release validation and cache-handling procedures.
  • Quality
    • Added coverage for expanded target discovery and completed release dry-run validation.

Walkthrough

Changes

The CLI adds opt-in secondary-entity discovery for WebSocket extract and related commands. Tests and documentation cover the new payload behavior. Dependency remediation, PM quality-gate cleanup, changelog entries, and delivery records are included.

Target topology and secure release

Layer / File(s) Summary
Secondary-entity extraction flow
src/commands/websocket-target.ts
Adds --include-secondary to extract and related. The flag sends primary_entities_only: false to Home Assistant.
Topology validation and documentation
tests/websocket-command.test.ts, tests/e2e-live.ts, README.md, docs/API.md, docs/AGENT_OPTIMIZATION.md, docs/LLM_INTEGRATION.md, CHANGELOG.md
Tests verify the payload. Examples and guidance describe primary-only defaults and secondary-entity inclusion.
Secure release tooling and quality gate
package.json, docs/RELEASING.md, .agents/pm/issues/hac-ndy5.toon, .agents/pm/history/hac-ndy5.jsonl
Updates tooling versions, the nanoid override, PM runtime cleanup, and advisory remediation records.
PM delivery records
.agents/pm/features/hac-17iu.toon, .agents/pm/tasks/hac-mw4f.toon, .agents/pm/plans/hac-0t0w.toon, .agents/pm/history/*
Records feature scope, task completion, execution steps, validation evidence, and release status.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant websocket_target as websocket-target.ts
  participant HomeAssistant
  CLI->>websocket_target: Run extract or related with --include-secondary
  websocket_target->>HomeAssistant: Send extract_from_target with primary_entities_only:false
  HomeAssistant-->>websocket_target: Return target topology
  websocket_target-->>CLI: Render extraction or related results
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the primary WebSocket topology and secure release gate changes.
Description check ✅ Passed The description explains the changes and rationale, documents extensive verification, and covers release and security impacts despite different section headings.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/complete-target-topology-secure-release

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements optional secondary-entity inclusion for WebSocket target extraction/related helpers while keeping primary-only behavior as the default, and reworks PM/release quality gates to use the locked pm CLI with added security remediation and docs alignment.

Sequence diagram for WebSocket target extract/related with optional secondary entities

sequenceDiagram
  actor User
  participant websocket_target_command as websocket_target_command
  participant HomeAssistantWebSocket as HomeAssistantWebSocket

  User->>websocket_target_command: hassio ws target extract --device-id abc123 [--include-secondary]
  websocket_target_command->>websocket_target_command: toTargetPayload(TargetOptions)
  websocket_target_command->>websocket_target_command: toExtractPayload(target, ExtractOptions)
  alt [includeSecondary is set]
    websocket_target_command->>HomeAssistantWebSocket: extract_from_target(ExtractPayload)
  else [includeSecondary not set]
    websocket_target_command->>HomeAssistantWebSocket: extract_from_target(ExtractPayload)
  end
  HomeAssistantWebSocket-->>websocket_target_command: ExtractedTarget

  User->>websocket_target_command: hassio ws target related --label-id lighting [--include-secondary]
  websocket_target_command->>websocket_target_command: toTargetPayload(TargetOptions)
  websocket_target_command->>websocket_target_command: toExtractPayload(target, ExtractOptions)
  alt [includeSecondary is set]
    websocket_target_command->>HomeAssistantWebSocket: extract_from_target(ExtractPayload)
  else [includeSecondary not set]
    websocket_target_command->>HomeAssistantWebSocket: extract_from_target(ExtractPayload)
  end
  HomeAssistantWebSocket-->>websocket_target_command: ExtractedTarget
Loading

Flow diagram for updated PM quality gate using locked pm CLI

flowchart LR
  A[quality:pm npm script] --> B[pm --version]
  B --> C[pm install npm:pm-changelog --project]
  C --> D[pm merge install]
  D --> E[pm gc --scope runtime --json]
  E --> F[pm validate --check-files --check-resolution --check-history-drift --strict-exit --json]
  F --> G[pm health --check-only --strict-exit --json]
Loading

File-Level Changes

Change Details Files
Add typed secondary-entity support to WebSocket target helpers and share a typed extract payload.
  • Extend TargetOptions with includeSecondary and introduce TargetOptionKey, ExtractOptions, and ExtractPayload types.
  • Restrict selector-key mapping to non-includeSecondary options via TargetOptionKey.
  • Add toExtractPayload helper that builds the Home Assistant extract_from_target payload and sets primary_entities_only only when includeSecondary is true.
  • Wire --include-secondary into ws target extract and related commands, passing the shared ExtractPayload to extract_from_target, including expand-group handling.
src/commands/websocket-target.ts
Verify new WebSocket behavior with unit and live E2E tests for secondary-entity inclusion.
  • Add tests asserting extract_from_target is called with primary_entities_only=false only when --include-secondary is provided for extract and related.
  • Update live E2E test to call ws target extract with --include-secondary and validate result shape.
tests/websocket-command.test.ts
tests/e2e-live.ts
Align CLI, agent, API, README, and LLM docs with primary-vs-secondary entity semantics and Home Assistant contracts.
  • Document --include-secondary usage for ws target extract in optimization, API, LLM integration, and README examples.
  • Clarify that extract and related default to primary entities and that --include-secondary should be used only for complete configuration/diagnostic topology due to privacy implications.
  • Explain PM quality gate behavior around runtime cache GC and authoritative validation in releasing docs.
docs/AGENT_OPTIMIZATION.md
docs/API.md
docs/RELEASING.md
docs/LLM_INTEGRATION.md
README.md
Harden PM/release governance by using the locked pm CLI binary and adding runtime GC/validation steps, and regenerate PM-managed changelog entries.
  • Change quality:pm script to use PM_CLI_BIN pointing at ./node_modules/.bin/pm, install pm-changelog via pm, run runtime-scope GC, then validate and health-check with strict exits.
  • Update changelog:pm scripts to invoke the local pm binary and generate/check CHANGELOG.md from closed PM items.
  • Regenerate CHANGELOG.md to include the nanoid security advisory and secondary-entity topology task, and add new PM feature/plan/task/issue files and history lines.
package.json
CHANGELOG.md
.agents/pm/features/hac-17iu.toon
.agents/pm/plans/hac-0t0w.toon
.agents/pm/tasks/hac-mw4f.toon
.agents/pm/issues/hac-ndy5.toon
.agents/pm/history/hac-0t0w.jsonl
.agents/pm/history/hac-17iu.jsonl
.agents/pm/history/hac-mw4f.jsonl
.agents/pm/history/hac-ndy5.jsonl
Refresh dependencies and remediate the nanoid security advisory with an override.
  • Bump ws, @unbrained/pm-cli, eslint, eslint-plugin-jsdoc, jscpd, and tsx to newer versions.
  • Override nanoid to 3.3.17 in package.json to address GHSA-2v37-7h3g-55p8.
  • Regenerate bun.lock to capture updated dependency graph.
package.json
bun.lock

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@greptileai please review the exact current head.

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@sourcery-ai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 59 minutes.

@sourcery-ai

sourcery-ai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Sorry @unbraind, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@codecov

codecov Bot commented Aug 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.agents/pm/history/hac-mw4f.jsonl:
- Around line 7-10: Regenerate the test-history mutations in
.agents/pm/history/hac-mw4f.jsonl lines 7-10 so every JSON Patch operation is
valid and replaying the sequence produces the canonical test order: focused
contract, coverage, then live acceptance. Synchronize
.agents/pm/tasks/hac-mw4f.toon lines 48-58 with that regenerated history output.

In @.agents/pm/history/hac-ndy5.jsonl:
- Around line 4-5: Update the linked-test normalization represented by the
history entries so reordering preserves every field from each original test
object, including timeout_seconds and note. Adjust the remove-and-add or schema
migration logic associated with metadata.tests to carry these fields into the
normalized entries, while retaining the existing command and scope values.

In `@docs/LLM_INTEGRATION.md`:
- Line 783: Update the “Resolve targets first” guidance to restrict
--include-secondary to the target extract and related subcommands, removing any
implication that it applies to triggers, conditions, or services. Keep those
subcommands listed for target resolution without the unsupported option.

In `@package.json`:
- Line 35: Update the quality:pm script so it resolves `@unbrained/pm-cli` once
per run, stores the exact resolved version, and reuses that version for merge
install, gc, validate, and health instead of independently invoking `@latest`.
Record the resolved version in the gate output while preserving the existing
command order and strict checks.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 11a56382-05ae-46a0-877f-dc28e6a28cac

📥 Commits

Reviewing files that changed from the base of the PR and between f3b6f09 and 49eba33.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • .agents/pm/features/hac-17iu.toon
  • .agents/pm/history/hac-0t0w.jsonl
  • .agents/pm/history/hac-17iu.jsonl
  • .agents/pm/history/hac-mw4f.jsonl
  • .agents/pm/history/hac-ndy5.jsonl
  • .agents/pm/issues/hac-ndy5.toon
  • .agents/pm/plans/hac-0t0w.toon
  • .agents/pm/tasks/hac-mw4f.toon
  • CHANGELOG.md
  • README.md
  • docs/AGENT_OPTIMIZATION.md
  • docs/API.md
  • docs/LLM_INTEGRATION.md
  • docs/RELEASING.md
  • package.json
  • src/commands/websocket-target.ts
  • tests/e2e-live.ts
  • tests/websocket-command.test.ts

Comment thread .agents/pm/history/hac-mw4f.jsonl
Comment thread .agents/pm/history/hac-ndy5.jsonl
Comment thread docs/LLM_INTEGRATION.md Outdated
Comment thread package.json Outdated
Reuse the single lockfile-installed pm-cli binary throughout governance and changelog gates, restore linked-test timeout and evidence metadata through PM, and clarify the subcommands that accept secondary topology.
@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Bot artifact disposition for reviewed head 49eba33 and fix head 17ae6f5:

  • CodeRabbit summary comment 5234034066: useful scope walkthrough and four inline findings. The embedded 0% docstring warning is a provider-side false result; the repository gate independently reports 134/134 source files (100%). Three findings produced fixes in 17ae6f5; the history rewrite suggestion was declined with replay and strict-validation evidence in-thread.
  • Sourcery guide 5234034327: useful and accurate change-flow summary; no finding.
  • CodeRabbit rate-limit receipt 5234034996: useful provider-availability evidence, not an approval or code finding.
  • Sourcery quota comment 5234035021 and review 4892667679: not useful for code quality; provider unavailable and not counted as approval.
  • Codecov comment 5234035832: useful exact confirmation that modified coverable lines are covered.
  • CodeRabbit review 4892677956: useful; all four inline findings were individually reacted to and answered, and all threads are resolved.

No bot artifact was treated as approval merely because its provider skipped or rate-limited.

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@greptileai please review the exact current head 17ae6f5.

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@sourcery-ai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 48 minutes.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The includeSecondary handling for both extract and related commands duplicates the payload construction logic; consider extracting a small helper (e.g., buildExtractPayload(target, expandGroup, includeSecondary)) to keep behavior consistent and reduce maintenance overhead.
  • In toTargetPayload, the inline tuple key union type ("entityId" | "deviceId" | ...) could be replaced with keyof TargetOptions or a dedicated alias to avoid divergence if the TargetOptions shape changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `includeSecondary` handling for both `extract` and `related` commands duplicates the payload construction logic; consider extracting a small helper (e.g., `buildExtractPayload(target, expandGroup, includeSecondary)`) to keep behavior consistent and reduce maintenance overhead.
- In `toTargetPayload`, the inline tuple key union type (`"entityId" | "deviceId" | ...`) could be replaced with `keyof TargetOptions` or a dedicated alias to avoid divergence if the `TargetOptions` shape changes.

## Individual Comments

### Comment 1
<location path="src/commands/websocket-target.ts" line_range="45-46" />
<code_context>
 function toTargetPayload(options: TargetOptions): Record<string, string[]> {
   const target: Record<string, string[]> = {};
-  const mappings: Array<[keyof TargetOptions, string]> = [
+  const mappings: Array<[
+    "entityId" | "deviceId" | "areaId" | "floorId" | "labelId",
+    string,
+  ]> = [
</code_context>
<issue_to_address>
**suggestion:** Consider typing `mappings` from `keyof TargetOptions` to avoid future drift when target options change.

The explicit literal union here omits `includeSecondary` but now partially duplicates `TargetOptions`’ keys. Deriving the type instead (e.g. `Exclude<keyof TargetOptions, "includeSecondary">`) would keep this mapping aligned with `TargetOptions` as keys are added or renamed, while still excluding non-target flags. That helps avoid the list drifting out of sync and introducing subtle bugs over time.

Suggested implementation:

```typescript
  floorId?: string;
  labelId?: string;
  includeSecondary?: boolean;
};

type TargetOptionKey = Exclude<keyof TargetOptions, "includeSecondary">;

type ExtractedTarget = {

```

```typescript
function toTargetPayload(options: TargetOptions): Record<string, string[]> {
  const target: Record<string, string[]> = {};
  const mappings: Array<[TargetOptionKey, string]> = [

```

1. Ensure that `TargetOptions`’ keys used in `mappings` (e.g. `entityId`, `deviceId`, `areaId`, `floorId`, `labelId`) are indeed part of `TargetOptions`; the new `TargetOptionKey` alias will otherwise cause type errors if any mapped key is not present.
2. If other non-target flags are added to `TargetOptions` in the future (similar to `includeSecondary`), they will automatically be excluded from `mappings` if you extend `TargetOptionKey`’s `Exclude` union accordingly.
</issue_to_address>

### Comment 2
<location path="tests/websocket-command.test.ts" line_range="284-294" />
<code_context>
     expect(parsed.related.labels).toHaveLength(1);
   });

+  it("can include secondary entities in related registry discovery", async () => {
+    call.mockResolvedValueOnce({});
+    await createWebsocketCommand().parseAsync([
+      "target", "related", "--area-id", "kitchen", "--include-secondary",
+    ], { from: "user" });
+    expect(call).toHaveBeenNthCalledWith(1, "extract_from_target", {
+      target: { area_id: ["kitchen"] },
+      expand_group: false,
+      primary_entities_only: false,
+    });
+  });
+
</code_context>
<issue_to_address>
**suggestion (testing):** Extend related-command tests to cover interaction with `--expand-group` when `--include-secondary` is also set.

Since related also supports `--expand-group`, please add a test that passes both `--expand-group` and `--include-secondary` and verifies the payload includes `expand_group: true` and `primary_entities_only: false`. This will confirm the flag wiring works correctly when both options are combined and help prevent regressions in group expansion with secondary entities.

```suggestion
  it("can include secondary entities in related registry discovery", async () => {
    call.mockResolvedValueOnce({});
    await createWebsocketCommand().parseAsync([
      "target", "related", "--area-id", "kitchen", "--include-secondary",
    ], { from: "user" });
    expect(call).toHaveBeenNthCalledWith(1, "extract_from_target", {
      target: { area_id: ["kitchen"] },
      expand_group: false,
      primary_entities_only: false,
    });
  });

  it("can include secondary entities in related registry discovery when expanding groups", async () => {
    call.mockResolvedValueOnce({});
    await createWebsocketCommand().parseAsync([
      "target", "related", "--area-id", "kitchen", "--include-secondary", "--expand-group",
    ], { from: "user" });
    expect(call).toHaveBeenNthCalledWith(1, "extract_from_target", {
      target: { area_id: ["kitchen"] },
      expand_group: true,
      primary_entities_only: false,
    });
  });
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/commands/websocket-target.ts Outdated
Comment thread tests/websocket-command.test.ts
Derive selector keys from TargetOptions, share extract_from_target payload construction across both commands, and assert combined group expansion with secondary topology. Record the exact review validation in the PM plan.
Append a canonical PM test reset and restore sequence so raw history replay and the task snapshot end in the same order while retaining timeout and evidence metadata.
@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Second-loop bot disposition:

  • CodeRabbit rate-limit comment 5234081045 is useful provider evidence only; it produced no new exact-head review.
  • CodeRabbit acknowledgment reviews 4892688588, 4892688620, and 4892689095 and their inline replies were useful; each was reacted to and answered. The remaining raw replay-order concern is fixed through append-only PM history in 2e2535f.
  • Sourcery review 4892690742 and inline comments 3745446983/3745446985 were useful. Both maintainability/test suggestions are fixed in 5c0e53d, reacted to, answered, and resolved.
  • Updated CodeRabbit and Sourcery overview comments remain accurate walkthroughs.

All hosted checks were green on 17ae6f5; the current head will be re-reviewed and rechecked because later review fixes were pushed.

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@greptileai please review the exact current head 2e2535f.

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@sourcery-ai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Rate Limit Exceeded

@unbraind have exceeded the limit for the number of chat messages per hour. Please wait 14 minutes and 22 seconds before sending another message.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In TargetOptions and the new toExtractPayload helper, consider explicitly documenting or constraining when includeSecondary is honored (only for extract/related) to avoid accidental reuse by other target subcommands in future changes.
  • The PM CLI path and invocation logic is now duplicated between the quality:pm and changelog:pm:* scripts; consider centralizing the PM_CLI_BIN usage so both paths consistently respect the same configuration and environment.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `TargetOptions` and the new `toExtractPayload` helper, consider explicitly documenting or constraining when `includeSecondary` is honored (only for `extract`/`related`) to avoid accidental reuse by other target subcommands in future changes.
- The PM CLI path and invocation logic is now duplicated between the `quality:pm` and `changelog:pm:*` scripts; consider centralizing the `PM_CLI_BIN` usage so both paths consistently respect the same configuration and environment.

## Individual Comments

### Comment 1
<location path="src/commands/websocket-target.ts" line_range="66-71" />
<code_context>
   return target;
 }

+function toExtractPayload(
+  target: Record<string, string[]>,
+  options: TargetOptions & { expandGroup: boolean }
+): Record<string, unknown> {
+  const payload: Record<string, unknown> = { target, expand_group: options.expandGroup };
+  if (options.includeSecondary) payload["primary_entities_only"] = false;
+  return payload;
+}
</code_context>
<issue_to_address>
**suggestion:** Make `toExtractPayload` signature and return type more specific to improve type safety.

The function only uses `expandGroup` and `includeSecondary`, so you can narrow the `options` parameter to `{ expandGroup: boolean; includeSecondary?: boolean }` and introduce a dedicated payload type such as `{ target: Record<string, string[]>; expand_group: boolean; primary_entities_only?: boolean }`. This makes the `client.call("extract_from_target", ...)` contract explicit and improves type safety if the backend payload shape changes.

Suggested implementation:

```typescript
type ExtractOptions = {
  expandGroup: boolean;
  includeSecondary?: boolean;
};

type ExtractPayload = {
  target: Record<string, string[]>;
  expand_group: boolean;
  primary_entities_only?: boolean;
};

function toExtractPayload(
  target: Record<string, string[]>,
  options: ExtractOptions
): ExtractPayload {
  const payload: ExtractPayload = {
    target,
    expand_group: options.expandGroup,
  };

  if (options.includeSecondary) {
    payload.primary_entities_only = false;
  }

  return payload;
}


```

Anywhere `toExtractPayload` is called, you should ensure the second argument conforms to `ExtractOptions` (i.e. it has `expandGroup` and optionally `includeSecondary`). If `TargetOptions & { expandGroup: boolean }` was relied on elsewhere, you may need to refactor those call sites to construct an `ExtractOptions` object explicitly or adjust their type annotations to match the new `ExtractOptions` type.
</issue_to_address>

### Comment 2
<location path="tests/websocket-command.test.ts" line_range="203-200" />
<code_context>
     });
   });

+  it("includes secondary entities only when explicitly requested", async () => {
+    call.mockResolvedValueOnce({});
+    await createWebsocketCommand().parseAsync([
+      "target", "extract", "--device-id", "device-1", "--include-secondary",
+    ], { from: "user" });
+    expect(call).toHaveBeenCalledWith("extract_from_target", {
+      target: { device_id: ["device-1"] },
+      expand_group: false,
+      primary_entities_only: false,
+    });
+  });
+
</code_context>
<issue_to_address>
**suggestion (testing):** Add a complementary test for the default primary-only behavior when --include-secondary is not provided

This test confirms the opt-in path for secondary entities. To also cover the privacy-safe default, please add a matching test (e.g. `it("defaults to primary entities when --include-secondary is omitted" ...)`) that runs `hassio ws target extract --device-id device-1` without the flag and asserts the payload has the expected default for `primary_entities_only` (or omits it, per the server contract). This will guard against regressions where secondary entities are included without explicit opt-in.

Suggested implementation:

```typescript
  it("includes secondary entities only when explicitly requested", async () => {
    call.mockResolvedValueOnce({});
    await createWebsocketCommand().parseAsync([
      "target", "extract", "--device-id", "device-1", "--include-secondary",
    ], { from: "user" });
    expect(call).toHaveBeenCalledWith("extract_from_target", {
      target: { device_id: ["device-1"] },
      expand_group: false,
      primary_entities_only: false,
    });
  });

  it("defaults to primary entities when --include-secondary is omitted", async () => {
    call.mockResolvedValueOnce({});
    await createWebsocketCommand().parseAsync([
      "target", "extract", "--device-id", "device-1",
    ], { from: "user" });
    expect(call).toHaveBeenCalledWith("extract_from_target", {
      target: { device_id: ["device-1"] },
      expand_group: false,
      primary_entities_only: true,
    });
  });

```

If the server contract is to omit `primary_entities_only` when using the default behavior (instead of explicitly sending `true`), update the new test’s expectation accordingly, e.g. by removing `primary_entities_only` from the expected payload or by asserting that it is `undefined` on the argument passed to `call`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/commands/websocket-target.ts Outdated
Comment thread tests/websocket-command.test.ts
Narrow the shared builder inputs and return an explicit Home Assistant extract_from_target payload type while preserving the opt-in secondary-entity wire contract.
@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Third-loop bot disposition:

  • CodeRabbit confirmation 3745458563 was useful and confirms raw PM replay now matches the task snapshot. Two hourly chat-limit replies were not useful for code quality and are recorded only as provider limitations.
  • Sourcery review 4892700259 was useful overall. Its payload-typing finding is fixed in ca287b1. The requested additional default test was declined because two existing tests already assert omission for extract and related, while the opt-in test asserts false; exact branch coverage is 100%.
  • The package scripts already share the same lockfile-installed ./node_modules/.bin/pm path. A further wrapper would add indirection without changing version resolution or environment behavior.
  • CodeRabbit remains rate-limited on the current head; this is not counted as approval.

Every new comment/review was reacted to and answered at its actual GitHub surface.

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@greptileai please review the exact current head ca287b1.

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@sourcery-ai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Rate Limit Exceeded

@unbraind have exceeded the limit for the number of chat messages per hour. Please wait 9 minutes and 27 seconds before sending another message.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Final exact-head provider disposition for ca287b1:

  • Required GitHub checks are all green: CodeQL, dependency audit, Trivy, history scan, changelog, coverage/Codecov, package smoke, Bun/Node quality, static contracts, title, and commit audit.
  • Sourcery completed on the exact head with no new review thread or finding; its earlier two findings are resolved or evidence-backed declined.
  • CodeRabbit could not run another full review because of provider limits. Its new top-level receipt 5234123747, reviews 4892706936/4892706991/4892707053, and inline receipts 3745469370/3745469438/3745469552 are repetitive quota artifacts, were down-voted, and contain no finding.
  • Greptile and Gemini were requested on every pushed head but produced no response. Their silence is recorded as provider unavailability, not approval.
  • All seven review threads are resolved, and every bot comment/review has a usefulness reaction plus a thread or consolidated disposition.

No actionable finding remains on the exact current head.

@unbraind

unbraind commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Sourcery exact-head review 4892709762 is useful confirmation that its final pass found no remaining issue; up-voted. It supersedes the earlier two-finding Sourcery loop, whose findings were already resolved or declined with exact existing-test evidence.

@unbraind
unbraind merged commit e73df8a into master Aug 9, 2026
22 checks passed
@unbraind
unbraind deleted the feat/complete-target-topology-secure-release branch August 9, 2026 22:18
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