Skip to content

fix(device-pairing): guard role normalization against non-string entries - #93504

Merged
vincentkoc merged 1 commit into
openclaw:mainfrom
ly-wang19:fix/device-pairing-roles-nonstring-guard
Jun 22, 2026
Merged

vincentkoc merged 1 commit into
openclaw:mainfrom
ly-wang19:fix/device-pairing-roles-nonstring-guard

Conversation

@ly-wang19

Copy link
Copy Markdown
Contributor

Summary

normalizeRoleList in src/shared/device-pairing-access.ts called .trim() on every roles[] entry and the singular role without a typeof === "string" guard. The roles/role fields are loaded from disk via a blind cast (coercePairingStateRecord, src/infra/pairing-files.ts), so a malformed/legacy pairing record with a non-string entry — e.g. roles: [123] or role: 5 — threw TypeError: role.trim is not a function, crashing resolvePendingDeviceApprovalState. openclaw devices list calls that per pending request with no surrounding try/catch (src/cli/devices-cli.runtime.ts), so the whole table render blows up on one bad record.

The scopes half of the same summary (normalizeDeviceAuthScopes) is already guarded, and the merged #90654/#92178 work added the same guard to the sibling mergeRoles/mergeScopes/formatAuditList in src/infra/device-pairing.ts — but missed this path.

This routes each item through the existing shared non-string-safe normalizer normalizeUniqueSingleOrTrimmedStringList (the exact helper mergeRoles uses), so non-string entries are dropped instead of crashing while valid roles are still trimmed, deduped, and sorted. Net −10 LOC; all three normalizeRoleList callers (pending request, approved device, token roles) are covered by the single fix.

Changes

  • src/shared/device-pairing-access.ts — route normalizeRoleList through the guarded shared helper
  • src/shared/device-pairing-access.test.ts — regression tests for non-string roles / role / token.role

Real behavior proof

Behavior addressed: normalizeRoleList crashed (TypeError: role.trim is not a function) on a non-string role from a malformed pairing record, taking down resolvePendingDeviceApprovalState / openclaw devices list; it now drops non-string entries and classifies access normally.

Real environment tested: a direct-call harness importing the real src/shared/device-pairing-access.ts, run with the repo's tsx, Node 22.22.1, no model / provider / channel credentials and no network (the function is pure). The "before" column reverts only that one source file to origin/main.

Exact steps or command run after this patch:

# dp-proof.mts (~20 lines) imports resolvePendingDeviceApprovalState from the real source
# and calls it with malformed (non-string) roles/role on the pending and paired paths.
tsx dp-proof.mts                                                                      # AFTER (this branch)
git stash push -- src/shared/device-pairing-access.ts && tsx dp-proof.mts && git stash pop   # BEFORE (origin/main)

Evidence after fix:

########## AFTER FIX (this branch) ##########
OK     pending roles:[123]         -> {"kind":"new-pairing","requested":{"roles":[],"scopes":["operator.read"]},"approved":null}
OK     pending singular role:5     -> {"kind":"new-pairing","requested":{"roles":[],"scopes":["operator.read"]},"approved":null}
OK     paired (approved) roles:[7] -> {"kind":"role-upgrade","requested":{"roles":["operator"],"scopes":["operator.read"]},"approved":{"roles":[],"scopes":["operator.read"]}}
OK     valid+malformed mixed [123,'operator',null,'  admin  '] + role:5 -> {"kind":"new-pairing","requested":{"roles":["admin","operator"],"scopes":["operator.read"]},"approved":null}

########## BEFORE FIX (origin/main) ##########
THROW  pending roles:[123]         -> TypeError: role.trim is not a function
THROW  pending singular role:5     -> TypeError: item.trim is not a function
THROW  paired (approved) roles:[7] -> TypeError: role.trim is not a function
THROW  valid+malformed mixed ...   -> TypeError: role.trim is not a function

Observed result after fix: every non-string role entry that previously threw is now dropped, valid roles are trimmed/deduped/sorted (["admin","operator"]), and the approval classification returns normally — no crash on any of the three role paths.

What was not tested: end-to-end openclaw devices list with a hand-crafted on-disk pairing file holding a non-string role. The pure-function repro exercises the exact crash; reachability is confirmed by source (the roles/role fields reach normalizeRoleList unvalidated through coercePairingStateRecord's blind cast, and the CLI's parseDevicePairingList only does Array.isArray checks, never element-type checks).

