fix(ssh): resolve bare-string host selectors by name then tag (#485) #15
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/485-ssh-host-selector"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Fixes #485. The
@swamp/sshhostsselector treated every bare string as a CEL expression, so a plain host name or tag silently matched nothing andtag:xwas a CEL parse error:Approach
Stop treating unmarked strings as code — the root cause. New selector grammar:
all[a, b]name:<host>tag:<tag>cel:<expr><str>A bare CEL expression (no
cel:prefix) still resolves but is deprecated — it logs an agent-readable warning naming the exactcel:form and will error in a future version. Plain-identifier no-matches throw a precise, copy-pasteable error:The fix lands at the single
resolveSelectionfunnel, so it covers all seven selector-taking methods (exec/script/copy/open/check/close/forward).Changes
_lib/selectors.ts—parseSelector(prefix classification), bare name→tag resolution, pinnedlooksLikeCelheuristic, deprecation warning.selectHostskeeps itsEffectiveHost[]return._lib/operations.ts—resolveSelectionexported + form-aware no-match error._lib/schemas.ts— agent-facing.describe()on the selector (the primary machine-readable contract).ssh.ts— modelversionbump to2026.05.29.1with a matchingupgradesentry (identity — no globalArguments schema change).manifest.yaml— version + description.README.md— every selector teaching site updated.selectors_485_test.ts(regression guard, written red-first),operations_test.ts(diagnostics via a structural fake), plus prefix/bare/collision/heuristic/deprecation cases inselectors_test.ts; existing bare-CEL cases migrated tocel:.Verification
selectors_485_test.tswas committed red (6/6 failing) before the fix and is now green.ssh/):deno check/lint/fmt --checkclean, 204 tests pass,deno install --frozenclean.swampCLI viaswamp extension source add(local source in a throwaway repo):name:/tag:/cel:prefixes, and the precise no-match error;2026.05.19.1up to2026.05.29.1on first run (upgradesentry fires; globalArgs preserved;validate5/5);WRNline with a clean, copy-pasteablecel:suggestion.Notes
cel:prefix, named in the warning text.looksLikeCelheuristic is structurally safe: a valid host name (HostNamePattern) can never contain a CEL-trigger character, so names are never misread as CEL.🤖 Generated with Claude Code
Code Review
Blocking Issues
None.
Suggestions
manifest.yamlusage example uses the deprecated bare-CEL form. The embedded code block uses--input hosts='"prod" in host.tags'(nocel:prefix), which will generate a deprecation warning when users copy-paste it. Update to--input hosts='cel:"prod" in host.tags'to stay consistent with README and the new preferred form. Same file's "Highlights" bullet still describes only three forms ("all", name list, bare CEL) and doesn't mentionname:/tag:/cel:at all — minor, but the manifest description is the first thing shown byswamp extension info.No-op try/catch in
compile()(selectors.ts:127–135). The inner try-catch only re-throws, which is identical to no try-catch. The comment is accurate but the wrapper adds no value and slightly obscures intent. The actual soft-miss handling lives inevalCelSelector's catch. Could be collapsed to justreturn result === true.The fix is correct and well-scoped.
selectHostsnow resolves bare strings as name-first-then-tag with a deprecated fallback to CEL when the stringlooksLikeCel, exactly matching the commit description and README. ThenoMatchMessagewording inoperations.tsmirrors the resolution order precisely, which is important for agent-readable diagnostics. Regression suite inselectors_485_test.tscovers the exact fleet from the issue report, andselectors_test.tshas thorough coverage of all new prefix forms, collision precedence, and the legacy deprecation warning path.