feat(catalog): vendored device-catalog enrichment + auto-refresh loop - #45
Conversation
…to-refresh (#43, #44) Vendor a snapshot of the community indigo-device-catalog (210 profiles, XKCD-table pattern: plain dict, zero network/IO at runtime) and use it three ways: - get_device_by_id now returns a `capabilities` block (boolean flag map) when the device's (pluginId, deviceTypeId) matches a catalog profile; the key is absent when uncataloged. - The colour/white control tools (set_rgb_color/percent, set_hex_color, set_named_color, set_white_levels) pre-check the relevant capability flag and refuse with a friendly ValueError naming what the device DOES support. Advisory only: no profile, missing flag, or unreadable device all proceed exactly as before. - New `list_uncataloged_devices` tool — the catalog-contribution gap report (plugin devices with no profile, standard paginated envelope). Snapshot pipeline (#44): scripts/generate_catalog_snapshot.py emits a deterministic Server Plugin/catalog_snapshot.py (provenance from the catalog checkout's git HEAD, never the wall clock). A new catalog-refresh workflow listens for repository_dispatch (catalog-updated) + workflow_dispatch, regenerates from catalog@main, and opens a patch-bump PR when the snapshot changed. The catalog-side dispatch workflow is documented in docs/catalog-dispatch-workflow.yml for Simon to copy into indigo-device-catalog (needs LITE_DISPATCH_TOKEN PAT; cross-repo commit out of scope here). Note: the catalog schema carries no role/polarity semantics (garage inverted onState etc.) — capabilities is the whole enrichment for now; role/polarity is a catalog-side schema follow-up. Version 2026.8.1 → 2026.9.0; README tool table regenerated (72 tools, completeness check green); 450 tests pass (34 new). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYAGXd4bE9D9Z5kZeSHeo7
📝 WalkthroughWalkthroughThe plugin now vendors device-catalog profiles, enriches device lookups, gates unsupported colour and white controls, reports uncataloged plugin devices, and automates snapshot refreshes through GitHub Actions. ChangesCatalog integration
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant MCPClient
participant LookupTools
participant Catalog as catalog.py
participant Snapshot as catalog_snapshot.py
MCPClient->>LookupTools: request device detail or uncataloged devices
LookupTools->>Catalog: profile_for(device)
Catalog->>Snapshot: read PROFILES by pluginId and deviceTypeId
Snapshot-->>Catalog: matching profile or no profile
Catalog-->>LookupTools: profile result
LookupTools-->>MCPClient: enriched detail or paginated results
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 @.github/workflows/catalog-refresh.yml:
- Around line 31-35: Before enabling the catalog refresh workflow, add a
workspace ADR using the existing ADR template documenting the new GitHub network
dependency introduced by the “Clone device catalog @ main” step. Reconcile the
README’s offline vendored-snapshot description with this behavior, and keep the
workflow change gated until the ADR and documentation are updated.
- Around line 24-26: Add workflow-level concurrency for the catalog refresh
workflow, using a shared group such as chore/catalog-refresh and setting
cancel-in-progress to false. Anchor the change near the refresh-snapshot job and
preserve the existing force-push behavior while ensuring repository_dispatch and
workflow_dispatch runs execute serially.
- Around line 9-11: Update the refresh workflow’s authentication used by
checkout, push, and gh pr create so it uses a GitHub App installation token or
appropriately scoped PAT instead of the default GITHUB_TOKEN, allowing
pull_request workflows such as version-check.yml to run automatically;
alternatively, explicitly configure the affected required workflow to use manual
approval.
In `@scripts/generate_catalog_snapshot.py`:
- Around line 143-144: Update the snapshot rendering loop in the generator to
wrap long profile literals deterministically so every emitted line is at most
120 characters while preserving valid output and stable ordering. Extend
test_render_is_deterministic to assert the maximum generated line length does
not exceed 120 characters.
🪄 Autofix (Beta)
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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f1966916-8aca-4393-a91b-e6792e37c408
📒 Files selected for processing (16)
.github/workflows/catalog-refresh.ymlCLAUDE.mdIndigo MCP Lite.indigoPlugin/Contents/Info.plistIndigo MCP Lite.indigoPlugin/Contents/Server Plugin/catalog.pyIndigo MCP Lite.indigoPlugin/Contents/Server Plugin/catalog_snapshot.pyIndigo MCP Lite.indigoPlugin/Contents/Server Plugin/tools/control.pyIndigo MCP Lite.indigoPlugin/Contents/Server Plugin/tools/lookup.pyREADME.mddocs/catalog-dispatch-workflow.ymlscripts/generate_catalog_snapshot.pytests/test_catalog_profile_for.pytests/test_catalog_snapshot_generator.pytests/test_control_capability_checks.pytests/test_lookup_catalog_enrichment.pytests/test_lookup_uncataloged.pytests/test_mcp_dispatch_integration.py
| jobs: | ||
| refresh-snapshot: | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Files:"
git ls-files .github/workflows || true
echo
echo "catalog-refresh workflow:"
if [ -f .github/workflows/catalog-refresh.yml ]; then
cat -n .github/workflows/catalog-refresh.yml
else
echo "missing .github/workflows/catalog-refresh.yml"
fi
echo
echo "Search catalog-refresh references:"
rg -n "catalog-refresh|chore/catalog-refresh|git.*(push|reset|checkout)|workflow_dispatch|schedule" .github/workflows || trueRepository: simons-plugins/indigo-mcp-lite
Length of output: 5283
🌐 Web query:
GitHub Actions concurrency group workflow cancel-in-progress fork pull request same workflow default
💡 Result:
To implement concurrency with cancel-in-progress: true in GitHub Actions for workflows that handle pull requests—including those from forks—you must carefully define your concurrency group key to ensure proper isolation and prevent unintended cancellations [1][2][3]. The concurrency key is scoped to the entire repository [4]. If multiple workflows use the same static or loosely defined key, they will cancel each other's runs regardless of the branch or PR [4][5]. Best Practices and Implementation: 1. Recommended Key Structure: Use a combination of the workflow name and the reference to ensure uniqueness per workflow and per branch/PR [2][3]. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true 2. Handling Pull Requests and Forks: Using github.ref (which typically resolves to refs/pull/:number/merge for pull requests) is preferred over github.head_ref [1]. github.head_ref is not unique across different forks; if two different users submit PRs from branches with the same name (e.g., "patch-1"), a concurrency group based solely on github.head_ref could cause one PR to cancel the other [1][4]. github.ref provides a unique identifier for every PR, preventing collisions between unrelated branches [1][3]. 3. Conditional Cancellation: To avoid cancelling runs on the main branch (where you typically want all commits to complete for deployment or release purposes) while still cancelling redundant runs on PRs, you can use an expression for cancel-in-progress [2][3]: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} 4. Alternatives for Complex Scenarios: If you need more granular control or are dealing with complex fork-based PR triggers that aren't addressed by standard concurrency groups, consider using a workflow_run event to trigger a separate, dedicated "cancellation" workflow [6][7]. This approach allows you to explicitly target and cancel specific "source" workflow runs using their ID [6][7]. Avoid using github.sha or github.run_id in your concurrency group key, as these are unique to every single run and will prevent any cancellation or queuing from occurring [3].
Citations:
- 1: ci: harden GHA workflows with least-privilege permissions, concurrency, and action updates jmeridth/jmeridth.github.io#72
- 2: https://starsling.dev/best-practices/github-actions/cancel-superseded-runs
- 3: https://dev.to/ace2932/cancel-duplicate-github-actions-runs-with-one-block-and-the-one-place-you-shouldnt-2m3k
- 4: GitHub Actions: Examples for canceling in-progress jobs / runs with concurrency groups lead to confusing behavior github/docs#11243
- 5: Scope CI-integ-test concurrency groups per-branch gradle/actions#983
- 6: https://github.com/potiuk/cancel-workflow-runs/blob/master/README.md
- 7: https://github.com/marketplace/actions/cancel-workflow-runs
🌐 Web query:
GitHub Actions actions checkout fetch-depth default pull requests head ref fetch all refs default pull_request
💡 Result:
The GitHub Actions checkout action (actions/checkout) does not fetch all refs by default [1][2]. By default, it performs a shallow fetch of only the single commit (or ref) that triggered the workflow, using a fetch-depth of 1 [1][3]. Regarding your specific scenario: 1. Default Behavior: When a workflow is triggered by a pull_request event, the default behavior of actions/checkout is to check out the merge commit (a temporary ref representing the pull request merged into the base branch) [4]. It does not automatically fetch all other branches or the complete history of the head ref [1]. 2. Fetch Depth: The fetch-depth input defaults to 1 [3][5]. Setting fetch-depth to 0 is the required configuration to fetch all history for all branches and tags [1][2]. 3. Pull Request Head Ref: If you need to work with the head ref of the pull request (rather than the default merge commit), you must explicitly specify it in your workflow configuration [5]: - uses: actions/checkout@v4 with: ref: ${{ github.head_ref }} 4. Fetching Additional Refs: If you require additional refs or full history while using the default shallow fetch, you must manually perform additional git fetch operations (e.g., git fetch origin ) after the checkout step [6]. In summary, actions/checkout remains optimized for speed by default by minimizing the amount of data transferred [1][2]. If you require access to full history or other refs, you must explicitly adjust the fetch-depth or issue additional Git commands [1][6].
Citations:
- 1: https://github.com/actions/checkout
- 2: https://github.com/actions/checkout?tab=readme-ov-file
- 3: https://github.com/actions/checkout/blob/cbb722410c2e876e24abbe8de2cc27693e501dcb/action.yml
- 4: https://docs.github.com/en/actions/reference/security/securely-using-pull_request_target
- 5: https://github.com/actions/checkout/tree/v6.0.0
- 6: How do I fetch all commits only in the PR branch actions/checkout#552
Serialize catalog refresh to chore/catalog-refresh.
catalog-refresh has no concurrency and later uses git push --force origin "$BRANCH", so overlapping repository_dispatch/workflow_dispatch runs can reset the shared branch and leave an older catalog snapshot. Add workflow-level concurrency with cancel-in-progress: false, or serialize PR branch pushes with a shared lock.
🤖 Prompt for 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.
In @.github/workflows/catalog-refresh.yml around lines 24 - 26, Add
workflow-level concurrency for the catalog refresh workflow, using a shared
group such as chore/catalog-refresh and setting cancel-in-progress to false.
Anchor the change near the refresh-snapshot job and preserve the existing
force-push behavior while ensuring repository_dispatch and workflow_dispatch
runs execute serially.
| - name: Clone device catalog @ main | ||
| run: | | ||
| git clone --depth 1 --branch main \ | ||
| https://github.com/simons-plugins/indigo-device-catalog.git \ | ||
| /tmp/indigo-device-catalog |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/sh
set -eu
rg -n -i '(catalog-refresh|indigo-device-catalog|repository_dispatch|network egress)' \
docs/adr CLAUDE.md README.md || trueRepository: simons-plugins/indigo-mcp-lite
Length of output: 1024
🏁 Script executed:
#!/bin/sh
set -eu
printf 'Repository files matching ADR/workspace/network terms:\n'
git ls-files | rg -i '(^docs/adr|ADR|adr|README\.md|CLAUDE\.md|catalog-refresh|indigo-device-catalog|network|egress)' || true
printf '\nWorkflow relevant section:\n'
if [ -f .github/workflows/catalog-refresh.yml ]; then
nl -ba .github/workflows/catalog-refresh.yml | sed -n '1,120p'
fi
printf '\nDocs/ADR directories:\n'
if [ -d docs/adr ]; then
find docs/adr -maxdepth 2 -type f -print | sort
else
find docs -maxdepth 3 -type f -print 2>/dev/null | rg -i 'adr|decision|architecture' || true
fiRepository: simons-plugins/indigo-mcp-lite
Length of output: 536
Propose a workspace ADR before enabling catalog refresh
This workflow now clones indigo-device-catalog from GitHub, introducing new cross-repository network egress. The workspace has only FTS5 and an ADR template so far, and the README still describes an offline vendored snapshot, so document this change via a workspace ADR before merge.
🤖 Prompt for 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.
In @.github/workflows/catalog-refresh.yml around lines 31 - 35, Before enabling
the catalog refresh workflow, add a workspace ADR using the existing ADR
template documenting the new GitHub network dependency introduced by the “Clone
device catalog @ main” step. Reconcile the README’s offline vendored-snapshot
description with this behavior, and keep the workflow change gated until the ADR
and documentation are updated.
Source: Learnings
| for key in sorted(profiles): | ||
| lines.append(f" {key!r}: {profiles[key]!r},") |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Keep generated snapshot lines within 120 characters.
Each profile is emitted as one repr line, producing many over-limit lines in catalog_snapshot.py (for example, line 22). Format the table deterministically with wrapped literals and assert the maximum generated line length in test_render_is_deterministic.
Proposed fix
import argparse
import json
+import pprint
import subprocess
@@
- "PROFILES = {",
+ "PROFILES = (",
]
- for key in sorted(profiles):
- lines.append(f" {key!r}: {profiles[key]!r},")
- lines.append("}")
+ lines.extend(
+ f" {line}"
+ for line in pprint.pformat(
+ profiles, width=100, sort_dicts=True
+ ).splitlines()
+ )
+ lines.append(")")As per coding guidelines, **/*.{py,toml} must maintain a 120-character line limit.
🤖 Prompt for 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.
In `@scripts/generate_catalog_snapshot.py` around lines 143 - 144, Update the
snapshot rendering loop in the generator to wrap long profile literals
deterministically so every emitted line is at most 120 characters while
preserving valid output and stable ordering. Extend test_render_is_deterministic
to assert the maximum generated line length does not exceed 120 characters.
Source: Coding guidelines
…n, generator floor, meta in gap report
Apply the six review items on the catalog-enrichment wave:
- catalog.py guards the snapshot import: a corrupt/missing
catalog_snapshot.py now degrades to an empty catalog (profile_for
misses, snapshot_meta {}) with a logged error instead of killing all
72 tools at registration. Covered by a purge-and-reimport test that
registers the full registry with a broken snapshot module.
- catalog-refresh workflow pushes and opens the PR with
secrets.CATALOG_PR_TOKEN (falling back to github.token) so the
automated PR triggers the required version-check CI rather than
sitting green-by-absence; header + dispatch doc explain the secret.
- Generator gains a --min-profiles sanity floor (default 100): a
truncated/moved catalog fails loudly instead of auto-PRing a
near-empty snapshot.
- list_uncataloged_devices includes catalog_snapshot provenance
(commit/date/profile_count) in the envelope so "everything
uncataloged" is diagnosable from the response alone.
- Capability pre-check logs (debug) the swallowed device-lookup
exception; logger threads through control.register from the
registry.
- New tests: mixed per-argument white refusal (supportsWhite ok,
supportsWhiteTemperature refused, names the failing flag) and a
wire-level dispatch test proving a refusal arrives as a friendly
isError tool result naming what the device does support.
455 tests pass (5 net new).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYAGXd4bE9D9Z5kZeSHeo7
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
Indigo MCP Lite.indigoPlugin/Contents/Server Plugin/tools/control.py (1)
232-241: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winNarrow the catalog capability lookup boundary.
Catching
Exceptionhere can turn unrelated device lookup failures into a skipped capability check, letting the write continue without the catalog message it was meant to block or surface. Catch onlyKeyError/IndexError/ValueErrorfrom the device lookup (or a clearly isolated lower-level IOM class), leaving other exceptions to fail the JSON-RPC flow.🤖 Prompt for 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. In `@Indigo` MCP Lite.indigoPlugin/Contents/Server Plugin/tools/control.py around lines 232 - 241, In the catalog capability pre-check exception handler, narrow the catch around the device lookup to only KeyError, IndexError, and ValueError (or the specific isolated IOM lookup exception), while preserving the existing debug logging and skipped-check behavior for those expected failures. Allow all other exceptions to propagate through the JSON-RPC flow.Sources: Coding guidelines, Linters/SAST tools
🤖 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 @.github/workflows/catalog-refresh.yml:
- Around line 37-40: Update the actions/checkout configuration to set
persist-credentials to false, then explicitly configure only the required
authentication immediately before the git push and gh pr create steps; ensure
any checkout-provided credential is removed before running later scripts.
---
Nitpick comments:
In `@Indigo` MCP Lite.indigoPlugin/Contents/Server Plugin/tools/control.py:
- Around line 232-241: In the catalog capability pre-check exception handler,
narrow the catch around the device lookup to only KeyError, IndexError, and
ValueError (or the specific isolated IOM lookup exception), while preserving the
existing debug logging and skipped-check behavior for those expected failures.
Allow all other exceptions to propagate through the JSON-RPC flow.
🪄 Autofix (Beta)
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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d9b44417-ef29-4c11-9518-fa27e46896e1
📒 Files selected for processing (12)
.github/workflows/catalog-refresh.ymlIndigo MCP Lite.indigoPlugin/Contents/Server Plugin/catalog.pyIndigo MCP Lite.indigoPlugin/Contents/Server Plugin/tool_registry.pyIndigo MCP Lite.indigoPlugin/Contents/Server Plugin/tools/control.pyIndigo MCP Lite.indigoPlugin/Contents/Server Plugin/tools/lookup.pydocs/catalog-dispatch-workflow.ymlscripts/generate_catalog_snapshot.pytests/test_catalog_profile_for.pytests/test_catalog_snapshot_generator.pytests/test_control_capability_checks.pytests/test_lookup_uncataloged.pytests/test_mcp_dispatch_integration.py
🚧 Files skipped from review as they are similar to previous changes (8)
- docs/catalog-dispatch-workflow.yml
- tests/test_mcp_dispatch_integration.py
- Indigo MCP Lite.indigoPlugin/Contents/Server Plugin/tools/lookup.py
- tests/test_lookup_uncataloged.py
- tests/test_catalog_snapshot_generator.py
- Indigo MCP Lite.indigoPlugin/Contents/Server Plugin/catalog.py
- tests/test_control_capability_checks.py
- scripts/generate_catalog_snapshot.py
| with: | ||
| # Persist the PAT (when configured) as the push credential so | ||
| # the branch push triggers pull_request CI on the refresh PR. | ||
| token: ${{ secrets.CATALOG_PR_TOKEN || github.token }} |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
if [ -f .github/workflows/catalog-refresh.yml ]; then
nl -ba .github/workflows/catalog-refresh.yml | sed -n '1,140p'
else
echo "missing .github/workflows/catalog-refresh.yml"
fi
echo
echo "== checkout occurrences =="
rg -n "actions/checkout|persist-credentials|git push|CATALOG_PR_TOKEN|persist-credentials:false|persist-credentials:\s*false|persist-credentials:\s*true" .github/workflows catalog-refresh.yml catalog-refresh.yml 2>/dev/null || true
echo
echo "== changed files/stat =="
git diff --stat -- .github/workflows/catalog-refresh.yml 2>/dev/null || true
git diff -- .github/workflows/catalog-refresh.yml 2>/dev/null | sed -n '1,220p' || true
echo
echo "== git/config/token references =="
rg -n "\b(git (push|clone|fetch|commit|cherry-pick|rebase|apply)|CATALOG_PR_TOKEN|secrets\.CATALOG_PR_TOKEN)\b" .github/workflows catalog-refresh.yml 2>/dev/null || trueRepository: simons-plugins/indigo-mcp-lite
Length of output: 231
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
if [ -f .github/workflows/catalog-refresh.yml ]; then
awk '{printf "%4d\t%s\n", NR, $0}' .github/workflows/catalog-refresh.yml | sed -n '1,160p'
else
echo "missing .github/workflows/catalog-refresh.yml"
fi
echo
echo "== checkout and credential references =="
rg -n "actions/checkout|persist-credentials|git push|git (clone|fetch|commit|cherry-pick|rebase|apply|log|show|config)|CATALOG_PR_TOKEN|secrets\.CATALOG_PR_TOKEN" .github/workflows catalog-refresh.yml 2>/dev/null || true
echo
echo "== changed files/stat and diff =="
git diff --stat -- .github/workflows/catalog-refresh.yml 2>/dev/null || true
git diff -- .github/workflows/catalog-refresh.yml 2>/dev/null | sed -n '1,240p' || trueRepository: simons-plugins/indigo-mcp-lite
Length of output: 6396
🌐 Web query:
actions/checkout v4 persist-credentials default true README
💡 Result:
In the actions/checkout action, including version 4, the default value for the persist-credentials input is true [1][2][3]. When persist-credentials is set to true (the default), the authentication token or SSH key used by the action is configured into the local git config [1][4][5]. This allows subsequent git commands executed within the workflow job—such as git fetch or git push—to be authenticated automatically without requiring additional setup [1][4][5]. The action includes a post-job cleanup step to remove these credentials [1][6][7]. Users who wish to prevent the action from persisting these credentials can opt out by explicitly setting the input to false in their workflow configuration: with: persist-credentials: false While the default behavior remains true in version 4, it is worth noting that more recent versions (such as v6) have improved security by storing these credentials in a separate file under the runner's temporary directory rather than directly in the repository's.git/config file [4].
Citations:
- 1: https://github.com/actions/checkout/blob/v4/README.md
- 2: https://github.com/actions/checkout/blob/v4/action.yml
- 3: https://github.com/actions/checkout/blob/v4.1.1/action.yml
- 4: https://github.com/actions/checkout/tree/v6.0.2?tab=readme-ov-file
- 5: https://github.com/actions/checkout/blob/master/adrs/0153-checkout-v2.md
- 6: https://github.com/actions/checkout
- 7: https://github.com/marketplace/actions/checkout?version=v3.6.0
Don’t let checkout persist the refresh PAT.
actions/checkout@v4 persists the token by default, so CATALOG_PR_TOKEN is available to every later command in this runner. If the PAT becomes available to an untrusted script, it can be exfiltrated and then used for the final checkout checkout push. Set persist-credentials: false and clear any checkout-provided credential before the git push/gh pr create steps, rather than leaving the checkout token configured in local git config throughout the job.
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 35-40: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
🤖 Prompt for 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.
In @.github/workflows/catalog-refresh.yml around lines 37 - 40, Update the
actions/checkout configuration to set persist-credentials to false, then
explicitly configure only the required authentication immediately before the git
push and gh pr create steps; ensure any checkout-provided credential is removed
before running later scripts.
Source: Linters/SAST tools
Closes #43, closes #44. Vendored snapshot (210 profiles, catalog@3d86e05),
capabilitiesblock in get_device_by_id, friendly capability refusals on colour/white tools (refuse only on explicit false; no profile → unchanged), newlist_uncataloged_devicesgap-reporter (72 tools), repository_dispatch refresh workflow + copy-in catalog-side workflow (needs LITE_DISPATCH_TOKEN PAT — Simon). Honest finding: catalog schema carries NO role/polarity fields — garage-polarity safety needs a catalog schema addition first. 450 tests. 2026.8.1 → 2026.9.0.🤖 Generated with Claude Code
https://claude.ai/code/session_01EYAGXd4bE9D9Z5kZeSHeo7
Summary by CodeRabbit
list_uncataloged_devicesto report plugin-owned devices missing catalog entries.list_uncataloged_devices.