Additional checks

Four cases in device-pairing-access.test.ts cover non-string roles[], singular role, and token.role (exercising all three callers), alongside the pre-existing valid-role and revoked-token cases. Formatter, static analysis, and type checks all pass for the changed files. Mirrors the merged #90654/#92178 robustness fix; net −10 source LOC.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. size: S labels Jun 16, 2026
@ly-wang19
ly-wang19 force-pushed the fix/device-pairing-roles-nonstring-guard branch 2 times, most recently from 7ea2735 to 41fca21 Compare June 17, 2026 04:25
@ly-wang19

Copy link
Copy Markdown
Contributor Author

@clawsweeper review — CI is green and the PR body has live, model-free before/after proof for the device-pairing role-normalization crash (completes the merged #90654 / #92178 sibling hardening on a path they missed). Happy to address any findings.

@clawsweeper

clawsweeper Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper

clawsweeper Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 22, 2026, 2:44 AM ET / 06:44 UTC.

Summary
The PR routes device-pairing role normalization through the shared non-string-safe normalizer and adds regression tests for malformed pending, paired, and token role values.

PR surface: Source -10, Tests +52. Total +42 across 2 files.

Reproducibility: yes. Current main has an unguarded .trim() in normalizeRoleList, and coercePairingStateRecord only blind-casts persisted records before CLI paths call resolvePendingDeviceApprovalState.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: migration/backfill/repair: src/shared/device-pairing-access.test.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Risk before merge

  • [P1] The current head is mergeable but still has clawsweeper:human-review and a failed checks-node-core-fast gate, so maintainers should clear the pause and normal merge gates before landing.

Maintainer options:

  1. Decide the mitigation before merge
    Land the narrow shared-helper repair after maintainer clearance and a green or accepted merge-gate result, keeping approval preview normalization aligned with the existing infra pairing hardening.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P2] The branch already contains the narrow repair; the remaining action is maintainer clearance of the human-review pause and normal merge gates, not an automated code fix.

Security
Cleared: The diff reuses an existing workspace normalizer and adds focused tests; it adds no dependency, secret, permission, CI, or code-execution surface.

Review details

Best possible solution:

Land the narrow shared-helper repair after maintainer clearance and a green or accepted merge-gate result, keeping approval preview normalization aligned with the existing infra pairing hardening.

Do we have a high-confidence way to reproduce the issue?

Yes. Current main has an unguarded .trim() in normalizeRoleList, and coercePairingStateRecord only blind-casts persisted records before CLI paths call resolvePendingDeviceApprovalState.

Is this the best way to solve the issue?

Yes. Reusing normalizeUniqueSingleOrTrimmedStringList is the narrowest maintainable fix because it matches mergeRoles and normalizeDeviceAuthScopes non-string-dropping behavior without adding a new compatibility path.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 1bd85e3cc35b.

Label changes

Label justifications:

  • P2: The item is a bounded device-pairing robustness fix for a malformed persisted-record crash path.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 🚀 automerge armed: This PR is in ClawSweeper's automerge lane. Sufficient (terminal): The PR body includes before/after terminal output from a direct harness against the real pure function, which is sufficient for this non-visual helper path.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes before/after terminal output from a direct harness against the real pure function, which is sufficient for this non-visual helper path.
Evidence reviewed

PR surface:

Source -10, Tests +52. Total +42 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 5 15 -10
Tests 1 52 0 +52
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 57 15 +42

What I checked:

  • Repository policy read: Root AGENTS.md was read fully; no narrower scoped AGENTS.md owns src/shared, and the only maintainer note found was Telegram-specific and not relevant to this PR. (AGENTS.md:1, 1bd85e3cc35b)
  • Current-main crash path: Current main's normalizeRoleList trims array entries and singular role values without a runtime type guard, so non-string values can throw before the PR. (src/shared/device-pairing-access.ts:50, 1bd85e3cc35b)
  • Persisted-state ingress: Pairing JSON maps are only checked as objects and then cast to typed records, leaving malformed nested roles or role values reachable by callers. (src/infra/pairing-files.ts:18, 1bd85e3cc35b)
  • CLI caller path: openclaw devices list maps pending records through resolvePendingDeviceApprovalState, so one malformed record can break pending table rendering on current main. (src/cli/devices-cli.runtime.ts:718, 1bd85e3cc35b)
  • Shared normalizer contract: normalizeUniqueSingleOrTrimmedStringList handles single-or-array input through normalizeOptionalString, which drops non-strings instead of coercing them into roles. (packages/normalization-core/src/string-normalization.ts:93, 1bd85e3cc35b)
  • Sibling device-pairing implementation: The infra pairing mergeRoles path already uses the same shared normalizer for role-like values, so the PR aligns the shared access helper with the sibling hardened path. (src/infra/device-pairing.ts:233, 1bd85e3cc35b)

Likely related people:

  • obviyus: GitHub path history shows this user authored the original shared device-pairing approval-state helper. (role: introduced shared access surface; confidence: high; commits: 9de39accdb10; files: src/shared/device-pairing-access.ts, src/shared/device-pairing-access.test.ts)
  • vincentkoc: GitHub and local history show recent pairing-area commits and the merge of the sibling malformed paired-access hardening PR. (role: recent adjacent contributor and merger; confidence: medium; commits: 6f53f84af3e1, 107904c2c515; files: src/infra/device-pairing.ts)
  • wangmiao0668000666: The merged sibling work applied the same non-string-safe normalizer direction to paired role/scope merging. (role: sibling hardening contributor; confidence: medium; commits: d6eefa191f68; files: src/infra/device-pairing.ts, src/infra/device-pairing.test.ts, src/gateway/server/ws-connection/message-handler.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. labels Jun 18, 2026
@vincentkoc

Copy link
Copy Markdown
Member

/clownfish automerge

@vincentkoc vincentkoc added clownfish:automerge Maintainer opted this Clownfish PR into bounded ClawSweeper-reviewed automerge clawsweeper:automerge Maintainer opted this PR into bounded ClawSweeper-reviewed automerge labels Jun 21, 2026
@vincentkoc

Copy link
Copy Markdown
Member

Clownfish is on the reef for this PR. 🐠

I tagged clownfish:automerge and sent ClawSweeper over this exact head. If the sweep finds rough coral, failing checks, or needs-human, I will take another bounded repair lap and ask for a fresh review.

A maintainer can call /clownfish stop any time and I will drift this back to human review.

@clawsweeper clawsweeper Bot added status: 🚀 automerge armed This PR is in ClawSweeper's automerge lane. clawsweeper:human-review Needs maintainer review before ClawSweeper can continue and removed status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jun 21, 2026
@clawsweeper

clawsweeper Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

🦞✅
ClawSweeper is pausing this repair loop for human review.

Source: clawsweeper[bot]
Reason: - [P2] No repair job is needed; the branch already contains the narrow fix, but the current human-review pause requires maintainer action before automerge can proceed.; Cleared: The diff narrows malformed persisted role handling through an existing normalizer and adds tests; it does not add dependency, CI, secret, permission, or code-execution surface. (sha=41fca210fea2ddfc699139a710c4ec73b26455c3)

Why human review is needed:
This item has security-sensitive risk. ClawSweeper is pausing instead of making an autonomous change that could affect trust, credentials, permissions, or exposure.

What the maintainer can do as a next step:
If the maintainer accepts the current risk and wants ClawSweeper to continue merge gates, comment @clawsweeper approve. If the security-sensitive detail still needs changes, describe the safe path or push the fix, then comment @clawsweeper automerge. If the risk should not be automated, keep the PR paused for manual review or comment @clawsweeper stop.

I added clawsweeper:human-review and left the final call with a maintainer.

@xuwei-xy

Copy link
Copy Markdown

I think the fix would involve adjusting auth. Would that work for your use case?

@vincentkoc

Copy link
Copy Markdown
Member

Clownfish 🐠 reef automerge status

This repair lap finished without changing the PR. Clownfish checked the reef and found no safe patch to push this time.

Target: #93504
Executor outcome: no planned fix actions.
Worker summary: PR #93504 is the open canonical repair candidate. The hydrated artifact shows the branch is editable, narrow, cleanly mergeable, and has passing relevant checks, but ClawSweeper returned a needs-human pause at the exact head instead of an automerge-ready verdict. The PR also currently carries the blocked clawsweeper:automerge label, so ProjectClownfish must not emit an executable repair action for the PR target in this result; maintainer or router action is needed before any further automated repair loop can safely proceed.

Worker actions:

  • needs_human on #93504: blocked - The canonical PR is not safely repairable by this structured result because the hydrated target carries blocked label clawsweeper:automerge and ClawSweeper is paused for human review at the exact head.
  • needs_human on cluster:automerge-openclaw-openclaw-93504: blocked - Cluster-level executable repair depends on the blocked fix(device-pairing): guard role normalization against non-string entries #93504 automerge repair target, so the safe replacement is a blocked non-mutating needs_human action.

Clownfish left the PR as-is: no push, no rebase, no replacement PR, no merge, and no fresh ClawSweeper pass.

fish notes: model gpt-5.5, reasoning medium.

@vincentkoc
vincentkoc force-pushed the fix/device-pairing-roles-nonstring-guard branch from 41fca21 to c422956 Compare June 22, 2026 02:43
@vincentkoc

Copy link
Copy Markdown
Member

Clownfish 🐠 reef update

Thanks for the work here. Clownfish got this branch swimming again without needing a replacement PR.

Source PR: #93504
Validation: node scripts/run-vitest.mjs src/shared/device-pairing-access.test.ts --run; pnpm check:changed
This keeps attribution in the original current: commits, review context, and changelog notes all stay visible.

fish notes: model gpt-5.5, reasoning medium; reviewed against b5556373c9b6.

@vincentkoc
vincentkoc force-pushed the fix/device-pairing-roles-nonstring-guard branch from 6cbf45c to b555637 Compare June 22, 2026 02:46
@clawsweeper

clawsweeper Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

🦞✅
ClawSweeper is pausing this repair loop for human review.

Source: clawsweeper[bot]
Reason: - [P2] No repair job is needed because the branch already contains the narrow fix; the remaining action is maintainer clearance of clawsweeper:human-review before automerge can proceed.; Cleared: The diff uses an existing normalizer and tests malformed persisted role handling; it adds no dependency, CI, secret, permission, or code-execution surface. (sha=b5556373c9b6ed37f5146156f9776b4b6d81bda4)

Why human review is needed:
This item has security-sensitive risk. ClawSweeper is pausing instead of making an autonomous change that could affect trust, credentials, permissions, or exposure.

What the maintainer can do as a next step:
If the maintainer accepts the current risk and wants ClawSweeper to continue merge gates, comment @clawsweeper approve. If the security-sensitive detail still needs changes, describe the safe path or push the fix, then comment @clawsweeper automerge. If the risk should not be automated, keep the PR paused for manual review or comment @clawsweeper stop.

I added clawsweeper:human-review and left the final call with a maintainer.

@ly-wang19
ly-wang19 force-pushed the fix/device-pairing-roles-nonstring-guard branch from b555637 to 92f4041 Compare June 22, 2026 04:37
@clawsweeper

clawsweeper Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

🦞✅
ClawSweeper is pausing this repair loop for human review.

Source: clawsweeper[bot]
Reason: - [P2] The branch already contains the narrow repair; the remaining action is maintainer clearance of clawsweeper:human-review and normal merge/check gates, not an automated code fix.; Cleared: The diff uses an existing local normalizer and focused tests; it adds no dependency, CI, secret, permission, persistence migration, or code-execution surface. (sha=92f4041e2c6274e35513a0c7d30994d5317f8376)

Why human review is needed:
This item has security-sensitive risk. ClawSweeper is pausing instead of making an autonomous change that could affect trust, credentials, permissions, or exposure.

What the maintainer can do as a next step:
If the maintainer accepts the current risk and wants ClawSweeper to continue merge gates, comment @clawsweeper approve. If the security-sensitive detail still needs changes, describe the safe path or push the fix, then comment @clawsweeper automerge. If the risk should not be automated, keep the PR paused for manual review or comment @clawsweeper stop.

I added clawsweeper:human-review and left the final call with a maintainer.

@ly-wang19
ly-wang19 force-pushed the fix/device-pairing-roles-nonstring-guard branch 2 times, most recently from 8c02489 to a2a0bf0 Compare June 22, 2026 05:57
@clawsweeper

clawsweeper Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

🦞✅
ClawSweeper is pausing this repair loop for human review.

Source: clawsweeper[bot]
Reason: - [P2] The branch already contains the narrow repair; the remaining action is maintainer clearance of the human-review pause and normal merge gates, not an automated code fix.; Cleared: The diff reuses an existing workspace normalizer and adds focused tests; it adds no dependency, secret, permission, CI, or code-execution surface. (sha=a2a0bf03c30c17b10a14b0595b2ba486ba091a05)

Why human review is needed:
This item has security-sensitive risk. ClawSweeper is pausing instead of making an autonomous change that could affect trust, credentials, permissions, or exposure.

What the maintainer can do as a next step:
If the maintainer accepts the current risk and wants ClawSweeper to continue merge gates, comment @clawsweeper approve. If the security-sensitive detail still needs changes, describe the safe path or push the fix, then comment @clawsweeper automerge. If the risk should not be automated, keep the PR paused for manual review or comment @clawsweeper stop.

I added clawsweeper:human-review and left the final call with a maintainer.

normalizeRoleList in src/shared/device-pairing-access.ts called .trim() on every roles[] entry and the singular role without a typeof === "string" guard, so a malformed/legacy on-disk pairing record (roles/role loaded via blind-cast JSON in coercePairingStateRecord) threw "TypeError: role.trim is not a function" and crashed resolvePendingDeviceApprovalState -- and thus `openclaw devices list`, which calls it per pending request with no try/catch.

Route each item through the shared non-string-safe normalizer normalizeUniqueSingleOrTrimmedStringList, mirroring the openclaw#90654/openclaw#92178 fix that already guarded the sibling mergeRoles/mergeScopes (src/infra/device-pairing.ts) and the in-file scopes path (normalizeDeviceAuthScopes). Non-string entries are dropped; valid roles are still trimmed, deduped, and sorted. Net -10 LOC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ly-wang19
ly-wang19 force-pushed the fix/device-pairing-roles-nonstring-guard branch from a2a0bf0 to 091db93 Compare June 22, 2026 07:35
@vincentkoc

Copy link
Copy Markdown
Member

Clownfish 🐠 reef automerge status

This pass ended as a no-op: no narrow repair surfaced, so Clownfish left the branch untouched.

Target: #93504
Executor outcome: no planned fix actions.
Worker summary: PR #93504 is the open canonical automerge candidate, but there is no executable Clownfish repair to plan. The hydrated artifact shows the PR is narrow, editable, cleanly mergeable, has passing relevant checks, and has no hydrated review comments, while ClawSweeper's latest exact-head result still pauses for maintainer human-review clearance rather than requesting code changes. Because close and merge are blocked by the job and the remaining blocker is maintainer clearance, this result does not emit a fix artifact.

Worker actions:

  • keep_closed on #90654: skipped - Already closed historical context; no mutation allowed or needed.
  • keep_closed on #92178: skipped - Merged sibling fix; not an open automerge target.
  • needs_human on #93504: blocked - The canonical PR is not blocked on code repair. It is blocked on maintainer clearance of the ClawSweeper human-review pause before the router can continue automerge gates.

Clownfish left the PR as-is: no push, no rebase, no replacement PR, no merge, and no fresh ClawSweeper pass.

fish notes: model gpt-5.5, reasoning medium.

@vincentkoc
vincentkoc merged commit b9a7bf8 into openclaw:main Jun 22, 2026
145 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 23, 2026
…ies (openclaw#93504)

normalizeRoleList in src/shared/device-pairing-access.ts called .trim() on every roles[] entry and the singular role without a typeof === "string" guard, so a malformed/legacy on-disk pairing record (roles/role loaded via blind-cast JSON in coercePairingStateRecord) threw "TypeError: role.trim is not a function" and crashed resolvePendingDeviceApprovalState -- and thus `openclaw devices list`, which calls it per pending request with no try/catch.

Route each item through the shared non-string-safe normalizer normalizeUniqueSingleOrTrimmedStringList, mirroring the openclaw#90654/openclaw#92178 fix that already guarded the sibling mergeRoles/mergeScopes (src/infra/device-pairing.ts) and the in-file scopes path (normalizeDeviceAuthScopes). Non-string entries are dropped; valid roles are still trimmed, deduped, and sorted. Net -10 LOC.

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clawsweeper:automerge Maintainer opted this PR into bounded ClawSweeper-reviewed automerge clawsweeper:human-review Needs maintainer review before ClawSweeper can continue clownfish:automerge Maintainer opted this Clownfish PR into bounded ClawSweeper-reviewed automerge P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 🚀 automerge armed This PR is in ClawSweeper's automerge lane.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants